blob: 519558d2a50e456a3b836c89ed842b8626976c9a [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
Mike Reed139e5e02017-03-08 11:29:33 -050057///////////////////////////////////////////////////////////////////////////////////////////////////
58
reedc83a2972015-07-16 07:40:45 -070059/*
60 * Return true if the drawing this rect would hit every pixels in the canvas.
61 *
62 * Returns false if
63 * - rect does not contain the canvas' bounds
64 * - paint is not fill
65 * - paint would blur or otherwise change the coverage of the rect
66 */
67bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
68 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070069 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
70 (int)kNone_ShaderOverrideOpacity,
71 "need_matching_enums0");
72 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
73 (int)kOpaque_ShaderOverrideOpacity,
74 "need_matching_enums1");
75 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
76 (int)kNotOpaque_ShaderOverrideOpacity,
77 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070078
79 const SkISize size = this->getBaseLayerSize();
80 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
Mike Reeda1361362017-03-07 09:37:29 -050081
82 // if we're clipped at all, we can't overwrite the entire surface
83 {
84 SkBaseDevice* base = this->getDevice();
85 SkBaseDevice* top = this->getTopDevice();
86 if (base != top) {
87 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
88 }
89 if (!base->clipIsWideOpen()) {
90 return false;
91 }
reedc83a2972015-07-16 07:40:45 -070092 }
93
94 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070095 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070096 return false; // conservative
97 }
halcanaryc5769b22016-08-10 07:13:21 -070098
99 SkRect devRect;
100 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
101 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -0700102 return false;
103 }
104 }
105
106 if (paint) {
107 SkPaint::Style paintStyle = paint->getStyle();
108 if (!(paintStyle == SkPaint::kFill_Style ||
109 paintStyle == SkPaint::kStrokeAndFill_Style)) {
110 return false;
111 }
Mike Reed9dc0b9e2019-07-29 17:52:48 -0400112 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
reedc83a2972015-07-16 07:40:45 -0700113 return false; // conservative
114 }
115 }
116 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
117}
118
119///////////////////////////////////////////////////////////////////////////////////////////////////
120
reed@google.comda17f752012-08-16 18:27:05 +0000121// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122//#define SK_TRACE_SAVERESTORE
123
124#ifdef SK_TRACE_SAVERESTORE
125 static int gLayerCounter;
126 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
127 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
128
129 static int gRecCounter;
130 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
131 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
132
133 static int gCanvasCounter;
134 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
135 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
136#else
137 #define inc_layer()
138 #define dec_layer()
139 #define inc_rec()
140 #define dec_rec()
141 #define inc_canvas()
142 #define dec_canvas()
143#endif
144
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000145typedef SkTLazy<SkPaint> SkLazyPaint;
146
reedc83a2972015-07-16 07:40:45 -0700147void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000148 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700149 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
150 ? SkSurface::kDiscard_ContentChangeMode
151 : SkSurface::kRetain_ContentChangeMode);
152 }
153}
154
155void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
156 ShaderOverrideOpacity overrideOpacity) {
157 if (fSurfaceBase) {
158 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
159 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
160 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
161 // and therefore we don't care which mode we're in.
162 //
163 if (fSurfaceBase->outstandingImageSnapshot()) {
164 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
165 mode = SkSurface::kDiscard_ContentChangeMode;
166 }
167 }
168 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000169 }
170}
171
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000174/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 The clip/matrix/proc are fields that reflect the top of the save/restore
176 stack. Whenever the canvas changes, it marks a dirty flag, and then before
177 these are used (assuming we're not on a layer) we rebuild these cache
178 values: they reflect the top of the save stack, but translated and clipped
179 by the device's XY offset and bitmap-bounds.
180*/
181struct DeviceCM {
Florin Malita713b8ef2017-04-28 10:57:24 -0400182 DeviceCM* fNext;
183 sk_sp<SkBaseDevice> fDevice;
184 SkRasterClip fClip;
185 std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
186 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
Florin Malita53f77bd2017-04-28 13:48:37 -0400187 sk_sp<SkImage> fClipImage;
188 SkMatrix fClipMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
Florin Malita53f77bd2017-04-28 13:48:37 -0400190 DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
Mike Kleinb34ab042017-05-01 21:34:14 +0000191 const SkImage* clipImage, const SkMatrix* clipMatrix)
halcanary96fcdcc2015-08-27 07:41:13 -0700192 : fNext(nullptr)
Florin Malita713b8ef2017-04-28 10:57:24 -0400193 , fDevice(std::move(device))
194 , fPaint(paint ? skstd::make_unique<SkPaint>(*paint) : nullptr)
reed8c30a812016-04-20 16:36:51 -0700195 , fStashedMatrix(stashed)
Mike Kleinb34ab042017-05-01 21:34:14 +0000196 , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
Florin Malita53f77bd2017-04-28 13:48:37 -0400197 , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
Florin Malita713b8ef2017-04-28 10:57:24 -0400198 {}
reed@google.com4b226022011-01-11 18:32:13 +0000199
mtkleinfeaadee2015-04-08 11:25:48 -0700200 void reset(const SkIRect& bounds) {
201 SkASSERT(!fPaint);
202 SkASSERT(!fNext);
203 SkASSERT(fDevice);
204 fClip.setRect(bounds);
205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206};
207
Mike Reed148b7fd2018-12-18 17:38:18 -0500208namespace {
209// Encapsulate state needed to restore from saveBehind()
210struct BackImage {
211 sk_sp<SkSpecialImage> fImage;
212 SkIPoint fLoc;
213};
214}
215
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216/* This is the record we keep for each save/restore level in the stack.
217 Since a level optionally copies the matrix and/or stack, we have pointers
218 for these fields. If the value is copied for this level, the copy is
219 stored in the ...Storage field, and the pointer points to that. If the
220 value is not copied for this level, we ignore ...Storage, and just point
221 at the corresponding value in the previous level in the stack.
222*/
223class SkCanvas::MCRec {
224public:
Mike Reed148b7fd2018-12-18 17:38:18 -0500225 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 /* If there are any layers in the stack, this points to the top-most
227 one that is at or below this level in the stack (so we know what
228 bitmap/device to draw into from this level. This value is NOT
229 reference counted, since the real owner is either our fLayer field,
230 or a previous one in a lower level.)
231 */
Mike Reed148b7fd2018-12-18 17:38:18 -0500232 DeviceCM* fTopLayer;
233 std::unique_ptr<BackImage> fBackImage;
234 SkConservativeClip fRasterClip;
235 SkMatrix fMatrix;
236 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
Mike Reeda1361362017-03-07 09:37:29 -0500238 MCRec() {
halcanary96fcdcc2015-08-27 07:41:13 -0700239 fLayer = nullptr;
240 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800241 fMatrix.reset();
242 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700243
reedd9544982014-09-09 18:46:22 -0700244 // don't bother initializing fNext
245 inc_rec();
246 }
Jim Van Verth343fe492017-05-02 16:49:24 -0400247 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
halcanary96fcdcc2015-08-27 07:41:13 -0700248 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700249 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800250 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // don't bother initializing fNext
253 inc_rec();
254 }
255 ~MCRec() {
halcanary385fe4d2015-08-26 13:07:48 -0700256 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 dec_rec();
258 }
mtkleinfeaadee2015-04-08 11:25:48 -0700259
260 void reset(const SkIRect& bounds) {
261 SkASSERT(fLayer);
262 SkASSERT(fDeferredSaveCount == 0);
263
264 fMatrix.reset();
265 fRasterClip.setRect(bounds);
266 fLayer->reset(bounds);
267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268};
269
Mike Reeda1361362017-03-07 09:37:29 -0500270class SkDrawIter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271public:
Mike Reeda1361362017-03-07 09:37:29 -0500272 SkDrawIter(SkCanvas* canvas)
273 : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr)
274 {}
reed@google.com4b226022011-01-11 18:32:13 +0000275
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 bool next() {
reed@google.comf68c5e22012-02-24 16:38:58 +0000277 const DeviceCM* rec = fCurrLayer;
278 if (rec && rec->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -0400279 fDevice = rec->fDevice.get();
280 fPaint = rec->fPaint.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700282 // fCurrLayer may be nullptr now
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 return true;
284 }
285 return false;
286 }
reed@google.com4b226022011-01-11 18:32:13 +0000287
reed@google.com6f8f2922011-03-04 22:27:10 +0000288 int getX() const { return fDevice->getOrigin().x(); }
289 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000291
Mike Reed99330ba2017-02-22 11:01:08 -0500292 SkBaseDevice* fDevice;
293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 const DeviceCM* fCurrLayer;
296 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297};
298
Florin Malita713b8ef2017-04-28 10:57:24 -0400299#define FOR_EACH_TOP_DEVICE( code ) \
300 do { \
301 DeviceCM* layer = fMCRec->fTopLayer; \
302 while (layer) { \
303 SkBaseDevice* device = layer->fDevice.get(); \
304 if (device) { \
305 code; \
306 } \
307 layer = layer->fNext; \
308 } \
Mike Reed7627fa52017-02-08 10:07:53 -0500309 } while (0)
310
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311/////////////////////////////////////////////////////////////////////////////
312
reeddbc3cef2015-04-29 12:18:57 -0700313/**
314 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700315 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700316 */
reedd053ce92016-03-22 10:17:23 -0700317static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700318 SkImageFilter* imgf = paint.getImageFilter();
319 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700320 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700321 }
322
reedd053ce92016-03-22 10:17:23 -0700323 SkColorFilter* imgCFPtr;
324 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700325 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700326 }
reedd053ce92016-03-22 10:17:23 -0700327 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700328
329 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700330 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700331 // there is no existing paint colorfilter, so we can just return the imagefilter's
332 return imgCF;
333 }
334
335 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
336 // and we need to combine them into a single colorfilter.
Mike Reed19d7bd62018-02-19 14:10:57 -0500337 return imgCF->makeComposed(sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700338}
339
senorblanco87e066e2015-10-28 11:23:36 -0700340/**
341 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
342 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
343 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
344 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
345 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
346 * conservative "effective" bounds based on the settings in the paint... with one exception. This
347 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
348 * deliberately ignored.
349 */
350static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
351 const SkRect& rawBounds,
352 SkRect* storage) {
353 SkPaint tmpUnfiltered(paint);
354 tmpUnfiltered.setImageFilter(nullptr);
355 if (tmpUnfiltered.canComputeFastBounds()) {
356 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
357 } else {
358 return rawBounds;
359 }
360}
361
Mike Reed38992392019-07-30 10:48:15 -0400362class AutoLayerForImageFilter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363public:
senorblanco87e066e2015-10-28 11:23:36 -0700364 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
365 // paint. It's used to determine the size of the offscreen layer for filters.
366 // If null, the clip will be used instead.
Mike Reed38992392019-07-30 10:48:15 -0400367 AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint,
368 bool skipLayerForImageFilter = false,
369 const SkRect* rawBounds = nullptr) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000370 fCanvas = canvas;
Mike Reed38992392019-07-30 10:48:15 -0400371 fPaint = &origPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700373 fTempLayerForImageFilter = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374
Mike Reed38992392019-07-30 10:48:15 -0400375 if (auto simplifiedCF = image_to_color_filter(origPaint)) {
376 SkASSERT(!fLazyPaint.isValid());
377 SkPaint* paint = fLazyPaint.set(origPaint);
reedd053ce92016-03-22 10:17:23 -0700378 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700379 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700380 fPaint = paint;
381 }
382
383 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700384 /**
385 * We implement ImageFilters for a given draw by creating a layer, then applying the
386 * imagefilter to the pixels of that layer (its backing surface/image), and then
387 * we call restore() to xfer that layer to the main canvas.
388 *
389 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
390 * 2. Generate the src pixels:
391 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
392 * return (fPaint). We then draw the primitive (using srcover) into a cleared
393 * buffer/surface.
394 * 3. Restore the layer created in #1
395 * The imagefilter is passed the buffer/surface from the layer (now filled with the
396 * src pixels of the primitive). It returns a new "filtered" buffer, which we
397 * draw onto the previous layer using the xfermode from the original paint.
398 */
Mike Reed38992392019-07-30 10:48:15 -0400399
400 SkPaint restorePaint;
401 restorePaint.setImageFilter(fPaint->refImageFilter());
402 restorePaint.setBlendMode(fPaint->getBlendMode());
403
senorblanco87e066e2015-10-28 11:23:36 -0700404 SkRect storage;
405 if (rawBounds) {
406 // Make rawBounds include all paint outsets except for those due to image filters.
407 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
408 }
Mike Reed38992392019-07-30 10:48:15 -0400409 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
reed76033be2015-03-14 10:54:31 -0700410 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700411 fTempLayerForImageFilter = true;
reed@google.com8926b162012-03-23 15:36:36 +0000412
Mike Reed38992392019-07-30 10:48:15 -0400413 // Remove the restorePaint fields from our "working" paint
414 SkASSERT(!fLazyPaint.isValid());
415 SkPaint* paint = fLazyPaint.set(origPaint);
416 paint->setImageFilter(nullptr);
417 paint->setBlendMode(SkBlendMode::kSrcOver);
418 fPaint = paint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 }
420 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000421
Mike Reed38992392019-07-30 10:48:15 -0400422 ~AutoLayerForImageFilter() {
reed5c476fb2015-04-20 08:04:21 -0700423 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000424 fCanvas->internalRestore();
425 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000426 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000428
reed@google.com4e2b3d32011-04-07 14:18:59 +0000429 const SkPaint& paint() const {
430 SkASSERT(fPaint);
431 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434private:
Mike Reed38992392019-07-30 10:48:15 -0400435 SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000436 SkCanvas* fCanvas;
Mike Reed38992392019-07-30 10:48:15 -0400437 const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it)
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000438 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700439 bool fTempLayerForImageFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440};
441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442////////// macros to place around the internal draw calls //////////////////
443
Mike Reed38992392019-07-30 10:48:15 -0400444#define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
reed3aafe112016-08-18 12:45:34 -0700445 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400446 AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \
447 { SkDrawIter iter(this);
reed262a71b2015-12-05 13:07:27 -0800448
449
Mike Reed38992392019-07-30 10:48:15 -0400450#define DRAW_BEGIN_DRAWDEVICE(paint) \
reed@google.com97af1a62012-08-28 12:19:02 +0000451 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400452 AutoLayerForImageFilter draw(this, paint, true); \
453 { SkDrawIter iter(this);
reed@google.com8926b162012-03-23 15:36:36 +0000454
Mike Reed38992392019-07-30 10:48:15 -0400455#define DRAW_BEGIN(paint, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000456 this->predrawNotify(); \
Mike Reed38992392019-07-30 10:48:15 -0400457 AutoLayerForImageFilter draw(this, paint, false, bounds); \
458 { SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000459
Mike Reed38992392019-07-30 10:48:15 -0400460#define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \
reedc83a2972015-07-16 07:40:45 -0700461 this->predrawNotify(bounds, &paint, auxOpaque); \
Mike Reed38992392019-07-30 10:48:15 -0400462 AutoLayerForImageFilter draw(this, paint, false, bounds); \
463 { SkDrawIter iter(this);
reedc83a2972015-07-16 07:40:45 -0700464
Mike Reed38992392019-07-30 10:48:15 -0400465#define DRAW_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466
467////////////////////////////////////////////////////////////////////////////
468
msarettfbfa2582016-08-12 08:29:08 -0700469static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
470 if (bounds.isEmpty()) {
471 return SkRect::MakeEmpty();
472 }
473
474 // Expand bounds out by 1 in case we are anti-aliasing. We store the
475 // bounds as floats to enable a faster quick reject implementation.
476 SkRect dst;
477 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
478 return dst;
479}
480
mtkleinfeaadee2015-04-08 11:25:48 -0700481void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
482 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700483 fMCRec->reset(bounds);
484
485 // We're peering through a lot of structs here. Only at this scope do we
Mike Reed139e5e02017-03-08 11:29:33 -0500486 // know that the device is a SkNoPixelsDevice.
Florin Malita713b8ef2017-04-28 10:57:24 -0400487 static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
msarettfbfa2582016-08-12 08:29:08 -0700488 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700489 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700490}
491
Hal Canary363a3f82018-10-04 11:04:48 -0400492void SkCanvas::init(sk_sp<SkBaseDevice> device) {
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000493 fAllowSimplifyClip = false;
reed2ff1fce2014-12-11 07:07:37 -0800494 fSaveCount = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
496 fMCRec = (MCRec*)fMCStack.push_back();
Mike Reeda1361362017-03-07 09:37:29 -0500497 new (fMCRec) MCRec;
Stan Iliev5f1bb0a2016-12-12 17:39:55 -0500498 fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect);
msarett9637ea92016-08-18 14:03:30 -0700499 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500
reeda499f902015-05-01 09:34:31 -0700501 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
502 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
Herb Derbyefe39bc2018-05-01 17:06:20 -0400503 new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr);
reedb679ca82015-04-07 04:40:48 -0700504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
halcanary96fcdcc2015-08-27 07:41:13 -0700507 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000508
reedf92c8662014-08-18 08:02:43 -0700509 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700510 // The root device and the canvas should always have the same pixel geometry
511 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed78e27682014-11-19 08:04:34 -0800512 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700513 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
Mike Reedc42a1cd2017-02-14 14:25:14 -0500514
Mike Reedc42a1cd2017-02-14 14:25:14 -0500515 device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
reedf92c8662014-08-18 08:02:43 -0700516 }
Herb Derby4ffa0272018-06-04 15:49:15 -0400517
Herb Derby59d997a2018-06-07 12:44:09 -0400518 fScratchGlyphRunBuilder = skstd::make_unique<SkGlyphRunBuilder>();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519}
520
reed@google.comcde92112011-07-06 20:00:52 +0000521SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000522 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700523 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000525 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000526
Hal Canary363a3f82018-10-04 11:04:48 -0400527 this->init(nullptr);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000528}
529
reed96a857e2015-01-25 10:33:58 -0800530SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000531 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800532 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000533{
534 inc_canvas();
Herb Derbyefe39bc2018-05-01 17:06:20 -0400535 this->init(sk_make_sp<SkNoPixelsDevice>(
Hal Canary363a3f82018-10-04 11:04:48 -0400536 SkIRect::MakeWH(SkTMax(width, 0), SkTMax(height, 0)), fProps));
reedd9544982014-09-09 18:46:22 -0700537}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000538
Hal Canary363a3f82018-10-04 11:04:48 -0400539SkCanvas::SkCanvas(const SkIRect& bounds)
reedd9544982014-09-09 18:46:22 -0700540 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700541 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700542{
543 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700544
Mike Reed566e53c2017-03-10 10:49:45 -0500545 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
Hal Canary363a3f82018-10-04 11:04:48 -0400546 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
reedd9544982014-09-09 18:46:22 -0700547}
548
Herb Derbyefe39bc2018-05-01 17:06:20 -0400549SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000550 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700551 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000552{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700554
Hal Canary363a3f82018-10-04 11:04:48 -0400555 this->init(device);
robertphillipsfcf78292015-06-19 11:49:52 -0700556}
557
reed4a8126e2014-09-22 07:29:03 -0700558SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700559 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700560 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700561{
562 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700563
Mike Reed910ca0f2018-04-25 13:04:05 -0400564 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400565 this->init(device);
reed4a8126e2014-09-22 07:29:03 -0700566}
reed29c857d2014-09-21 10:25:07 -0700567
Mike Reed356f7c22017-01-10 11:58:39 -0500568SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
569 SkRasterHandleAllocator::Handle hndl)
reed4a8126e2014-09-22 07:29:03 -0700570 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
571 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
Mike Reed356f7c22017-01-10 11:58:39 -0500572 , fAllocator(std::move(alloc))
reed4a8126e2014-09-22 07:29:03 -0700573{
574 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700575
Mike Reed910ca0f2018-04-25 13:04:05 -0400576 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400577 this->init(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578}
579
Mike Reed356f7c22017-01-10 11:58:39 -0500580SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
581
Matt Sarett31f99ce2017-04-11 08:46:01 -0400582#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
583SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior)
584 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
585 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
586 , fAllocator(nullptr)
587{
588 inc_canvas();
589
590 SkBitmap tmp(bitmap);
591 *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr);
Mike Reedc247a4e2018-04-25 15:40:09 -0400592 sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr));
Hal Canary363a3f82018-10-04 11:04:48 -0400593 this->init(device);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400594}
595#endif
596
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597SkCanvas::~SkCanvas() {
598 // free up the contents of our deque
599 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 this->internalRestore(); // restore the last, since we're going away
602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 dec_canvas();
604}
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606///////////////////////////////////////////////////////////////////////////////
607
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000608void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700609 this->onFlush();
610}
611
612void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000613 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000614 if (device) {
615 device->flush();
616 }
617}
618
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000619SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000620 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000621 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
622}
623
senorblancoafc7cce2016-02-02 18:44:15 -0800624SkIRect SkCanvas::getTopLayerBounds() const {
625 SkBaseDevice* d = this->getTopDevice();
626 if (!d) {
627 return SkIRect::MakeEmpty();
628 }
629 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
630}
631
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000632SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000634 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 SkASSERT(rec && rec->fLayer);
Florin Malita713b8ef2017-04-28 10:57:24 -0400636 return rec->fLayer->fDevice.get();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637}
638
Florin Malita0ed3b642017-01-13 16:56:38 +0000639SkBaseDevice* SkCanvas::getTopDevice() const {
Florin Malita713b8ef2017-04-28 10:57:24 -0400640 return fMCRec->fTopLayer->fDevice.get();
reed@google.com9266fed2011-03-30 00:18:03 +0000641}
642
Mike Reed353196f2017-07-21 11:01:18 -0400643bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000644 SkBaseDevice* device = this->getDevice();
Mike Reed353196f2017-07-21 11:01:18 -0400645 return device && pm.addr() && device->readPixels(pm, x, y);
reed@google.com51df9e32010-12-23 19:29:18 +0000646}
647
Mike Reed353196f2017-07-21 11:01:18 -0400648bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
649 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
Mike Reed12e946b2017-04-17 10:53:29 -0400650}
651
652bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
653 SkPixmap pm;
654 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
655}
656
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000657bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
Mike Reed4edb5d22017-04-17 11:02:51 -0400658 SkPixmap pm;
659 if (bitmap.peekPixels(&pm)) {
reedcf01e312015-05-23 19:14:51 -0700660 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000661 }
662 return false;
663}
664
Matt Sarett03dd6d52017-01-23 12:15:09 -0500665bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000666 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000667 SkBaseDevice* device = this->getDevice();
668 if (!device) {
669 return false;
670 }
671
Matt Sarett03dd6d52017-01-23 12:15:09 -0500672 // This check gives us an early out and prevents generation ID churn on the surface.
673 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
674 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
Mike Reed92b33352019-08-24 19:39:13 -0400675 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
Matt Sarett03dd6d52017-01-23 12:15:09 -0500676 return false;
Matt Sarett26ecfe02017-01-23 15:51:01 +0000677 }
Matt Sarett26ecfe02017-01-23 15:51:01 +0000678
Matt Sarett03dd6d52017-01-23 12:15:09 -0500679 // Tell our owning surface to bump its generation ID.
680 const bool completeOverwrite =
681 srcRect.size() == SkISize::Make(device->width(), device->height());
reedc83a2972015-07-16 07:40:45 -0700682 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700683
Matt Sarett03dd6d52017-01-23 12:15:09 -0500684 // This can still fail, most notably in the case of a invalid color type or alpha type
685 // conversion. We could pull those checks into this function and avoid the unnecessary
686 // generation ID bump. But then we would be performing those checks twice, since they
687 // are also necessary at the bitmap/pixmap entry points.
Mike Reed353196f2017-07-21 11:01:18 -0400688 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000689}
reed@google.com51df9e32010-12-23 19:29:18 +0000690
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691//////////////////////////////////////////////////////////////////////////////
692
reed2ff1fce2014-12-11 07:07:37 -0800693void SkCanvas::checkForDeferredSave() {
694 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800695 this->doSave();
696 }
697}
698
reedf0090cb2014-11-26 08:55:51 -0800699int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800700#ifdef SK_DEBUG
701 int count = 0;
702 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
703 for (;;) {
704 const MCRec* rec = (const MCRec*)iter.next();
705 if (!rec) {
706 break;
707 }
708 count += 1 + rec->fDeferredSaveCount;
709 }
710 SkASSERT(count == fSaveCount);
711#endif
712 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800713}
714
715int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800716 fSaveCount += 1;
717 fMCRec->fDeferredSaveCount += 1;
718 return this->getSaveCount() - 1; // return our prev value
719}
720
721void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800722 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700723
724 SkASSERT(fMCRec->fDeferredSaveCount > 0);
725 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800726 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800727}
728
729void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800730 if (fMCRec->fDeferredSaveCount > 0) {
731 SkASSERT(fSaveCount > 1);
732 fSaveCount -= 1;
733 fMCRec->fDeferredSaveCount -= 1;
734 } else {
735 // check for underflow
736 if (fMCStack.count() > 1) {
737 this->willRestore();
738 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700739 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800740 this->internalRestore();
741 this->didRestore();
742 }
reedf0090cb2014-11-26 08:55:51 -0800743 }
744}
745
746void SkCanvas::restoreToCount(int count) {
747 // sanity check
748 if (count < 1) {
749 count = 1;
750 }
mtkleinf0f14112014-12-12 08:46:25 -0800751
reedf0090cb2014-11-26 08:55:51 -0800752 int n = this->getSaveCount() - count;
753 for (int i = 0; i < n; ++i) {
754 this->restore();
755 }
756}
757
reed2ff1fce2014-12-11 07:07:37 -0800758void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700760 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000762
Mike Reedc42a1cd2017-02-14 14:25:14 -0500763 FOR_EACH_TOP_DEVICE(device->save());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764}
765
reed4960eee2015-12-18 07:09:18 -0800766bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
Cary Clark7eddfb82018-03-13 14:41:10 -0400767 return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768}
769
reed4960eee2015-12-18 07:09:18 -0800770bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -0700771 SkIRect* intersection, const SkImageFilter* imageFilter) {
Michael Ludwigaa861a12019-07-19 10:13:47 -0400772 // clipRectBounds() is called to determine the input layer size needed for a given image filter.
773 // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the
774 // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image
775 // filter does not require a decomposed CTM matrix, the filter space and device space are the
776 // same. When it has been decomposed, we want the original image filter node to process the
777 // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no
778 // longer the original filter, but has the remainder matrix baked into it, and passing in the
779 // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds
780 // to the original filter node (barring inflation from consecutive calls to mapRect). While
781 // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always
782 // passing getDeviceClipBounds() to 'imageFilter' is correct.
783 // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will
784 // be important to more accurately calculate the clip bounds in the layer space for the original
785 // image filter (similar to how matrix image filter does it, but ideally without the inflation).
Mike Reed918e1442017-01-23 11:39:45 -0500786 SkIRect clipBounds = this->getDeviceClipBounds();
787 if (clipBounds.isEmpty()) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000788 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000789 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000790
reed96e657d2015-03-10 17:30:07 -0700791 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
792
Robert Phillips12078432018-05-17 11:17:39 -0400793 if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
794 // If the image filter DAG affects transparent black then we will need to render
795 // out to the clip bounds
796 bounds = nullptr;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000797 }
Robert Phillips12078432018-05-17 11:17:39 -0400798
799 SkIRect inputSaveLayerBounds;
bsalomon49f085d2014-09-05 13:34:00 -0700800 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 SkRect r;
reed96e657d2015-03-10 17:30:07 -0700802 ctm.mapRect(&r, *bounds);
Robert Phillips12078432018-05-17 11:17:39 -0400803 r.roundOut(&inputSaveLayerBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 } else { // no user bounds, so just use the clip
Robert Phillips12078432018-05-17 11:17:39 -0400805 inputSaveLayerBounds = clipBounds;
806 }
807
808 if (imageFilter) {
809 // expand the clip bounds by the image filter DAG to include extra content that might
810 // be required by the image filters.
811 clipBounds = imageFilter->filterBounds(clipBounds, ctm,
812 SkImageFilter::kReverse_MapDirection,
813 &inputSaveLayerBounds);
814 }
815
816 SkIRect clippedSaveLayerBounds;
817 if (bounds) {
818 // For better or for worse, user bounds currently act as a hard clip on the layer's
819 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
820 clippedSaveLayerBounds = inputSaveLayerBounds;
821 } else {
822 // If there are no user bounds, we don't want to artificially restrict the resulting
823 // layer bounds, so allow the expanded clip bounds free reign.
824 clippedSaveLayerBounds = clipBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 }
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800826
827 // early exit if the layer's bounds are clipped out
Robert Phillips12078432018-05-17 11:17:39 -0400828 if (!clippedSaveLayerBounds.intersect(clipBounds)) {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800829 if (BoundsAffectsClip(saveLayerFlags)) {
830 fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
831 fMCRec->fRasterClip.setEmpty();
832 fDeviceClipBounds.setEmpty();
833 }
834 return false;
835 }
Robert Phillips12078432018-05-17 11:17:39 -0400836 SkASSERT(!clippedSaveLayerBounds.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837
reed4960eee2015-12-18 07:09:18 -0800838 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -0700839 // Simplify the current clips since they will be applied properly during restore()
Robert Phillips12078432018-05-17 11:17:39 -0400840 fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
841 fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000842 }
843
844 if (intersection) {
Robert Phillips12078432018-05-17 11:17:39 -0400845 *intersection = clippedSaveLayerBounds;
junov@chromium.orga907ac32012-02-24 21:54:07 +0000846 }
Robert Phillips12078432018-05-17 11:17:39 -0400847
junov@chromium.orga907ac32012-02-24 21:54:07 +0000848 return true;
849}
850
reed4960eee2015-12-18 07:09:18 -0800851int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
852 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000853}
854
Cary Clarke041e312018-03-06 13:00:52 -0500855int SkCanvas::saveLayer(const SaveLayerRec& rec) {
Yuqian Lif7c723c2018-08-29 16:25:47 -0700856 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed49f8da02018-08-27 10:48:52 -0400857 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
858 // no need for the layer (or any of the draws until the matching restore()
859 this->save();
860 this->clipRect({0,0,0,0});
861 } else {
862 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
863 fSaveCount += 1;
864 this->internalSaveLayer(rec, strategy);
865 }
reed4960eee2015-12-18 07:09:18 -0800866 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -0800867}
868
Mike Reed148b7fd2018-12-18 17:38:18 -0500869int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
870 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
871 // Assuming clips never expand, if the request bounds is outside of the current clip
872 // there is no need to copy/restore the area, so just devolve back to a regular save.
873 this->save();
874 } else {
875 bool doTheWork = this->onDoSaveBehind(bounds);
876 fSaveCount += 1;
877 this->internalSave();
878 if (doTheWork) {
879 this->internalSaveBehind(bounds);
880 }
881 }
882 return this->getSaveCount() - 1;
883}
884
reeda2217ef2016-07-20 06:04:34 -0700885void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
Mike Reedc42a1cd2017-02-14 14:25:14 -0500886 SkBaseDevice* dst, const SkIPoint& dstOrigin,
Mike Reeda1361362017-03-07 09:37:29 -0500887 const SkMatrix& ctm) {
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400888 // The local bounds of the src device; all snap bounds must be intersected with this rect
889 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig08b260c2019-05-17 11:21:53 -0400890
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400891 SkPaint p;
892 if (!filter) {
893 // All devices are currently axis aligned, so they only differ by their origin. This means
894 // that we only have to copy a dst-sized block of pixels out of src and translate it to the
895 // matching position relative to dst's origin.
896 SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(),
897 dstOrigin.y() - src->getOrigin().y(),
898 dst->width(), dst->height());
899 if (!snapBounds.intersect(srcDevRect)) {
Michael Ludwig08b260c2019-05-17 11:21:53 -0400900 return;
901 }
902
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400903 auto special = src->snapSpecial(snapBounds);
904 if (special) {
905 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
906 }
907 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400908 }
909
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400910 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
911 // by the backdrop filter.
912 SkMatrix toRoot, layerMatrix;
913 SkSize scale;
914 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
915 toRoot = SkMatrix::I();
916 layerMatrix = ctm;
917 } else if (ctm.decomposeScale(&scale, &toRoot)) {
918 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
919 } else {
920 // Perspective, for now, do no scaling of the layer itself.
921 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
922 // the matrix, e.g. based on the midpoint of the near/far planes?
923 toRoot = ctm;
924 layerMatrix = SkMatrix::I();
925 }
926
927 // We have to map the dst bounds from the root space into the layer space where filtering will
928 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
929 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
930 // is a safe, conservative estimate.
931 SkMatrix fromRoot;
932 if (!toRoot.invert(&fromRoot)) {
933 return;
934 }
935
936 // This represents what the backdrop filter needs to produce in the layer space, and is sized
937 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
938 SkIRect layerTargetBounds = fromRoot.mapRect(
939 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
940 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
941 // require some extra input pixels.
942 SkIRect layerInputBounds = filter->filterBounds(
943 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
944 &layerTargetBounds);
945
946 // Map the required input into the root space, then make relative to the src device. This will
947 // be conservative contents required to fill a layerInputBounds-sized surface with the backdrop
948 // content (transformed back into the layer space using fromRoot).
949 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
950 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
951 if (!backdropBounds.intersect(srcDevRect)) {
952 return;
953 }
954
955 auto special = src->snapSpecial(backdropBounds);
956 if (!special) {
957 return;
958 }
959
960 SkColorType colorType = src->imageInfo().colorType();
961 if (colorType == kUnknown_SkColorType) {
962 colorType = kRGBA_8888_SkColorType;
963 }
964 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
965 if (!toRoot.isIdentity()) {
966 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
967 // and stored in a temporary surface, which is then used as the input to the actual filter.
968 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
969 if (!tmpSurface) {
970 return;
971 }
972 p.setFilterQuality(kHigh_SkFilterQuality);
973
974 auto tmpCanvas = tmpSurface->getCanvas();
975 tmpCanvas->clear(SK_ColorTRANSPARENT);
976 // Reading in reverse, this takes the backdrop bounds from src device space into the root
977 // space, then maps from root space into the layer space, then maps it so the input layer's
978 // top left corner is (0, 0). This transformation automatically accounts for any cropping
979 // performed on backdropBounds.
980 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
981 tmpCanvas->concat(fromRoot);
982 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
983 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
984 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
985 special = tmpSurface->makeImageSnapshot();
986 } else {
987 // Since there is no extra transform that was done, update the input bounds to reflect
988 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
989 // was equal to backdropBounds before it was made relative to the src device and cropped.
990 // When we use the original snapped image directly, just map the update backdrop bounds
991 // back into the shared layer space
992 layerInputBounds = backdropBounds;
993 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
994 }
995
996 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
997 // layer space. This has to further offset everything so that filter evaluation thinks the
998 // source image's top left corner is (0, 0).
999 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1000 // this can be simplified.
1001 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1002 SkMatrix filterCTM = layerMatrix;
1003 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1004 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1005
1006 SkIPoint offset;
1007 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001008 if (special) {
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001009 // Draw the filtered backdrop content into the dst device. Must adjust the reported offset
1010 // to include the layerInputBounds origin shift, since the returned offset is in the coord
1011 // system defined by filterCTM, and we want it to be in that of layerMatrix.
1012 offset += layerInputBounds.topLeft();
1013
1014 // Manually setting the device's CTM requires accounting for the device's origin.
1015 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
1016 // before filtering the backdrop device, and if SkAutoDeviceCTMRestore had a way to accept
1017 // a global CTM instead of a device CTM.
1018 SkMatrix dstCTM = toRoot;
1019 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
1020 SkAutoDeviceCTMRestore acr(dst, dstCTM);
1021
1022 // And because devices don't have a special-image draw function that supports arbitrary
1023 // matrices, we are abusing the asImage() functionality here...
1024 SkRect specialSrc = SkRect::Make(special->subset());
1025 auto hackImageMeh = special->asImage();
1026 dst->drawImageRect(
1027 hackImageMeh.get(), &specialSrc,
1028 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1029 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001030 }
robertphillips7354a4b2015-12-16 05:08:27 -08001031}
reed70ee31b2015-12-10 13:44:45 -08001032
Mike Reed25394292019-03-07 09:36:36 -05001033// This is shared by all backends, but contains raster-specific thoughts. Can we defer to the
1034// device to perform this?
Mike Kleine083f7c2018-02-07 12:54:27 -05001035static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001036 // Need to force L32 for now if we have an image filter.
Mike Reed25394292019-03-07 09:36:36 -05001037 // If filters ever support other colortypes, e.g. F16, we can modify this check.
Mike Klein649fb732018-02-26 15:09:16 -05001038 if (paint && paint->getImageFilter()) {
Mike Reed25394292019-03-07 09:36:36 -05001039 // TODO: can we query the imagefilter, to see if it can handle floats (so we don't always
1040 // use N32 when the layer itself was float)?
1041 return SkImageInfo::MakeN32Premul(w, h, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001042 }
Mike Klein649fb732018-02-26 15:09:16 -05001043
1044 SkColorType ct = prev.colorType();
1045 if (prev.bytesPerPixel() <= 4) {
1046 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1047 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1048 ct = kN32_SkColorType;
1049 }
1050 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001051}
1052
reed4960eee2015-12-18 07:09:18 -08001053void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001054 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001055 const SkRect* bounds = rec.fBounds;
1056 const SkPaint* paint = rec.fPaint;
1057 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1058
Mike Reed5532c2a2019-02-23 12:00:32 -05001059 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1060 // regardless of any hint-rect from the caller. skbug.com/8783
1061 if (rec.fBackdrop) {
1062 bounds = nullptr;
1063 }
1064
reed8c30a812016-04-20 16:36:51 -07001065 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001066 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001067 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001068 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001069
reed8c30a812016-04-20 16:36:51 -07001070 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001071 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1072 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1073 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001074 *
1075 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001076 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1077 * if necessary.
1078 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1079 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001080 * 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 -04001081 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001082 * of the original imagefilter, and draw that (via drawSprite)
1083 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1084 *
1085 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1086 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1087 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001088 if (imageFilter) {
1089 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001090 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1091 &modifiedCTM);
1092 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001093 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001094 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001095 modifiedRec = fMCRec;
1096 this->internalSetMatrix(modifiedCTM);
1097 SkPaint* p = lazyP.set(*paint);
1098 p->setImageFilter(std::move(modifiedFilter));
1099 imageFilter = p->getImageFilter();
1100 paint = p;
1101 }
1102 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1103 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001104 }
reed8c30a812016-04-20 16:36:51 -07001105
junov@chromium.orga907ac32012-02-24 21:54:07 +00001106 // do this before we create the layer. We don't call the public save() since
1107 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001108 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001109
junov@chromium.orga907ac32012-02-24 21:54:07 +00001110 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001111 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001112 if (modifiedRec) {
1113 // In this case there will be no layer in which to stash the matrix so we need to
1114 // revert the prior MCRec to its earlier state.
1115 modifiedRec->fMatrix = stashedMatrix;
1116 }
reed2ff1fce2014-12-11 07:07:37 -08001117 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 }
1119
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001120 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1121 // the clipRectBounds() call above?
1122 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001123 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001124 }
1125
reed8dc0ccb2015-03-20 06:32:52 -07001126 SkPixelGeometry geo = fProps.pixelGeometry();
1127 if (paint) {
reed76033be2015-03-14 10:54:31 -07001128 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001129 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001130 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001131 }
1132 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133
robertphillips5139e502016-07-19 05:10:40 -07001134 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001135 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001136 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001137 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001138 }
reedb2db8982014-11-13 12:41:02 -08001139
Mike Kleine083f7c2018-02-07 12:54:27 -05001140 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001141 if (rec.fSaveLayerFlags & kF16ColorType) {
1142 info = info.makeColorType(kRGBA_F16_SkColorType);
1143 }
reed129ed1c2016-02-22 06:42:31 -08001144
Hal Canary704cd322016-11-07 14:13:52 -05001145 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001146 {
Florin Malita4571e492019-07-16 10:25:58 -04001147 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001148 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001149 const bool trackCoverage =
1150 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001151 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001152 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001153 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001154 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1155 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001156 return;
reed61f501f2015-04-29 08:34:00 -07001157 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001158 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001159 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160
Mike Reedb43a3e02017-02-11 10:18:58 -05001161 // only have a "next" if this new layer doesn't affect the clip (rare)
1162 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 fMCRec->fLayer = layer;
1164 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001165
Mike Reedc61abee2017-02-28 17:45:27 -05001166 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001167 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001168 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001169 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001170
Mike Reedc42a1cd2017-02-14 14:25:14 -05001171 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1172
1173 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1174 if (layer->fNext) {
1175 // need to punch a hole in the previous device, so we don't draw there, given that
1176 // the new top-layer will allow drawing to happen "below" it.
1177 SkRegion hole(ir);
1178 do {
1179 layer = layer->fNext;
1180 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1181 } while (layer->fNext);
1182 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183}
1184
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001185int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001186 if (0xFF == alpha) {
1187 return this->saveLayer(bounds, nullptr);
1188 } else {
1189 SkPaint tmpPaint;
1190 tmpPaint.setAlpha(alpha);
1191 return this->saveLayer(bounds, &tmpPaint);
1192 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001193}
1194
Mike Reed148b7fd2018-12-18 17:38:18 -05001195void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
1196 SkIRect devBounds;
1197 if (localBounds) {
1198 SkRect tmp;
1199 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1200 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1201 devBounds.setEmpty();
1202 }
1203 } else {
1204 devBounds = this->getDeviceClipBounds();
1205 }
1206 if (devBounds.isEmpty()) {
1207 return;
1208 }
1209
1210 SkBaseDevice* device = this->getTopDevice();
1211 if (nullptr == device) { // Do we still need this check???
1212 return;
1213 }
1214
1215 // need the bounds relative to the device itself
1216 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
1217
Michael Ludwigac352122019-08-28 21:03:05 +00001218 // This is getting the special image from the current device, which is then drawn into (both by
1219 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1220 // own device, we need to explicitly copy the back image contents so that its original content
1221 // is available when we splat it back later during restore.
1222 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001223 if (!backImage) {
1224 return;
1225 }
1226
1227 // we really need the save, so we can wack the fMCRec
1228 this->checkForDeferredSave();
1229
1230 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1231
1232 SkPaint paint;
1233 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001234 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001235}
1236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237void SkCanvas::internalRestore() {
1238 SkASSERT(fMCStack.count() != 0);
1239
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001240 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 DeviceCM* layer = fMCRec->fLayer; // may be null
1242 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001243 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244
Mike Reed148b7fd2018-12-18 17:38:18 -05001245 // move this out before we do the actual restore
1246 auto backImage = std::move(fMCRec->fBackImage);
1247
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 // now do the normal restore()
1249 fMCRec->~MCRec(); // balanced in save()
1250 fMCStack.pop_back();
1251 fMCRec = (MCRec*)fMCStack.back();
1252
Mike Reedc42a1cd2017-02-14 14:25:14 -05001253 if (fMCRec) {
1254 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1255 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001256
Mike Reed148b7fd2018-12-18 17:38:18 -05001257 if (backImage) {
1258 SkPaint paint;
1259 paint.setBlendMode(SkBlendMode::kDstOver);
1260 const int x = backImage->fLoc.x();
1261 const int y = backImage->fLoc.y();
1262 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1263 nullptr, SkMatrix::I());
1264 }
1265
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1267 since if we're being recorded, we don't want to record this (the
1268 recorder will have already recorded the restore).
1269 */
bsalomon49f085d2014-09-05 13:34:00 -07001270 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001271 if (fMCRec) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001272 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001273 layer->fDevice->setImmutable();
Florin Malita713b8ef2017-04-28 10:57:24 -04001274 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001275 layer->fPaint.get(),
1276 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001277 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001278 this->internalSetMatrix(layer->fStashedMatrix);
reed@google.com8926b162012-03-23 15:36:36 +00001279 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001280 delete layer;
reedb679ca82015-04-07 04:40:48 -07001281 } else {
1282 // we're at the root
reeda499f902015-05-01 09:34:31 -07001283 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001284 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001285 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001287 }
msarettfbfa2582016-08-12 08:29:08 -07001288
1289 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001290 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001291 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293}
1294
reede8f30622016-03-23 18:59:25 -07001295sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001296 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001297 props = &fProps;
1298 }
1299 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001300}
1301
reede8f30622016-03-23 18:59:25 -07001302sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001303 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001304 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001305}
1306
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001307SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001308 return this->onImageInfo();
1309}
1310
1311SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001312 SkBaseDevice* dev = this->getDevice();
1313 if (dev) {
1314 return dev->imageInfo();
1315 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001316 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001317 }
1318}
1319
brianosman898235c2016-04-06 07:38:23 -07001320bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001321 return this->onGetProps(props);
1322}
1323
1324bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001325 SkBaseDevice* dev = this->getDevice();
1326 if (dev) {
1327 if (props) {
1328 *props = fProps;
1329 }
1330 return true;
1331 } else {
1332 return false;
1333 }
1334}
1335
reed6ceeebd2016-03-09 14:26:26 -08001336bool SkCanvas::peekPixels(SkPixmap* pmap) {
1337 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001338}
1339
reed884e97c2015-05-26 11:31:54 -07001340bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001341 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001342 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343}
1344
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001345void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001346 SkPixmap pmap;
1347 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001348 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001349 }
1350 if (info) {
1351 *info = pmap.info();
1352 }
1353 if (rowBytes) {
1354 *rowBytes = pmap.rowBytes();
1355 }
1356 if (origin) {
Florin Malita0ed3b642017-01-13 16:56:38 +00001357 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001358 }
reed884e97c2015-05-26 11:31:54 -07001359 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001360}
1361
reed884e97c2015-05-26 11:31:54 -07001362bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001363 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001364 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001365}
1366
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368
Mike Reed8bcd1282019-03-13 16:51:54 -04001369// In our current design/features, we should never have a layer (src) in a different colorspace
1370// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1371// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1372// colorspace.
1373static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1374 SkASSERT(src == dst);
1375}
1376
Florin Malita53f77bd2017-04-28 13:48:37 -04001377void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
1378 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001380 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 paint = &tmp;
1382 }
reed@google.com4b226022011-01-11 18:32:13 +00001383
Mike Reed38992392019-07-30 10:48:15 -04001384 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001385
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001387 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001388 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1389 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001390 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001391 SkImageFilter* filter = paint->getImageFilter();
1392 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001393 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001394 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1395 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001396 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1397 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001398 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1399 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001400 }
reed@google.com76dd2772012-01-05 21:15:07 +00001401 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001402 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 }
reeda2217ef2016-07-20 06:04:34 -07001405
Mike Reed38992392019-07-30 10:48:15 -04001406 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407}
1408
reed32704672015-12-16 08:27:10 -08001409/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001410
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001411void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001412 if (dx || dy) {
1413 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001414 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001415
reedfe69b502016-09-12 06:31:48 -07001416 // Translate shouldn't affect the is-scale-translateness of the matrix.
1417 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001418
Mike Reedc42a1cd2017-02-14 14:25:14 -05001419 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001420
reedfe69b502016-09-12 06:31:48 -07001421 this->didTranslate(dx,dy);
1422 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423}
1424
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001425void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001426 SkMatrix m;
1427 m.setScale(sx, sy);
1428 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429}
1430
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001431void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001432 SkMatrix m;
1433 m.setRotate(degrees);
1434 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435}
1436
bungeman7438bfc2016-07-12 15:01:19 -07001437void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1438 SkMatrix m;
1439 m.setRotate(degrees, px, py);
1440 this->concat(m);
1441}
1442
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001443void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001444 SkMatrix m;
1445 m.setSkew(sx, sy);
1446 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001447}
1448
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001449void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001450 if (matrix.isIdentity()) {
1451 return;
1452 }
1453
reed2ff1fce2014-12-11 07:07:37 -08001454 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001455 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001456 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001457
Mike Reed7627fa52017-02-08 10:07:53 -05001458 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001459
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001460 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001461}
1462
reed8c30a812016-04-20 16:36:51 -07001463void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001464 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001465 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001466
Mike Reedc42a1cd2017-02-14 14:25:14 -05001467 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001468}
1469
1470void SkCanvas::setMatrix(const SkMatrix& matrix) {
1471 this->checkForDeferredSave();
1472 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001473 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001477 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
1480//////////////////////////////////////////////////////////////////////////////
1481
Mike Reedc1f77742016-12-09 09:00:50 -05001482void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001483 if (!rect.isFinite()) {
1484 return;
1485 }
reed2ff1fce2014-12-11 07:07:37 -08001486 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1488 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001489}
1490
Mike Reedc1f77742016-12-09 09:00:50 -05001491void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001492 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001493
Mike Reed7627fa52017-02-08 10:07:53 -05001494 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001495
reedc64eff52015-11-21 12:39:45 -08001496 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001497 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1498 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001499 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500}
1501
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001502void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1503 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001504 if (fClipRestrictionRect.isEmpty()) {
1505 // we notify the device, but we *dont* resolve deferred saves (since we're just
1506 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001507 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001508 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001509 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001510 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001511 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001512 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001513 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1514 }
1515}
1516
Mike Reedc1f77742016-12-09 09:00:50 -05001517void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001518 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001519 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001520 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1522 } else {
1523 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001524 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001526
Mike Reedc1f77742016-12-09 09:00:50 -05001527void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001528 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001529
Brian Salomona3b45d42016-10-03 11:36:16 -04001530 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001531
Mike Reed7627fa52017-02-08 10:07:53 -05001532 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001533
Mike Reed20800c82017-11-15 16:09:04 -05001534 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1535 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001536 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001537}
1538
Mike Reedc1f77742016-12-09 09:00:50 -05001539void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001540 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001541 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001542
1543 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1544 SkRect r;
1545 if (path.isRect(&r)) {
1546 this->onClipRect(r, op, edgeStyle);
1547 return;
1548 }
1549 SkRRect rrect;
1550 if (path.isOval(&r)) {
1551 rrect.setOval(r);
1552 this->onClipRRect(rrect, op, edgeStyle);
1553 return;
1554 }
1555 if (path.isRRect(&rrect)) {
1556 this->onClipRRect(rrect, op, edgeStyle);
1557 return;
1558 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 }
robertphillips39f05382015-11-24 09:30:12 -08001560
1561 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562}
1563
Mike Reedc1f77742016-12-09 09:00:50 -05001564void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001565 AutoValidateClip avc(this);
1566
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001568
Mike Reed7627fa52017-02-08 10:07:53 -05001569 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570
Brian Salomona3b45d42016-10-03 11:36:16 -04001571 const SkPath* rasterClipPath = &path;
1572 const SkMatrix* matrix = &fMCRec->fMatrix;
Mike Reed20800c82017-11-15 16:09:04 -05001573 fMCRec->fRasterClip.opPath(*rasterClipPath, *matrix, this->getTopLayerBounds(),
1574 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001575 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
Mike Reedc1f77742016-12-09 09:00:50 -05001578void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001579 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581}
1582
Mike Reedc1f77742016-12-09 09:00:50 -05001583void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001584 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001585
reed@google.com5c3d1472011-02-22 19:12:23 +00001586 AutoValidateClip avc(this);
1587
Mike Reed20800c82017-11-15 16:09:04 -05001588 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001589 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590}
1591
reed@google.com819c9212011-02-23 18:56:55 +00001592#ifdef SK_DEBUG
1593void SkCanvas::validateClip() const {
1594 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001595 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001596 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001597 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001598 return;
1599 }
reed@google.com819c9212011-02-23 18:56:55 +00001600}
1601#endif
1602
Mike Reeda1361362017-03-07 09:37:29 -05001603bool SkCanvas::androidFramework_isClipAA() const {
1604 bool containsAA = false;
1605
1606 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1607
1608 return containsAA;
1609}
1610
1611class RgnAccumulator {
1612 SkRegion* fRgn;
1613public:
1614 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1615 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1616 SkIPoint origin = device->getOrigin();
1617 if (origin.x() | origin.y()) {
1618 rgn->translate(origin.x(), origin.y());
1619 }
1620 fRgn->op(*rgn, SkRegion::kUnion_Op);
1621 }
1622};
1623
1624void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1625 RgnAccumulator accum(rgn);
1626 SkRegion tmp;
1627
1628 rgn->setEmpty();
1629 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001630}
1631
reed@google.com5c3d1472011-02-22 19:12:23 +00001632///////////////////////////////////////////////////////////////////////////////
1633
reed@google.com754de5f2014-02-24 19:38:20 +00001634bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001635 return fMCRec->fRasterClip.isEmpty();
1636
1637 // TODO: should we only use the conservative answer in a recording canvas?
1638#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001639 SkBaseDevice* dev = this->getTopDevice();
1640 // if no device we return true
1641 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001642#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001643}
1644
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001645bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001646 SkBaseDevice* dev = this->getTopDevice();
1647 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001648 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001649}
1650
msarettfbfa2582016-08-12 08:29:08 -07001651static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1652#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1653 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1654 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1655 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1656 return 0xF != _mm_movemask_ps(mask);
1657#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1658 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1659 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1660 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1661 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1662#else
1663 SkRect devRectAsRect;
1664 SkRect devClipAsRect;
1665 devRect.store(&devRectAsRect.fLeft);
1666 devClip.store(&devClipAsRect.fLeft);
1667 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1668#endif
1669}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001670
msarettfbfa2582016-08-12 08:29:08 -07001671// It's important for this function to not be inlined. Otherwise the compiler will share code
1672// between the fast path and the slow path, resulting in two slow paths.
1673static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1674 const SkMatrix& matrix) {
1675 SkRect deviceRect;
1676 matrix.mapRect(&deviceRect, src);
1677 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1678}
1679
1680bool SkCanvas::quickReject(const SkRect& src) const {
1681#ifdef SK_DEBUG
1682 // Verify that fDeviceClipBounds are set properly.
1683 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001684 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001685 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001686 } else {
msarettfbfa2582016-08-12 08:29:08 -07001687 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 }
msarettfbfa2582016-08-12 08:29:08 -07001689
msarett9637ea92016-08-18 14:03:30 -07001690 // Verify that fIsScaleTranslate is set properly.
1691 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001692#endif
1693
msarett9637ea92016-08-18 14:03:30 -07001694 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001695 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1696 }
1697
1698 // We inline the implementation of mapScaleTranslate() for the fast path.
1699 float sx = fMCRec->fMatrix.getScaleX();
1700 float sy = fMCRec->fMatrix.getScaleY();
1701 float tx = fMCRec->fMatrix.getTranslateX();
1702 float ty = fMCRec->fMatrix.getTranslateY();
1703 Sk4f scale(sx, sy, sx, sy);
1704 Sk4f trans(tx, ty, tx, ty);
1705
1706 // Apply matrix.
1707 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1708
1709 // Make sure left < right, top < bottom.
1710 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1711 Sk4f min = Sk4f::Min(ltrb, rblt);
1712 Sk4f max = Sk4f::Max(ltrb, rblt);
1713 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1714 // ARM this sequence generates the fastest (a single instruction).
1715 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1716
1717 // Check if the device rect is NaN or outside the clip.
1718 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com3b3e8952012-08-16 20:53:31 +00001721bool SkCanvas::quickReject(const SkPath& path) const {
1722 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723}
1724
Mike Klein83c8dd92017-11-28 17:08:45 -05001725SkRect SkCanvas::getLocalClipBounds() const {
1726 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001727 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001728 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729 }
1730
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001731 SkMatrix inverse;
1732 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001733 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001734 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001735 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736
Mike Reed42e8c532017-01-23 14:09:13 -05001737 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001738 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001739 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001740
Mike Reedb57b9312018-04-23 12:12:54 -04001741 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001742 inverse.mapRect(&bounds, r);
1743 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
Mike Klein83c8dd92017-11-28 17:08:45 -05001746SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001747 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748}
1749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001751 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752}
1753
Brian Osman11052242016-10-27 14:47:55 -04001754GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001755 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001756 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001757}
1758
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001759GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001760 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001761 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001762}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001763
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001764void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1765 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001766 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001767 if (outer.isEmpty()) {
1768 return;
1769 }
1770 if (inner.isEmpty()) {
1771 this->drawRRect(outer, paint);
1772 return;
1773 }
1774
1775 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001776 // be able to return ...
1777 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001778 //
1779 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001780 if (!outer.getBounds().contains(inner.getBounds())) {
1781 return;
1782 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001783
1784 this->onDrawDRRect(outer, inner, paint);
1785}
1786
reed41af9662015-01-05 07:49:08 -08001787void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001788 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001789 this->onDrawPaint(paint);
1790}
1791
1792void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001793 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001794 // To avoid redundant logic in our culling code and various backends, we always sort rects
1795 // before passing them along.
1796 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001797}
1798
Mike Reedd5674082019-04-19 15:00:47 -04001799void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1800 TRACE_EVENT0("skia", TRACE_FUNC);
1801 this->onDrawBehind(paint);
1802}
1803
msarettdca352e2016-08-26 06:37:45 -07001804void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001805 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001806 if (region.isEmpty()) {
1807 return;
1808 }
1809
1810 if (region.isRect()) {
1811 return this->drawIRect(region.getBounds(), paint);
1812 }
1813
1814 this->onDrawRegion(region, paint);
1815}
1816
reed41af9662015-01-05 07:49:08 -08001817void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001818 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001819 // To avoid redundant logic in our culling code and various backends, we always sort rects
1820 // before passing them along.
1821 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001822}
1823
1824void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001825 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001826 this->onDrawRRect(rrect, paint);
1827}
1828
1829void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001830 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001831 this->onDrawPoints(mode, count, pts, paint);
1832}
1833
Mike Reede88a1cb2017-03-17 09:50:46 -04001834void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1835 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001836 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001837 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001838 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1839 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001840 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001841}
1842
1843void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001844 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001845 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001846 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1847}
1848
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001849void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1850 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001851 TRACE_EVENT0("skia", TRACE_FUNC);
1852 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001853 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001854 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1855}
1856
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001857void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1858 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001859 TRACE_EVENT0("skia", TRACE_FUNC);
1860 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001861 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001862 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001863}
1864
1865void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001866 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001867 this->onDrawPath(path, paint);
1868}
1869
reeda85d4d02015-05-06 12:56:48 -07001870void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001871 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001872 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001873 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001874}
1875
Mike Reedc4e31092018-01-30 11:15:27 -05001876// Returns true if the rect can be "filled" : non-empty and finite
1877static bool fillable(const SkRect& r) {
1878 SkScalar w = r.width();
1879 SkScalar h = r.height();
1880 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1881}
1882
reede47829b2015-08-06 10:02:53 -07001883void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1884 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001885 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001886 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001887 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001888 return;
1889 }
1890 this->onDrawImageRect(image, &src, dst, paint, constraint);
1891}
reed41af9662015-01-05 07:49:08 -08001892
reed84984ef2015-07-17 07:09:43 -07001893void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1894 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001895 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001896 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001897}
1898
Brian Salomonf08002c2018-10-26 16:15:46 -04001899void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001900 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001901 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001902 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001903}
reede47829b2015-08-06 10:02:53 -07001904
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001905namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001906class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001907public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001908 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1909 if (!origPaint) {
1910 return;
1911 }
1912 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1913 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1914 }
1915 if (origPaint->getMaskFilter()) {
1916 fPaint.writable()->setMaskFilter(nullptr);
1917 }
1918 if (origPaint->isAntiAlias()) {
1919 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001920 }
1921 }
1922
1923 const SkPaint* get() const {
1924 return fPaint;
1925 }
1926
1927private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001928 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001929};
1930} // namespace
1931
reed4c21dc52015-06-25 12:32:03 -07001932void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1933 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001934 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001935 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001936 if (dst.isEmpty()) {
1937 return;
1938 }
msarett552bca92016-08-03 06:53:26 -07001939 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001940 LatticePaint latticePaint(paint);
1941 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001942 } else {
reede47829b2015-08-06 10:02:53 -07001943 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001944 }
reed4c21dc52015-06-25 12:32:03 -07001945}
1946
msarett16882062016-08-16 09:31:08 -07001947void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1948 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001949 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001950 RETURN_ON_NULL(image);
1951 if (dst.isEmpty()) {
1952 return;
1953 }
msarett71df2d72016-09-30 12:41:42 -07001954
1955 SkIRect bounds;
1956 Lattice latticePlusBounds = lattice;
1957 if (!latticePlusBounds.fBounds) {
1958 bounds = SkIRect::MakeWH(image->width(), image->height());
1959 latticePlusBounds.fBounds = &bounds;
1960 }
1961
1962 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001963 LatticePaint latticePaint(paint);
1964 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07001965 } else {
1966 this->drawImageRect(image, dst, paint);
1967 }
1968}
1969
reed41af9662015-01-05 07:49:08 -08001970void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001971 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07001972 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001973 return;
1974 }
reed41af9662015-01-05 07:49:08 -08001975 this->onDrawBitmap(bitmap, dx, dy, paint);
1976}
1977
reede47829b2015-08-06 10:02:53 -07001978void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001979 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001980 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07001981 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001982 return;
1983 }
reede47829b2015-08-06 10:02:53 -07001984 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001985}
1986
reed84984ef2015-07-17 07:09:43 -07001987void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1988 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001989 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001990}
1991
reede47829b2015-08-06 10:02:53 -07001992void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1993 SrcRectConstraint constraint) {
1994 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1995 constraint);
1996}
reede47829b2015-08-06 10:02:53 -07001997
reed41af9662015-01-05 07:49:08 -08001998void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1999 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002000 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002001 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002002 return;
2003 }
msarett552bca92016-08-03 06:53:26 -07002004 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002005 LatticePaint latticePaint(paint);
2006 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002007 } else {
reeda5517e22015-07-14 10:54:12 -07002008 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002009 }
reed41af9662015-01-05 07:49:08 -08002010}
2011
msarettc573a402016-08-02 08:05:56 -07002012void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2013 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002014 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002015 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002016 return;
2017 }
msarett71df2d72016-09-30 12:41:42 -07002018
2019 SkIRect bounds;
2020 Lattice latticePlusBounds = lattice;
2021 if (!latticePlusBounds.fBounds) {
2022 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2023 latticePlusBounds.fBounds = &bounds;
2024 }
2025
2026 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002027 LatticePaint latticePaint(paint);
2028 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002029 } else {
msarett16882062016-08-16 09:31:08 -07002030 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002031 }
msarettc573a402016-08-02 08:05:56 -07002032}
2033
reed71c3c762015-06-24 10:29:17 -07002034void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002035 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002036 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002037 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002038 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002039 if (count <= 0) {
2040 return;
2041 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002042 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002043 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002044 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002045}
2046
reedf70b5312016-03-04 16:36:20 -08002047void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002048 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002049 if (key) {
2050 this->onDrawAnnotation(rect, key, value);
2051 }
2052}
2053
reede47829b2015-08-06 10:02:53 -07002054void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2055 const SkPaint* paint, SrcRectConstraint constraint) {
2056 if (src) {
2057 this->drawImageRect(image, *src, dst, paint, constraint);
2058 } else {
2059 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2060 dst, paint, constraint);
2061 }
2062}
2063void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2064 const SkPaint* paint, SrcRectConstraint constraint) {
2065 if (src) {
2066 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2067 } else {
2068 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2069 dst, paint, constraint);
2070 }
2071}
2072
Mike Reed4204da22017-05-17 08:53:36 -04002073void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002074 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002075 this->onDrawShadowRec(path, rec);
2076}
2077
2078void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2079 SkPaint paint;
2080 const SkRect& pathBounds = path.getBounds();
2081
Mike Reed38992392019-07-30 10:48:15 -04002082 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002083 while (iter.next()) {
2084 iter.fDevice->drawShadow(path, rec);
2085 }
Mike Reed38992392019-07-30 10:48:15 -04002086 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002087}
2088
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002089void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002090 QuadAAFlags aaFlags, const SkColor4f& color,
2091 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002092 TRACE_EVENT0("skia", TRACE_FUNC);
2093 // Make sure the rect is sorted before passing it along
2094 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2095}
2096
2097void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2098 const SkPoint dstClips[],
2099 const SkMatrix preViewMatrices[],
2100 const SkPaint* paint,
2101 SrcRectConstraint constraint) {
2102 TRACE_EVENT0("skia", TRACE_FUNC);
2103 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2104}
2105
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106//////////////////////////////////////////////////////////////////////////////
2107// These are the virtual drawing methods
2108//////////////////////////////////////////////////////////////////////////////
2109
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002110void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002111 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002112 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2113 }
2114}
2115
reed41af9662015-01-05 07:49:08 -08002116void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002117 this->internalDrawPaint(paint);
2118}
2119
2120void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002121 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122
2123 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002124 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
2126
Mike Reed38992392019-07-30 10:48:15 -04002127 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
reed41af9662015-01-05 07:49:08 -08002130void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2131 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 if ((long)count <= 0) {
2133 return;
2134 }
2135
Mike Reed822128b2017-02-28 16:41:03 -05002136 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002137 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002138 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002139 // special-case 2 points (common for drawing a single line)
2140 if (2 == count) {
2141 r.set(pts[0], pts[1]);
2142 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002143 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002144 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002145 if (!r.isFinite()) {
2146 return;
2147 }
Mike Reed822128b2017-02-28 16:41:03 -05002148 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002149 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2150 return;
2151 }
2152 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002153 }
reed@google.coma584aed2012-05-16 14:06:02 +00002154
halcanary96fcdcc2015-08-27 07:41:13 -07002155 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156
Mike Reed38992392019-07-30 10:48:15 -04002157 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002158
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002160 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 }
reed@google.com4b226022011-01-11 18:32:13 +00002162
Mike Reed38992392019-07-30 10:48:15 -04002163 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164}
2165
reed4a167172016-08-18 17:15:25 -07002166static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002167 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002168}
2169
reed41af9662015-01-05 07:49:08 -08002170void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002171 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002173 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002174 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002175 return;
2176 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 }
reed@google.com4b226022011-01-11 18:32:13 +00002178
reed4a167172016-08-18 17:15:25 -07002179 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002180 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181
reed4a167172016-08-18 17:15:25 -07002182 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002183 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002184 }
2185
Mike Reed38992392019-07-30 10:48:15 -04002186 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002187 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002188 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002189 SkDrawIter iter(this);
2190 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002191 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002192 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194}
2195
msarett44df6512016-08-25 13:54:30 -07002196void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002197 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002198 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002199 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002200 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2201 return;
2202 }
msarett44df6512016-08-25 13:54:30 -07002203 }
2204
Mike Reed38992392019-07-30 10:48:15 -04002205 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002206
2207 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002208 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002209 }
2210
Mike Reed38992392019-07-30 10:48:15 -04002211 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002212}
2213
Mike Reedd5674082019-04-19 15:00:47 -04002214void SkCanvas::onDrawBehind(const SkPaint& paint) {
2215 SkIRect bounds;
2216 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2217 for (;;) {
2218 const MCRec* rec = (const MCRec*)iter.prev();
2219 if (!rec) {
2220 return; // no backimages, so nothing to draw
2221 }
2222 if (rec->fBackImage) {
2223 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2224 rec->fBackImage->fImage->width(),
2225 rec->fBackImage->fImage->height());
2226 break;
2227 }
2228 }
2229
Mike Reed38992392019-07-30 10:48:15 -04002230 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002231
2232 while (iter.next()) {
2233 SkBaseDevice* dev = iter.fDevice;
2234
Mike Reedd5674082019-04-19 15:00:47 -04002235 dev->save();
2236 // We use clipRegion because it is already defined to operate in dev-space
2237 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2238 // but we don't want that, so we undo that before calling in.
2239 SkRegion rgn(bounds.makeOffset(dev->fOrigin.fX, dev->fOrigin.fY));
2240 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002241 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002242 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002243 }
2244
Mike Reed38992392019-07-30 10:48:15 -04002245 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002246}
2247
reed41af9662015-01-05 07:49:08 -08002248void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002249 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002250 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002251 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002252 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002253 return;
2254 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002255 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002256
Mike Reed38992392019-07-30 10:48:15 -04002257 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002258
2259 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002260 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002261 }
2262
Mike Reed38992392019-07-30 10:48:15 -04002263 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002264}
2265
bsalomonac3aa242016-08-19 11:25:19 -07002266void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2267 SkScalar sweepAngle, bool useCenter,
2268 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002269 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002270 if (paint.canComputeFastBounds()) {
2271 SkRect storage;
2272 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002273 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002274 return;
2275 }
bsalomonac3aa242016-08-19 11:25:19 -07002276 }
2277
Mike Reed38992392019-07-30 10:48:15 -04002278 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002279
2280 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002281 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002282 }
2283
Mike Reed38992392019-07-30 10:48:15 -04002284 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002285}
2286
reed41af9662015-01-05 07:49:08 -08002287void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002288 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002289 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002290 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2291 return;
2292 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002293 }
2294
2295 if (rrect.isRect()) {
2296 // call the non-virtual version
2297 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002298 return;
2299 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002300 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002301 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2302 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002303 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002304
Mike Reed38992392019-07-30 10:48:15 -04002305 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002306
2307 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002308 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002309 }
2310
Mike Reed38992392019-07-30 10:48:15 -04002311 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002312}
2313
Mike Reed822128b2017-02-28 16:41:03 -05002314void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002315 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002316 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002317 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2318 return;
2319 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002320 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002321
Mike Reed38992392019-07-30 10:48:15 -04002322 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002323
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002324 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002325 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002326 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002327
Mike Reed38992392019-07-30 10:48:15 -04002328 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002329}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002330
reed41af9662015-01-05 07:49:08 -08002331void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002332 if (!path.isFinite()) {
2333 return;
2334 }
2335
Mike Reed822128b2017-02-28 16:41:03 -05002336 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002337 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002338 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002339 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2340 return;
2341 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002343
Mike Reed822128b2017-02-28 16:41:03 -05002344 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002345 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002346 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002347 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002348 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002350
Mike Reed38992392019-07-30 10:48:15 -04002351 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352
2353 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002354 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 }
2356
Mike Reed38992392019-07-30 10:48:15 -04002357 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358}
2359
reed262a71b2015-12-05 13:07:27 -08002360bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002361 if (!paint.getImageFilter()) {
2362 return false;
2363 }
2364
2365 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002366 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002367 return false;
2368 }
2369
2370 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2371 // Once we can filter and the filter will return a result larger than itself, we should be
2372 // able to remove this constraint.
2373 // skbug.com/4526
2374 //
2375 SkPoint pt;
2376 ctm.mapXY(x, y, &pt);
2377 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2378 return ir.contains(fMCRec->fRasterClip.getBounds());
2379}
2380
Mike Reedf441cfc2018-04-11 14:50:16 -04002381// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2382// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2383// null.
2384static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2385 if (paintParam) {
2386 *real = *paintParam;
2387 real->setStyle(SkPaint::kFill_Style);
2388 real->setPathEffect(nullptr);
2389 paintParam = real;
2390 }
2391 return paintParam;
2392}
2393
reeda85d4d02015-05-06 12:56:48 -07002394void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002395 SkPaint realPaint;
2396 paint = init_image_paint(&realPaint, paint);
2397
reeda85d4d02015-05-06 12:56:48 -07002398 SkRect bounds = SkRect::MakeXYWH(x, y,
2399 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002400 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002401 SkRect tmp = bounds;
2402 if (paint) {
2403 paint->computeFastBounds(tmp, &tmp);
2404 }
2405 if (this->quickReject(tmp)) {
2406 return;
2407 }
reeda85d4d02015-05-06 12:56:48 -07002408 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002409 // At this point we need a real paint object. If the caller passed null, then we should
2410 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2411 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2412 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002413
reeda2217ef2016-07-20 06:04:34 -07002414 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002415 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2416 *paint);
2417 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002418 special = this->getDevice()->makeSpecial(image);
2419 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002420 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002421 }
2422 }
2423
Mike Reed38992392019-07-30 10:48:15 -04002424 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002425
reeda85d4d02015-05-06 12:56:48 -07002426 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002427 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002428 if (special) {
2429 SkPoint pt;
Mike Reeda1361362017-03-07 09:37:29 -05002430 iter.fDevice->ctm().mapXY(x, y, &pt);
2431 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002432 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002433 SkScalarRoundToInt(pt.fY), pnt,
2434 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002435 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002436 iter.fDevice->drawImageRect(
2437 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2438 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002439 }
reeda85d4d02015-05-06 12:56:48 -07002440 }
halcanary9d524f22016-03-29 09:03:52 -07002441
Mike Reed38992392019-07-30 10:48:15 -04002442 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002443}
2444
reed41af9662015-01-05 07:49:08 -08002445void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002446 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002447 SkPaint realPaint;
2448 paint = init_image_paint(&realPaint, paint);
2449
halcanary96fcdcc2015-08-27 07:41:13 -07002450 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002451 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002452 if (paint) {
2453 paint->computeFastBounds(dst, &storage);
2454 }
2455 if (this->quickReject(storage)) {
2456 return;
2457 }
reeda85d4d02015-05-06 12:56:48 -07002458 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002459 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002460
Mike Reed38992392019-07-30 10:48:15 -04002461 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002462
reeda85d4d02015-05-06 12:56:48 -07002463 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002464 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002465 }
halcanary9d524f22016-03-29 09:03:52 -07002466
Mike Reed38992392019-07-30 10:48:15 -04002467 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002468}
2469
reed41af9662015-01-05 07:49:08 -08002470void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471 SkDEBUGCODE(bitmap.validate();)
2472
reed33366972015-10-08 09:22:02 -07002473 if (bitmap.drawsNothing()) {
2474 return;
2475 }
2476
Mike Reedf441cfc2018-04-11 14:50:16 -04002477 SkPaint realPaint;
2478 init_image_paint(&realPaint, paint);
2479 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002480
Mike Reed822128b2017-02-28 16:41:03 -05002481 SkRect bounds;
2482 bitmap.getBounds(&bounds);
2483 bounds.offset(x, y);
2484 bool canFastBounds = paint->canComputeFastBounds();
2485 if (canFastBounds) {
2486 SkRect storage;
2487 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002488 return;
2489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002490 }
reed@google.com4b226022011-01-11 18:32:13 +00002491
reeda2217ef2016-07-20 06:04:34 -07002492 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002493 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2494 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002495 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002496 special = this->getDevice()->makeSpecial(bitmap);
2497 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002498 drawAsSprite = false;
2499 }
2500 }
2501
Mike Reed38992392019-07-30 10:48:15 -04002502 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002503
2504 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002505 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002506 if (special) {
reed262a71b2015-12-05 13:07:27 -08002507 SkPoint pt;
Mike Reeda1361362017-03-07 09:37:29 -05002508 iter.fDevice->ctm().mapXY(x, y, &pt);
2509 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002510 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002511 SkScalarRoundToInt(pt.fY), pnt,
2512 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002513 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002514 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2515 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2516 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002517 }
reed33366972015-10-08 09:22:02 -07002518 }
msarettfbfa2582016-08-12 08:29:08 -07002519
Mike Reed38992392019-07-30 10:48:15 -04002520 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521}
2522
reed@google.com9987ec32011-09-07 11:57:52 +00002523// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002524void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002525 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002526 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002527 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528 return;
2529 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002530
halcanary96fcdcc2015-08-27 07:41:13 -07002531 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002532 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002533 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2534 return;
2535 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002536 }
reed@google.com3d608122011-11-21 15:16:16 +00002537
reed@google.com33535f32012-09-25 15:37:50 +00002538 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002539 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002540 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002541 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002542
Mike Reed38992392019-07-30 10:48:15 -04002543 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002544
reed@google.com33535f32012-09-25 15:37:50 +00002545 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002546 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002547 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002548
Mike Reed38992392019-07-30 10:48:15 -04002549 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002550}
2551
reed41af9662015-01-05 07:49:08 -08002552void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002553 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002554 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002555 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002556}
2557
reed4c21dc52015-06-25 12:32:03 -07002558void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2559 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002560 SkPaint realPaint;
2561 paint = init_image_paint(&realPaint, paint);
2562
halcanary96fcdcc2015-08-27 07:41:13 -07002563 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002564 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002565 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2566 return;
2567 }
reed@google.com3d608122011-11-21 15:16:16 +00002568 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002569 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002570
Mike Reed38992392019-07-30 10:48:15 -04002571 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002572
reed4c21dc52015-06-25 12:32:03 -07002573 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002574 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002575 }
halcanary9d524f22016-03-29 09:03:52 -07002576
Mike Reed38992392019-07-30 10:48:15 -04002577 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002578}
2579
reed41af9662015-01-05 07:49:08 -08002580void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2581 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002582 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002583 SkPaint realPaint;
2584 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002585
halcanary96fcdcc2015-08-27 07:41:13 -07002586 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002587 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002588 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2589 return;
2590 }
reed4c21dc52015-06-25 12:32:03 -07002591 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002592 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002593
Mike Reed38992392019-07-30 10:48:15 -04002594 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002595
reed4c21dc52015-06-25 12:32:03 -07002596 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002597 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002598 }
halcanary9d524f22016-03-29 09:03:52 -07002599
Mike Reed38992392019-07-30 10:48:15 -04002600 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002601}
2602
msarett16882062016-08-16 09:31:08 -07002603void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2604 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002605 SkPaint realPaint;
2606 paint = init_image_paint(&realPaint, paint);
2607
msarett16882062016-08-16 09:31:08 -07002608 if (nullptr == paint || paint->canComputeFastBounds()) {
2609 SkRect storage;
2610 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2611 return;
2612 }
2613 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002614 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002615
Mike Reed38992392019-07-30 10:48:15 -04002616 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002617
2618 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002619 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002620 }
2621
Mike Reed38992392019-07-30 10:48:15 -04002622 DRAW_END
msarett16882062016-08-16 09:31:08 -07002623}
2624
2625void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2626 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002627 SkPaint realPaint;
2628 paint = init_image_paint(&realPaint, paint);
2629
msarett16882062016-08-16 09:31:08 -07002630 if (nullptr == paint || paint->canComputeFastBounds()) {
2631 SkRect storage;
2632 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2633 return;
2634 }
2635 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002636 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002637
Mike Reed38992392019-07-30 10:48:15 -04002638 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002639
2640 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002641 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002642 }
2643
Mike Reed38992392019-07-30 10:48:15 -04002644 DRAW_END
msarett16882062016-08-16 09:31:08 -07002645}
2646
fmalita00d5c2c2014-08-21 08:53:26 -07002647void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2648 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002649 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002650 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002651 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002652 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002653 SkRect tmp;
2654 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2655 return;
2656 }
2657 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002658 }
2659
fmalita024f9962015-03-03 19:08:17 -08002660 // We cannot filter in the looper as we normally do, because the paint is
2661 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002662 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002663
fmalitaaa1b9122014-08-28 14:32:24 -07002664 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002665 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002666 }
2667
Mike Reed38992392019-07-30 10:48:15 -04002668 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002669}
2670
Mike Reed358fcad2018-11-23 15:27:51 -05002671// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002672void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002673 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2674 TRACE_EVENT0("skia", TRACE_FUNC);
2675 if (byteLength) {
2676 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002677 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002678 }
2679}
Mike Reed4f81bb72019-01-23 09:23:00 -05002680
fmalita00d5c2c2014-08-21 08:53:26 -07002681void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2682 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002683 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002684 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002685 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002686 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002687}
reed@google.come0d9ce82014-04-23 04:00:17 +00002688
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002689void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002690 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002691 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002692
2693 while (iter.next()) {
2694 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002695 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002696 }
2697
Mike Reed38992392019-07-30 10:48:15 -04002698 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002699}
2700
dandovb3c9d1c2014-08-12 08:34:29 -07002701void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002702 const SkPoint texCoords[4], SkBlendMode bmode,
2703 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002704 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002705 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002706 return;
2707 }
mtklein6cfa73a2014-08-13 13:33:49 -07002708
Mike Reedfaba3712016-11-03 14:45:31 -04002709 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002710}
2711
2712void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002713 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002714 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002715 // Since a patch is always within the convex hull of the control points, we discard it when its
2716 // bounding rectangle is completely outside the current clip.
2717 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002718 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002719 if (this->quickReject(bounds)) {
2720 return;
2721 }
mtklein6cfa73a2014-08-13 13:33:49 -07002722
Mike Reed38992392019-07-30 10:48:15 -04002723 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002724
dandovecfff212014-08-04 10:02:00 -07002725 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002726 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002727 }
mtklein6cfa73a2014-08-13 13:33:49 -07002728
Mike Reed38992392019-07-30 10:48:15 -04002729 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002730}
2731
reeda8db7282015-07-07 10:22:31 -07002732void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002733#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002734 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002735#endif
reede3b38ce2016-01-08 09:18:44 -08002736 RETURN_ON_NULL(dr);
2737 if (x || y) {
2738 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2739 this->onDrawDrawable(dr, &matrix);
2740 } else {
2741 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002742 }
2743}
2744
reeda8db7282015-07-07 10:22:31 -07002745void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002746#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002747 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002748#endif
reede3b38ce2016-01-08 09:18:44 -08002749 RETURN_ON_NULL(dr);
2750 if (matrix && matrix->isIdentity()) {
2751 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002752 }
reede3b38ce2016-01-08 09:18:44 -08002753 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002754}
2755
2756void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002757 // drawable bounds are no longer reliable (e.g. android displaylist)
2758 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002759 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002760}
2761
reed71c3c762015-06-24 10:29:17 -07002762void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002763 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002764 const SkRect* cull, const SkPaint* paint) {
2765 if (cull && this->quickReject(*cull)) {
2766 return;
2767 }
2768
2769 SkPaint pnt;
2770 if (paint) {
2771 pnt = *paint;
2772 }
halcanary9d524f22016-03-29 09:03:52 -07002773
Mike Reed38992392019-07-30 10:48:15 -04002774 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002775 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002776 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002777 }
Mike Reed38992392019-07-30 10:48:15 -04002778 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002779}
2780
reedf70b5312016-03-04 16:36:20 -08002781void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2782 SkASSERT(key);
2783
2784 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002785 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002786 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002787 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002788 }
Mike Reed38992392019-07-30 10:48:15 -04002789 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002790}
2791
Michael Ludwiga595f862019-08-27 15:25:49 -04002792void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2793 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002794 SkASSERT(r.isSorted());
2795
2796 // If this used a paint, it would be a filled color with blend mode, which does not
2797 // need to use an autodraw loop, so use SkDrawIter directly.
2798 if (this->quickReject(r)) {
2799 return;
2800 }
2801
Michael Ludwiga4b44882019-08-28 14:34:58 -04002802 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002803 SkDrawIter iter(this);
2804 while(iter.next()) {
2805 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2806 }
2807}
2808
2809void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2810 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2811 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002812 if (count <= 0) {
2813 // Nothing to draw
2814 return;
2815 }
2816
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002817 SkPaint realPaint;
2818 init_image_paint(&realPaint, paint);
2819
Michael Ludwiga4b44882019-08-28 14:34:58 -04002820 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2821 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2822 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2823 // or we need it for the autolooper (since it greatly improves image filter perf).
2824 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2825 bool setBoundsValid = count == 1 || needsAutoLooper;
2826 SkRect setBounds = imageSet[0].fDstRect;
2827 if (imageSet[0].fMatrixIndex >= 0) {
2828 // Account for the per-entry transform that is applied prior to the CTM when drawing
2829 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002830 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002831 if (needsAutoLooper) {
2832 for (int i = 1; i < count; ++i) {
2833 SkRect entryBounds = imageSet[i].fDstRect;
2834 if (imageSet[i].fMatrixIndex >= 0) {
2835 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2836 }
2837 setBounds.joinPossiblyEmptyRect(entryBounds);
2838 }
2839 }
2840
2841 // If we happen to have the draw bounds, though, might as well check quickReject().
2842 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2843 SkRect tmp;
2844 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2845 return;
2846 }
2847 }
2848
2849 if (needsAutoLooper) {
2850 SkASSERT(setBoundsValid);
2851 DRAW_BEGIN(realPaint, &setBounds)
2852 while (iter.next()) {
2853 iter.fDevice->drawEdgeAAImageSet(
2854 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2855 }
2856 DRAW_END
2857 } else {
2858 this->predrawNotify();
2859 SkDrawIter iter(this);
2860 while(iter.next()) {
2861 iter.fDevice->drawEdgeAAImageSet(
2862 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2863 }
2864 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002865}
2866
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867//////////////////////////////////////////////////////////////////////////////
2868// These methods are NOT virtual, and therefore must call back into virtual
2869// methods, rather than actually drawing themselves.
2870//////////////////////////////////////////////////////////////////////////////
2871
reed374772b2016-10-05 17:33:02 -07002872void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002873 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002875 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 this->drawPaint(paint);
2877}
2878
2879void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002880 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2882}
2883
Mike Reed3661bc92017-02-22 13:21:42 -05002884void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002885 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002886 pts[0].set(x0, y0);
2887 pts[1].set(x1, y1);
2888 this->drawPoints(kLines_PointMode, 2, pts, paint);
2889}
2890
Mike Reed3661bc92017-02-22 13:21:42 -05002891void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002892 if (radius < 0) {
2893 radius = 0;
2894 }
2895
2896 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002897 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002898 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002899}
2900
2901void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2902 const SkPaint& paint) {
2903 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002904 SkRRect rrect;
2905 rrect.setRectXY(r, rx, ry);
2906 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002907 } else {
2908 this->drawRect(r, paint);
2909 }
2910}
2911
reed@android.com8a1c16f2008-12-17 15:59:43 +00002912void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2913 SkScalar sweepAngle, bool useCenter,
2914 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002915 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002916 if (oval.isEmpty() || !sweepAngle) {
2917 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002918 }
bsalomon21af9ca2016-08-25 12:29:23 -07002919 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920}
2921
reed@android.comf76bacf2009-05-13 14:00:33 +00002922///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002923#ifdef SK_DISABLE_SKPICTURE
2924void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002925
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002926
2927void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2928 const SkPaint* paint) {}
2929#else
Mike Klein88d90712018-01-27 17:30:04 +00002930/**
2931 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2932 * against the playback cost of recursing into the subpicture to get at its actual ops.
2933 *
2934 * For now we pick a conservatively small value, though measurement (and other heuristics like
2935 * the type of ops contained) may justify changing this value.
2936 */
2937#define kMaxPictureOpsToUnrollInsteadOfRef 1
2938
reedd5fa1a42014-08-09 11:08:05 -07002939void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002940 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002941 RETURN_ON_NULL(picture);
2942
reede3b38ce2016-01-08 09:18:44 -08002943 if (matrix && matrix->isIdentity()) {
2944 matrix = nullptr;
2945 }
Mike Klein88d90712018-01-27 17:30:04 +00002946 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2947 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2948 picture->playback(this);
2949 } else {
2950 this->onDrawPicture(picture, matrix, paint);
2951 }
reedd5fa1a42014-08-09 11:08:05 -07002952}
robertphillips9b14f262014-06-04 05:40:44 -07002953
reedd5fa1a42014-08-09 11:08:05 -07002954void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2955 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002956 if (!paint || paint->canComputeFastBounds()) {
2957 SkRect bounds = picture->cullRect();
2958 if (paint) {
2959 paint->computeFastBounds(bounds, &bounds);
2960 }
2961 if (matrix) {
2962 matrix->mapRect(&bounds);
2963 }
2964 if (this->quickReject(bounds)) {
2965 return;
2966 }
2967 }
2968
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002969 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002970 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002971}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002972#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973
reed@android.com8a1c16f2008-12-17 15:59:43 +00002974///////////////////////////////////////////////////////////////////////////////
2975///////////////////////////////////////////////////////////////////////////////
2976
reed3aafe112016-08-18 12:45:34 -07002977SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002978 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002979
2980 SkASSERT(canvas);
2981
reed3aafe112016-08-18 12:45:34 -07002982 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 fDone = !fImpl->next();
2984}
2985
2986SkCanvas::LayerIter::~LayerIter() {
2987 fImpl->~SkDrawIter();
2988}
2989
2990void SkCanvas::LayerIter::next() {
2991 fDone = !fImpl->next();
2992}
2993
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002994SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05002995 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996}
2997
2998const SkMatrix& SkCanvas::LayerIter::matrix() const {
Mike Reeda1361362017-03-07 09:37:29 -05002999 return fImpl->fDevice->ctm();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000}
3001
3002const SkPaint& SkCanvas::LayerIter::paint() const {
3003 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003004 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003005 paint = &fDefaultPaint;
3006 }
3007 return *paint;
3008}
3009
Mike Reedca37f322018-03-08 13:22:16 -05003010SkIRect SkCanvas::LayerIter::clipBounds() const {
3011 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003012}
3013
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3015int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003016
3017///////////////////////////////////////////////////////////////////////////////
3018
Brian Osmane8a98632019-04-10 10:26:10 -04003019SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3020SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3021SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3022SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3023
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003024SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3025 const SkRect& dstRect, int matrixIndex, float alpha,
3026 unsigned aaFlags, bool hasClip)
3027 : fImage(std::move(image))
3028 , fSrcRect(srcRect)
3029 , fDstRect(dstRect)
3030 , fMatrixIndex(matrixIndex)
3031 , fAlpha(alpha)
3032 , fAAFlags(aaFlags)
3033 , fHasClip(hasClip) {}
3034
3035SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3036 const SkRect& dstRect, float alpha, unsigned aaFlags)
3037 : fImage(std::move(image))
3038 , fSrcRect(srcRect)
3039 , fDstRect(dstRect)
3040 , fAlpha(alpha)
3041 , fAAFlags(aaFlags) {}
3042
3043///////////////////////////////////////////////////////////////////////////////
3044
Mike Reed5df49342016-11-12 08:06:55 -06003045std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003046 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003047 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003048 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003049 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003050
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003051 SkBitmap bitmap;
3052 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003053 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003054 }
Mike Reed12f77342017-11-08 11:19:52 -05003055
3056 return props ?
3057 skstd::make_unique<SkCanvas>(bitmap, *props) :
3058 skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003059}
reedd5fa1a42014-08-09 11:08:05 -07003060
3061///////////////////////////////////////////////////////////////////////////////
3062
Florin Malitaee424ac2016-12-01 12:47:59 -05003063SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003064 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003065
Florin Malita439ace92016-12-02 12:05:41 -05003066SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003067 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003068
Herb Derbyefe39bc2018-05-01 17:06:20 -04003069SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003070 : INHERITED(device) {}
3071
Florin Malitaee424ac2016-12-01 12:47:59 -05003072SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3073 (void)this->INHERITED::getSaveLayerStrategy(rec);
3074 return kNoLayer_SaveLayerStrategy;
3075}
3076
Mike Reed148b7fd2018-12-18 17:38:18 -05003077bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3078 return false;
3079}
3080
Florin Malitaee424ac2016-12-01 12:47:59 -05003081///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003082
reed73603f32016-09-20 08:42:38 -07003083static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3084static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3085static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3086static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3087static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3088static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003089
3090///////////////////////////////////////////////////////////////////////////////////////////////////
3091
3092SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3093 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003094 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003095 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
3096 SkIPoint origin = dev->getOrigin();
3097 SkMatrix ctm = this->getTotalMatrix();
3098 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3099
3100 SkIRect clip = fMCRec->fRasterClip.getBounds();
3101 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003102 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003103 clip.setEmpty();
3104 }
3105
3106 fAllocator->updateHandle(handle, ctm, clip);
3107 return handle;
3108 }
3109 return nullptr;
3110}
3111
3112static bool install(SkBitmap* bm, const SkImageInfo& info,
3113 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003114 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003115}
3116
3117SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3118 SkBitmap* bm) {
3119 SkRasterHandleAllocator::Rec rec;
3120 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3121 return nullptr;
3122 }
3123 return rec.fHandle;
3124}
3125
3126std::unique_ptr<SkCanvas>
3127SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3128 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003129 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003130 return nullptr;
3131 }
3132
3133 SkBitmap bm;
3134 Handle hndl;
3135
3136 if (rec) {
3137 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3138 } else {
3139 hndl = alloc->allocBitmap(info, &bm);
3140 }
3141 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3142}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003143
3144///////////////////////////////////////////////////////////////////////////////////////////////////