blob: 357f3f57d3aa4b56153fc115d2f33adeefde0519 [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 Ludwigb6e89222019-09-05 13:10:21 -0400888 // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected
889 // with this rect.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400890 const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height());
Michael Ludwig08b260c2019-05-17 11:21:53 -0400891
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400892 if (!filter) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400893 // All non-filtered devices are currently axis aligned, so they only differ by their origin.
894 // This means that we only have to copy a dst-sized block of pixels out of src and translate
895 // it to the matching position relative to dst's origin.
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400896 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) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400905 // The image is drawn at 1-1 scale with integer translation, so no filtering is needed.
906 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400907 dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I());
908 }
909 return;
Michael Ludwig7d2ad0f2019-05-03 17:09:47 -0400910 }
911
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400912 // First decompose the ctm into a post-filter transform and a filter matrix that is supported
913 // by the backdrop filter.
914 SkMatrix toRoot, layerMatrix;
915 SkSize scale;
916 if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
917 toRoot = SkMatrix::I();
918 layerMatrix = ctm;
919 } else if (ctm.decomposeScale(&scale, &toRoot)) {
920 layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
921 } else {
922 // Perspective, for now, do no scaling of the layer itself.
923 // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from
924 // the matrix, e.g. based on the midpoint of the near/far planes?
925 toRoot = ctm;
926 layerMatrix = SkMatrix::I();
927 }
928
929 // We have to map the dst bounds from the root space into the layer space where filtering will
930 // occur. If we knew the input bounds of the content that defined the original dst bounds, we
931 // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds
932 // is a safe, conservative estimate.
933 SkMatrix fromRoot;
934 if (!toRoot.invert(&fromRoot)) {
935 return;
936 }
937
938 // This represents what the backdrop filter needs to produce in the layer space, and is sized
939 // such that drawing it into dst with the toRoot transform will cover the actual dst device.
940 SkIRect layerTargetBounds = fromRoot.mapRect(
941 SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut();
942 // While layerTargetBounds is what needs to be output by the filter, the filtering process may
943 // require some extra input pixels.
944 SkIRect layerInputBounds = filter->filterBounds(
945 layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection,
946 &layerTargetBounds);
947
948 // Map the required input into the root space, then make relative to the src device. This will
Michael Ludwigb6e89222019-09-05 13:10:21 -0400949 // be the conservative contents required to fill a layerInputBounds-sized surface with the
950 // backdrop content (transformed back into the layer space using fromRoot).
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400951 SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut();
952 backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y());
953 if (!backdropBounds.intersect(srcDevRect)) {
954 return;
955 }
956
957 auto special = src->snapSpecial(backdropBounds);
958 if (!special) {
959 return;
960 }
961
962 SkColorType colorType = src->imageInfo().colorType();
963 if (colorType == kUnknown_SkColorType) {
964 colorType = kRGBA_8888_SkColorType;
965 }
966 SkColorSpace* colorSpace = src->imageInfo().colorSpace();
Michael Ludwigb6e89222019-09-05 13:10:21 -0400967
968 SkPaint p;
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400969 if (!toRoot.isIdentity()) {
Michael Ludwigb6e89222019-09-05 13:10:21 -0400970 // Drawing the temporary and final filtered image requires a higher filter quality if the
971 // 'toRoot' transformation is not identity, in order to minimize the impact on already
972 // rendered edges/content.
973 // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs
974 p.setFilterQuality(kHigh_SkFilterQuality);
975
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400976 // The snapped backdrop content needs to be transformed by fromRoot into the layer space,
977 // and stored in a temporary surface, which is then used as the input to the actual filter.
978 auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size());
979 if (!tmpSurface) {
980 return;
981 }
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400982
983 auto tmpCanvas = tmpSurface->getCanvas();
984 tmpCanvas->clear(SK_ColorTRANSPARENT);
985 // Reading in reverse, this takes the backdrop bounds from src device space into the root
986 // space, then maps from root space into the layer space, then maps it so the input layer's
987 // top left corner is (0, 0). This transformation automatically accounts for any cropping
988 // performed on backdropBounds.
989 tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
990 tmpCanvas->concat(fromRoot);
991 tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -0400992
Michael Ludwig6d1c0d42019-09-04 17:39:42 -0400993 tmpCanvas->drawImageRect(special->asImage(), special->subset(),
994 SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint);
995 special = tmpSurface->makeImageSnapshot();
996 } else {
997 // Since there is no extra transform that was done, update the input bounds to reflect
998 // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds
999 // was equal to backdropBounds before it was made relative to the src device and cropped.
1000 // When we use the original snapped image directly, just map the update backdrop bounds
1001 // back into the shared layer space
1002 layerInputBounds = backdropBounds;
1003 layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001004
1005 // Similar to the unfiltered case above, when toRoot is the identity, then the final
1006 // draw will be 1-1 so there is no need to increase filter quality.
1007 p.setFilterQuality(kNone_SkFilterQuality);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001008 }
1009
1010 // Now evaluate the filter on 'special', which contains the backdrop content mapped back into
1011 // layer space. This has to further offset everything so that filter evaluation thinks the
1012 // source image's top left corner is (0, 0).
1013 // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs,
1014 // this can be simplified.
1015 layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1016 SkMatrix filterCTM = layerMatrix;
1017 filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop);
1018 skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get());
1019
1020 SkIPoint offset;
1021 special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset);
reeda2217ef2016-07-20 06:04:34 -07001022 if (special) {
Michael Ludwigb6e89222019-09-05 13:10:21 -04001023 // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin
1024 // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM'
1025 // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative
1026 // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot').
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001027 offset += layerInputBounds.topLeft();
1028
1029 // Manually setting the device's CTM requires accounting for the device's origin.
1030 // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured
1031 // before filtering the backdrop device, and if SkAutoDeviceCTMRestore had a way to accept
1032 // a global CTM instead of a device CTM.
1033 SkMatrix dstCTM = toRoot;
1034 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
1035 SkAutoDeviceCTMRestore acr(dst, dstCTM);
1036
1037 // And because devices don't have a special-image draw function that supports arbitrary
1038 // matrices, we are abusing the asImage() functionality here...
1039 SkRect specialSrc = SkRect::Make(special->subset());
Michael Ludwigb6e89222019-09-05 13:10:21 -04001040 auto looseImage = special->asImage();
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001041 dst->drawImageRect(
Michael Ludwigb6e89222019-09-05 13:10:21 -04001042 looseImage.get(), &specialSrc,
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001043 SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()),
1044 p, kStrict_SrcRectConstraint);
reeda2217ef2016-07-20 06:04:34 -07001045 }
robertphillips7354a4b2015-12-16 05:08:27 -08001046}
reed70ee31b2015-12-10 13:44:45 -08001047
Mike Reed25394292019-03-07 09:36:36 -05001048// This is shared by all backends, but contains raster-specific thoughts. Can we defer to the
1049// device to perform this?
Mike Kleine083f7c2018-02-07 12:54:27 -05001050static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001051 // Need to force L32 for now if we have an image filter.
Mike Reed25394292019-03-07 09:36:36 -05001052 // If filters ever support other colortypes, e.g. F16, we can modify this check.
Mike Klein649fb732018-02-26 15:09:16 -05001053 if (paint && paint->getImageFilter()) {
Mike Reed25394292019-03-07 09:36:36 -05001054 // TODO: can we query the imagefilter, to see if it can handle floats (so we don't always
1055 // use N32 when the layer itself was float)?
1056 return SkImageInfo::MakeN32Premul(w, h, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001057 }
Mike Klein649fb732018-02-26 15:09:16 -05001058
1059 SkColorType ct = prev.colorType();
1060 if (prev.bytesPerPixel() <= 4) {
1061 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1062 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1063 ct = kN32_SkColorType;
1064 }
1065 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001069 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001070 const SkRect* bounds = rec.fBounds;
1071 const SkPaint* paint = rec.fPaint;
1072 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1073
Mike Reed5532c2a2019-02-23 12:00:32 -05001074 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1075 // regardless of any hint-rect from the caller. skbug.com/8783
1076 if (rec.fBackdrop) {
1077 bounds = nullptr;
1078 }
1079
reed8c30a812016-04-20 16:36:51 -07001080 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001081 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001082 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001083 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001084
reed8c30a812016-04-20 16:36:51 -07001085 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001086 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1087 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1088 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001089 *
1090 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001091 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1092 * if necessary.
1093 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1094 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001095 * 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 -04001096 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001097 * of the original imagefilter, and draw that (via drawSprite)
1098 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1099 *
1100 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1101 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1102 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001103 if (imageFilter) {
1104 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001105 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1106 &modifiedCTM);
1107 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001108 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001109 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001110 modifiedRec = fMCRec;
1111 this->internalSetMatrix(modifiedCTM);
1112 SkPaint* p = lazyP.set(*paint);
1113 p->setImageFilter(std::move(modifiedFilter));
1114 imageFilter = p->getImageFilter();
1115 paint = p;
1116 }
1117 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1118 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001119 }
reed8c30a812016-04-20 16:36:51 -07001120
junov@chromium.orga907ac32012-02-24 21:54:07 +00001121 // do this before we create the layer. We don't call the public save() since
1122 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001123 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124
junov@chromium.orga907ac32012-02-24 21:54:07 +00001125 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001126 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001127 if (modifiedRec) {
1128 // In this case there will be no layer in which to stash the matrix so we need to
1129 // revert the prior MCRec to its earlier state.
1130 modifiedRec->fMatrix = stashedMatrix;
1131 }
reed2ff1fce2014-12-11 07:07:37 -08001132 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 }
1134
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001135 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1136 // the clipRectBounds() call above?
1137 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001138 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001139 }
1140
reed8dc0ccb2015-03-20 06:32:52 -07001141 SkPixelGeometry geo = fProps.pixelGeometry();
1142 if (paint) {
reed76033be2015-03-14 10:54:31 -07001143 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001144 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001145 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001146 }
1147 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148
robertphillips5139e502016-07-19 05:10:40 -07001149 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001150 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001151 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001152 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001153 }
reedb2db8982014-11-13 12:41:02 -08001154
Mike Kleine083f7c2018-02-07 12:54:27 -05001155 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001156 if (rec.fSaveLayerFlags & kF16ColorType) {
1157 info = info.makeColorType(kRGBA_F16_SkColorType);
1158 }
reed129ed1c2016-02-22 06:42:31 -08001159
Hal Canary704cd322016-11-07 14:13:52 -05001160 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001161 {
Florin Malita4571e492019-07-16 10:25:58 -04001162 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001163 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001164 const bool trackCoverage =
1165 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001166 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001167 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001168 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001169 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1170 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001171 return;
reed61f501f2015-04-29 08:34:00 -07001172 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001173 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001174 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
Mike Reedb43a3e02017-02-11 10:18:58 -05001176 // only have a "next" if this new layer doesn't affect the clip (rare)
1177 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 fMCRec->fLayer = layer;
1179 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001180
Mike Reedc61abee2017-02-28 17:45:27 -05001181 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001182 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001183 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001184 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001185
Mike Reedc42a1cd2017-02-14 14:25:14 -05001186 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1187
1188 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1189 if (layer->fNext) {
1190 // need to punch a hole in the previous device, so we don't draw there, given that
1191 // the new top-layer will allow drawing to happen "below" it.
1192 SkRegion hole(ir);
1193 do {
1194 layer = layer->fNext;
1195 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1196 } while (layer->fNext);
1197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001200int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001201 if (0xFF == alpha) {
1202 return this->saveLayer(bounds, nullptr);
1203 } else {
1204 SkPaint tmpPaint;
1205 tmpPaint.setAlpha(alpha);
1206 return this->saveLayer(bounds, &tmpPaint);
1207 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001208}
1209
Mike Reed148b7fd2018-12-18 17:38:18 -05001210void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
1211 SkIRect devBounds;
1212 if (localBounds) {
1213 SkRect tmp;
1214 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1215 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1216 devBounds.setEmpty();
1217 }
1218 } else {
1219 devBounds = this->getDeviceClipBounds();
1220 }
1221 if (devBounds.isEmpty()) {
1222 return;
1223 }
1224
1225 SkBaseDevice* device = this->getTopDevice();
1226 if (nullptr == device) { // Do we still need this check???
1227 return;
1228 }
1229
1230 // need the bounds relative to the device itself
1231 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
1232
Michael Ludwigac352122019-08-28 21:03:05 +00001233 // This is getting the special image from the current device, which is then drawn into (both by
1234 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1235 // own device, we need to explicitly copy the back image contents so that its original content
1236 // is available when we splat it back later during restore.
1237 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001238 if (!backImage) {
1239 return;
1240 }
1241
1242 // we really need the save, so we can wack the fMCRec
1243 this->checkForDeferredSave();
1244
1245 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1246
1247 SkPaint paint;
1248 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001249 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001250}
1251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252void SkCanvas::internalRestore() {
1253 SkASSERT(fMCStack.count() != 0);
1254
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001255 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 DeviceCM* layer = fMCRec->fLayer; // may be null
1257 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001258 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259
Mike Reed148b7fd2018-12-18 17:38:18 -05001260 // move this out before we do the actual restore
1261 auto backImage = std::move(fMCRec->fBackImage);
1262
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 // now do the normal restore()
1264 fMCRec->~MCRec(); // balanced in save()
1265 fMCStack.pop_back();
1266 fMCRec = (MCRec*)fMCStack.back();
1267
Mike Reedc42a1cd2017-02-14 14:25:14 -05001268 if (fMCRec) {
1269 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1270 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001271
Mike Reed148b7fd2018-12-18 17:38:18 -05001272 if (backImage) {
1273 SkPaint paint;
1274 paint.setBlendMode(SkBlendMode::kDstOver);
1275 const int x = backImage->fLoc.x();
1276 const int y = backImage->fLoc.y();
1277 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1278 nullptr, SkMatrix::I());
1279 }
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1282 since if we're being recorded, we don't want to record this (the
1283 recorder will have already recorded the restore).
1284 */
bsalomon49f085d2014-09-05 13:34:00 -07001285 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001286 if (fMCRec) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001287 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001288 layer->fDevice->setImmutable();
Florin Malita713b8ef2017-04-28 10:57:24 -04001289 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001290 layer->fPaint.get(),
1291 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001292 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001293 this->internalSetMatrix(layer->fStashedMatrix);
reed@google.com8926b162012-03-23 15:36:36 +00001294 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001295 delete layer;
reedb679ca82015-04-07 04:40:48 -07001296 } else {
1297 // we're at the root
reeda499f902015-05-01 09:34:31 -07001298 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001299 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001300 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001302 }
msarettfbfa2582016-08-12 08:29:08 -07001303
1304 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001305 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001306 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
reede8f30622016-03-23 18:59:25 -07001310sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001311 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001312 props = &fProps;
1313 }
1314 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001315}
1316
reede8f30622016-03-23 18:59:25 -07001317sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001318 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001319 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001320}
1321
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001322SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001323 return this->onImageInfo();
1324}
1325
1326SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001327 SkBaseDevice* dev = this->getDevice();
1328 if (dev) {
1329 return dev->imageInfo();
1330 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001331 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001332 }
1333}
1334
brianosman898235c2016-04-06 07:38:23 -07001335bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001336 return this->onGetProps(props);
1337}
1338
1339bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001340 SkBaseDevice* dev = this->getDevice();
1341 if (dev) {
1342 if (props) {
1343 *props = fProps;
1344 }
1345 return true;
1346 } else {
1347 return false;
1348 }
1349}
1350
reed6ceeebd2016-03-09 14:26:26 -08001351bool SkCanvas::peekPixels(SkPixmap* pmap) {
1352 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353}
1354
reed884e97c2015-05-26 11:31:54 -07001355bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001356 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001357 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001358}
1359
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001360void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001361 SkPixmap pmap;
1362 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001363 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001364 }
1365 if (info) {
1366 *info = pmap.info();
1367 }
1368 if (rowBytes) {
1369 *rowBytes = pmap.rowBytes();
1370 }
1371 if (origin) {
Florin Malita0ed3b642017-01-13 16:56:38 +00001372 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001373 }
reed884e97c2015-05-26 11:31:54 -07001374 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001375}
1376
reed884e97c2015-05-26 11:31:54 -07001377bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001378 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001379 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001380}
1381
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383
Mike Reed8bcd1282019-03-13 16:51:54 -04001384// In our current design/features, we should never have a layer (src) in a different colorspace
1385// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1386// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1387// colorspace.
1388static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1389 SkASSERT(src == dst);
1390}
1391
Florin Malita53f77bd2017-04-28 13:48:37 -04001392void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
1393 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001395 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 paint = &tmp;
1397 }
reed@google.com4b226022011-01-11 18:32:13 +00001398
Mike Reed38992392019-07-30 10:48:15 -04001399 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001402 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001403 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1404 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001405 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001406 SkImageFilter* filter = paint->getImageFilter();
1407 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001408 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001409 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1410 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001411 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1412 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001413 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1414 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001415 }
reed@google.com76dd2772012-01-05 21:15:07 +00001416 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001417 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
reeda2217ef2016-07-20 06:04:34 -07001420
Mike Reed38992392019-07-30 10:48:15 -04001421 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
reed32704672015-12-16 08:27:10 -08001424/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001425
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001426void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001427 if (dx || dy) {
1428 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001429 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001430
reedfe69b502016-09-12 06:31:48 -07001431 // Translate shouldn't affect the is-scale-translateness of the matrix.
1432 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001433
Mike Reedc42a1cd2017-02-14 14:25:14 -05001434 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001435
reedfe69b502016-09-12 06:31:48 -07001436 this->didTranslate(dx,dy);
1437 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001440void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001441 SkMatrix m;
1442 m.setScale(sx, sy);
1443 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001447 SkMatrix m;
1448 m.setRotate(degrees);
1449 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
bungeman7438bfc2016-07-12 15:01:19 -07001452void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1453 SkMatrix m;
1454 m.setRotate(degrees, px, py);
1455 this->concat(m);
1456}
1457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001459 SkMatrix m;
1460 m.setSkew(sx, sy);
1461 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001462}
1463
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001464void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001465 if (matrix.isIdentity()) {
1466 return;
1467 }
1468
reed2ff1fce2014-12-11 07:07:37 -08001469 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001470 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001471 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001472
Mike Reed7627fa52017-02-08 10:07:53 -05001473 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001474
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001475 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001476}
1477
reed8c30a812016-04-20 16:36:51 -07001478void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001479 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001480 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001481
Mike Reedc42a1cd2017-02-14 14:25:14 -05001482 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001483}
1484
1485void SkCanvas::setMatrix(const SkMatrix& matrix) {
1486 this->checkForDeferredSave();
1487 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001488 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001492 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
1495//////////////////////////////////////////////////////////////////////////////
1496
Mike Reedc1f77742016-12-09 09:00:50 -05001497void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001498 if (!rect.isFinite()) {
1499 return;
1500 }
reed2ff1fce2014-12-11 07:07:37 -08001501 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001502 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1503 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504}
1505
Mike Reedc1f77742016-12-09 09:00:50 -05001506void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001507 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001508
Mike Reed7627fa52017-02-08 10:07:53 -05001509 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001510
reedc64eff52015-11-21 12:39:45 -08001511 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001512 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1513 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001514 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515}
1516
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001517void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1518 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001519 if (fClipRestrictionRect.isEmpty()) {
1520 // we notify the device, but we *dont* resolve deferred saves (since we're just
1521 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001522 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001523 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001524 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001525 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001526 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001527 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001528 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1529 }
1530}
1531
Mike Reedc1f77742016-12-09 09:00:50 -05001532void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001533 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001535 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001536 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1537 } else {
1538 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001539 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001540}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001541
Mike Reedc1f77742016-12-09 09:00:50 -05001542void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001543 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001544
Brian Salomona3b45d42016-10-03 11:36:16 -04001545 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001546
Mike Reed7627fa52017-02-08 10:07:53 -05001547 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001548
Mike Reed20800c82017-11-15 16:09:04 -05001549 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1550 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001551 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552}
1553
Mike Reedc1f77742016-12-09 09:00:50 -05001554void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001557
1558 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1559 SkRect r;
1560 if (path.isRect(&r)) {
1561 this->onClipRect(r, op, edgeStyle);
1562 return;
1563 }
1564 SkRRect rrect;
1565 if (path.isOval(&r)) {
1566 rrect.setOval(r);
1567 this->onClipRRect(rrect, op, edgeStyle);
1568 return;
1569 }
1570 if (path.isRRect(&rrect)) {
1571 this->onClipRRect(rrect, op, edgeStyle);
1572 return;
1573 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574 }
robertphillips39f05382015-11-24 09:30:12 -08001575
1576 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577}
1578
Mike Reedc1f77742016-12-09 09:00:50 -05001579void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001580 AutoValidateClip avc(this);
1581
Brian Salomona3b45d42016-10-03 11:36:16 -04001582 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001583
Mike Reed7627fa52017-02-08 10:07:53 -05001584 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585
Brian Salomona3b45d42016-10-03 11:36:16 -04001586 const SkPath* rasterClipPath = &path;
1587 const SkMatrix* matrix = &fMCRec->fMatrix;
Mike Reed20800c82017-11-15 16:09:04 -05001588 fMCRec->fRasterClip.opPath(*rasterClipPath, *matrix, this->getTopLayerBounds(),
1589 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001590 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591}
1592
Mike Reedc1f77742016-12-09 09:00:50 -05001593void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001594 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596}
1597
Mike Reedc1f77742016-12-09 09:00:50 -05001598void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001599 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001600
reed@google.com5c3d1472011-02-22 19:12:23 +00001601 AutoValidateClip avc(this);
1602
Mike Reed20800c82017-11-15 16:09:04 -05001603 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001604 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605}
1606
reed@google.com819c9212011-02-23 18:56:55 +00001607#ifdef SK_DEBUG
1608void SkCanvas::validateClip() const {
1609 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001610 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001611 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001612 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001613 return;
1614 }
reed@google.com819c9212011-02-23 18:56:55 +00001615}
1616#endif
1617
Mike Reeda1361362017-03-07 09:37:29 -05001618bool SkCanvas::androidFramework_isClipAA() const {
1619 bool containsAA = false;
1620
1621 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1622
1623 return containsAA;
1624}
1625
1626class RgnAccumulator {
1627 SkRegion* fRgn;
1628public:
1629 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1630 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1631 SkIPoint origin = device->getOrigin();
1632 if (origin.x() | origin.y()) {
1633 rgn->translate(origin.x(), origin.y());
1634 }
1635 fRgn->op(*rgn, SkRegion::kUnion_Op);
1636 }
1637};
1638
1639void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1640 RgnAccumulator accum(rgn);
1641 SkRegion tmp;
1642
1643 rgn->setEmpty();
1644 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001645}
1646
reed@google.com5c3d1472011-02-22 19:12:23 +00001647///////////////////////////////////////////////////////////////////////////////
1648
reed@google.com754de5f2014-02-24 19:38:20 +00001649bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001650 return fMCRec->fRasterClip.isEmpty();
1651
1652 // TODO: should we only use the conservative answer in a recording canvas?
1653#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001654 SkBaseDevice* dev = this->getTopDevice();
1655 // if no device we return true
1656 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001657#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001658}
1659
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001660bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001661 SkBaseDevice* dev = this->getTopDevice();
1662 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001663 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001664}
1665
msarettfbfa2582016-08-12 08:29:08 -07001666static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1667#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1668 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1669 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1670 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1671 return 0xF != _mm_movemask_ps(mask);
1672#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1673 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1674 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1675 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1676 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1677#else
1678 SkRect devRectAsRect;
1679 SkRect devClipAsRect;
1680 devRect.store(&devRectAsRect.fLeft);
1681 devClip.store(&devClipAsRect.fLeft);
1682 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1683#endif
1684}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001685
msarettfbfa2582016-08-12 08:29:08 -07001686// It's important for this function to not be inlined. Otherwise the compiler will share code
1687// between the fast path and the slow path, resulting in two slow paths.
1688static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1689 const SkMatrix& matrix) {
1690 SkRect deviceRect;
1691 matrix.mapRect(&deviceRect, src);
1692 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1693}
1694
1695bool SkCanvas::quickReject(const SkRect& src) const {
1696#ifdef SK_DEBUG
1697 // Verify that fDeviceClipBounds are set properly.
1698 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001699 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001700 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001701 } else {
msarettfbfa2582016-08-12 08:29:08 -07001702 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 }
msarettfbfa2582016-08-12 08:29:08 -07001704
msarett9637ea92016-08-18 14:03:30 -07001705 // Verify that fIsScaleTranslate is set properly.
1706 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001707#endif
1708
msarett9637ea92016-08-18 14:03:30 -07001709 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001710 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1711 }
1712
1713 // We inline the implementation of mapScaleTranslate() for the fast path.
1714 float sx = fMCRec->fMatrix.getScaleX();
1715 float sy = fMCRec->fMatrix.getScaleY();
1716 float tx = fMCRec->fMatrix.getTranslateX();
1717 float ty = fMCRec->fMatrix.getTranslateY();
1718 Sk4f scale(sx, sy, sx, sy);
1719 Sk4f trans(tx, ty, tx, ty);
1720
1721 // Apply matrix.
1722 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1723
1724 // Make sure left < right, top < bottom.
1725 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1726 Sk4f min = Sk4f::Min(ltrb, rblt);
1727 Sk4f max = Sk4f::Max(ltrb, rblt);
1728 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1729 // ARM this sequence generates the fastest (a single instruction).
1730 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1731
1732 // Check if the device rect is NaN or outside the clip.
1733 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734}
1735
reed@google.com3b3e8952012-08-16 20:53:31 +00001736bool SkCanvas::quickReject(const SkPath& path) const {
1737 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738}
1739
Mike Klein83c8dd92017-11-28 17:08:45 -05001740SkRect SkCanvas::getLocalClipBounds() const {
1741 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001742 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001743 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 }
1745
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001746 SkMatrix inverse;
1747 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001748 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001749 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001750 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751
Mike Reed42e8c532017-01-23 14:09:13 -05001752 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001753 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001754 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001755
Mike Reedb57b9312018-04-23 12:12:54 -04001756 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001757 inverse.mapRect(&bounds, r);
1758 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759}
1760
Mike Klein83c8dd92017-11-28 17:08:45 -05001761SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001762 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001763}
1764
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001766 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767}
1768
Brian Osman11052242016-10-27 14:47:55 -04001769GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001770 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001771 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001772}
1773
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001774GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001775 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001776 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001777}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001778
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001779void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1780 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001781 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001782 if (outer.isEmpty()) {
1783 return;
1784 }
1785 if (inner.isEmpty()) {
1786 this->drawRRect(outer, paint);
1787 return;
1788 }
1789
1790 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001791 // be able to return ...
1792 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001793 //
1794 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001795 if (!outer.getBounds().contains(inner.getBounds())) {
1796 return;
1797 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001798
1799 this->onDrawDRRect(outer, inner, paint);
1800}
1801
reed41af9662015-01-05 07:49:08 -08001802void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001803 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001804 this->onDrawPaint(paint);
1805}
1806
1807void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001808 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001809 // To avoid redundant logic in our culling code and various backends, we always sort rects
1810 // before passing them along.
1811 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001812}
1813
Mike Reedd5674082019-04-19 15:00:47 -04001814void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1815 TRACE_EVENT0("skia", TRACE_FUNC);
1816 this->onDrawBehind(paint);
1817}
1818
msarettdca352e2016-08-26 06:37:45 -07001819void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001820 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001821 if (region.isEmpty()) {
1822 return;
1823 }
1824
1825 if (region.isRect()) {
1826 return this->drawIRect(region.getBounds(), paint);
1827 }
1828
1829 this->onDrawRegion(region, paint);
1830}
1831
reed41af9662015-01-05 07:49:08 -08001832void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001833 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001834 // To avoid redundant logic in our culling code and various backends, we always sort rects
1835 // before passing them along.
1836 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001837}
1838
1839void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001840 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001841 this->onDrawRRect(rrect, paint);
1842}
1843
1844void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001845 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001846 this->onDrawPoints(mode, count, pts, paint);
1847}
1848
Mike Reede88a1cb2017-03-17 09:50:46 -04001849void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1850 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001851 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001852 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001853 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1854 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001855 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001856}
1857
1858void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001859 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001860 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001861 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1862}
1863
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001864void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1865 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001866 TRACE_EVENT0("skia", TRACE_FUNC);
1867 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001868 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001869 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1870}
1871
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001872void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
1873 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001874 TRACE_EVENT0("skia", TRACE_FUNC);
1875 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001876 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001877 this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001878}
1879
1880void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001881 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001882 this->onDrawPath(path, paint);
1883}
1884
reeda85d4d02015-05-06 12:56:48 -07001885void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001886 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001887 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001888 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001889}
1890
Mike Reedc4e31092018-01-30 11:15:27 -05001891// Returns true if the rect can be "filled" : non-empty and finite
1892static bool fillable(const SkRect& r) {
1893 SkScalar w = r.width();
1894 SkScalar h = r.height();
1895 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1896}
1897
reede47829b2015-08-06 10:02:53 -07001898void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1899 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001900 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001901 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001902 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001903 return;
1904 }
1905 this->onDrawImageRect(image, &src, dst, paint, constraint);
1906}
reed41af9662015-01-05 07:49:08 -08001907
reed84984ef2015-07-17 07:09:43 -07001908void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1909 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001910 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001911 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001912}
1913
Brian Salomonf08002c2018-10-26 16:15:46 -04001914void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001915 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001916 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001917 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001918}
reede47829b2015-08-06 10:02:53 -07001919
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001920namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001921class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001922public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001923 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1924 if (!origPaint) {
1925 return;
1926 }
1927 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1928 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1929 }
1930 if (origPaint->getMaskFilter()) {
1931 fPaint.writable()->setMaskFilter(nullptr);
1932 }
1933 if (origPaint->isAntiAlias()) {
1934 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001935 }
1936 }
1937
1938 const SkPaint* get() const {
1939 return fPaint;
1940 }
1941
1942private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001943 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001944};
1945} // namespace
1946
reed4c21dc52015-06-25 12:32:03 -07001947void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1948 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001949 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001950 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001951 if (dst.isEmpty()) {
1952 return;
1953 }
msarett552bca92016-08-03 06:53:26 -07001954 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001955 LatticePaint latticePaint(paint);
1956 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001957 } else {
reede47829b2015-08-06 10:02:53 -07001958 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001959 }
reed4c21dc52015-06-25 12:32:03 -07001960}
1961
msarett16882062016-08-16 09:31:08 -07001962void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1963 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001964 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001965 RETURN_ON_NULL(image);
1966 if (dst.isEmpty()) {
1967 return;
1968 }
msarett71df2d72016-09-30 12:41:42 -07001969
1970 SkIRect bounds;
1971 Lattice latticePlusBounds = lattice;
1972 if (!latticePlusBounds.fBounds) {
1973 bounds = SkIRect::MakeWH(image->width(), image->height());
1974 latticePlusBounds.fBounds = &bounds;
1975 }
1976
1977 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001978 LatticePaint latticePaint(paint);
1979 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07001980 } else {
1981 this->drawImageRect(image, dst, paint);
1982 }
1983}
1984
reed41af9662015-01-05 07:49:08 -08001985void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001986 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07001987 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001988 return;
1989 }
reed41af9662015-01-05 07:49:08 -08001990 this->onDrawBitmap(bitmap, dx, dy, paint);
1991}
1992
reede47829b2015-08-06 10:02:53 -07001993void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001994 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001995 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07001996 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001997 return;
1998 }
reede47829b2015-08-06 10:02:53 -07001999 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002000}
2001
reed84984ef2015-07-17 07:09:43 -07002002void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2003 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002004 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002005}
2006
reede47829b2015-08-06 10:02:53 -07002007void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2008 SrcRectConstraint constraint) {
2009 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2010 constraint);
2011}
reede47829b2015-08-06 10:02:53 -07002012
reed41af9662015-01-05 07:49:08 -08002013void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2014 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002015 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002016 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002017 return;
2018 }
msarett552bca92016-08-03 06:53:26 -07002019 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002020 LatticePaint latticePaint(paint);
2021 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002022 } else {
reeda5517e22015-07-14 10:54:12 -07002023 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002024 }
reed41af9662015-01-05 07:49:08 -08002025}
2026
msarettc573a402016-08-02 08:05:56 -07002027void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2028 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002029 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002030 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002031 return;
2032 }
msarett71df2d72016-09-30 12:41:42 -07002033
2034 SkIRect bounds;
2035 Lattice latticePlusBounds = lattice;
2036 if (!latticePlusBounds.fBounds) {
2037 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2038 latticePlusBounds.fBounds = &bounds;
2039 }
2040
2041 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002042 LatticePaint latticePaint(paint);
2043 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002044 } else {
msarett16882062016-08-16 09:31:08 -07002045 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002046 }
msarettc573a402016-08-02 08:05:56 -07002047}
2048
reed71c3c762015-06-24 10:29:17 -07002049void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002050 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002051 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002052 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002053 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002054 if (count <= 0) {
2055 return;
2056 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002057 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002058 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002059 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002060}
2061
reedf70b5312016-03-04 16:36:20 -08002062void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002063 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002064 if (key) {
2065 this->onDrawAnnotation(rect, key, value);
2066 }
2067}
2068
reede47829b2015-08-06 10:02:53 -07002069void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2070 const SkPaint* paint, SrcRectConstraint constraint) {
2071 if (src) {
2072 this->drawImageRect(image, *src, dst, paint, constraint);
2073 } else {
2074 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2075 dst, paint, constraint);
2076 }
2077}
2078void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2079 const SkPaint* paint, SrcRectConstraint constraint) {
2080 if (src) {
2081 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2082 } else {
2083 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2084 dst, paint, constraint);
2085 }
2086}
2087
Mike Reed4204da22017-05-17 08:53:36 -04002088void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002089 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002090 this->onDrawShadowRec(path, rec);
2091}
2092
2093void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2094 SkPaint paint;
2095 const SkRect& pathBounds = path.getBounds();
2096
Mike Reed38992392019-07-30 10:48:15 -04002097 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002098 while (iter.next()) {
2099 iter.fDevice->drawShadow(path, rec);
2100 }
Mike Reed38992392019-07-30 10:48:15 -04002101 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002102}
2103
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002104void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002105 QuadAAFlags aaFlags, const SkColor4f& color,
2106 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002107 TRACE_EVENT0("skia", TRACE_FUNC);
2108 // Make sure the rect is sorted before passing it along
2109 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2110}
2111
2112void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2113 const SkPoint dstClips[],
2114 const SkMatrix preViewMatrices[],
2115 const SkPaint* paint,
2116 SrcRectConstraint constraint) {
2117 TRACE_EVENT0("skia", TRACE_FUNC);
2118 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2119}
2120
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121//////////////////////////////////////////////////////////////////////////////
2122// These are the virtual drawing methods
2123//////////////////////////////////////////////////////////////////////////////
2124
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002125void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002126 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002127 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2128 }
2129}
2130
reed41af9662015-01-05 07:49:08 -08002131void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002132 this->internalDrawPaint(paint);
2133}
2134
2135void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002136 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137
2138 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002139 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 }
2141
Mike Reed38992392019-07-30 10:48:15 -04002142 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143}
2144
reed41af9662015-01-05 07:49:08 -08002145void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2146 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 if ((long)count <= 0) {
2148 return;
2149 }
2150
Mike Reed822128b2017-02-28 16:41:03 -05002151 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002152 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002153 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002154 // special-case 2 points (common for drawing a single line)
2155 if (2 == count) {
2156 r.set(pts[0], pts[1]);
2157 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002158 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002159 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002160 if (!r.isFinite()) {
2161 return;
2162 }
Mike Reed822128b2017-02-28 16:41:03 -05002163 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002164 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2165 return;
2166 }
2167 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002168 }
reed@google.coma584aed2012-05-16 14:06:02 +00002169
halcanary96fcdcc2015-08-27 07:41:13 -07002170 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171
Mike Reed38992392019-07-30 10:48:15 -04002172 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002173
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002175 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 }
reed@google.com4b226022011-01-11 18:32:13 +00002177
Mike Reed38992392019-07-30 10:48:15 -04002178 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179}
2180
reed4a167172016-08-18 17:15:25 -07002181static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002182 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002183}
2184
reed41af9662015-01-05 07:49:08 -08002185void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002186 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002188 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002189 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002190 return;
2191 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192 }
reed@google.com4b226022011-01-11 18:32:13 +00002193
reed4a167172016-08-18 17:15:25 -07002194 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002195 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196
reed4a167172016-08-18 17:15:25 -07002197 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002198 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002199 }
2200
Mike Reed38992392019-07-30 10:48:15 -04002201 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002202 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002203 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002204 SkDrawIter iter(this);
2205 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002206 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209}
2210
msarett44df6512016-08-25 13:54:30 -07002211void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002212 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002213 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002214 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002215 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2216 return;
2217 }
msarett44df6512016-08-25 13:54:30 -07002218 }
2219
Mike Reed38992392019-07-30 10:48:15 -04002220 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002221
2222 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002223 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002224 }
2225
Mike Reed38992392019-07-30 10:48:15 -04002226 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002227}
2228
Mike Reedd5674082019-04-19 15:00:47 -04002229void SkCanvas::onDrawBehind(const SkPaint& paint) {
2230 SkIRect bounds;
2231 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2232 for (;;) {
2233 const MCRec* rec = (const MCRec*)iter.prev();
2234 if (!rec) {
2235 return; // no backimages, so nothing to draw
2236 }
2237 if (rec->fBackImage) {
2238 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2239 rec->fBackImage->fImage->width(),
2240 rec->fBackImage->fImage->height());
2241 break;
2242 }
2243 }
2244
Mike Reed38992392019-07-30 10:48:15 -04002245 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002246
2247 while (iter.next()) {
2248 SkBaseDevice* dev = iter.fDevice;
2249
Mike Reedd5674082019-04-19 15:00:47 -04002250 dev->save();
2251 // We use clipRegion because it is already defined to operate in dev-space
2252 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2253 // but we don't want that, so we undo that before calling in.
2254 SkRegion rgn(bounds.makeOffset(dev->fOrigin.fX, dev->fOrigin.fY));
2255 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002256 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002257 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002258 }
2259
Mike Reed38992392019-07-30 10:48:15 -04002260 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002261}
2262
reed41af9662015-01-05 07:49:08 -08002263void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002264 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002265 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002266 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002267 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002268 return;
2269 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002270 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002271
Mike Reed38992392019-07-30 10:48:15 -04002272 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002273
2274 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002275 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002276 }
2277
Mike Reed38992392019-07-30 10:48:15 -04002278 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002279}
2280
bsalomonac3aa242016-08-19 11:25:19 -07002281void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2282 SkScalar sweepAngle, bool useCenter,
2283 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002284 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002285 if (paint.canComputeFastBounds()) {
2286 SkRect storage;
2287 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002288 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002289 return;
2290 }
bsalomonac3aa242016-08-19 11:25:19 -07002291 }
2292
Mike Reed38992392019-07-30 10:48:15 -04002293 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002294
2295 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002296 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002297 }
2298
Mike Reed38992392019-07-30 10:48:15 -04002299 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002300}
2301
reed41af9662015-01-05 07:49:08 -08002302void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002303 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002304 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002305 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2306 return;
2307 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002308 }
2309
2310 if (rrect.isRect()) {
2311 // call the non-virtual version
2312 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002313 return;
2314 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002315 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002316 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2317 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002318 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002319
Mike Reed38992392019-07-30 10:48:15 -04002320 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002321
2322 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002323 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002324 }
2325
Mike Reed38992392019-07-30 10:48:15 -04002326 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002327}
2328
Mike Reed822128b2017-02-28 16:41:03 -05002329void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002330 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002331 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002332 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2333 return;
2334 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002335 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002336
Mike Reed38992392019-07-30 10:48:15 -04002337 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002338
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002339 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002340 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002341 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002342
Mike Reed38992392019-07-30 10:48:15 -04002343 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002344}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002345
reed41af9662015-01-05 07:49:08 -08002346void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002347 if (!path.isFinite()) {
2348 return;
2349 }
2350
Mike Reed822128b2017-02-28 16:41:03 -05002351 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002352 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002353 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002354 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2355 return;
2356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002358
Mike Reed822128b2017-02-28 16:41:03 -05002359 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002360 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002361 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002362 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002363 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002364 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365
Mike Reed38992392019-07-30 10:48:15 -04002366 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367
2368 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002369 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 }
2371
Mike Reed38992392019-07-30 10:48:15 -04002372 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373}
2374
reed262a71b2015-12-05 13:07:27 -08002375bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002376 if (!paint.getImageFilter()) {
2377 return false;
2378 }
2379
2380 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002381 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002382 return false;
2383 }
2384
2385 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2386 // Once we can filter and the filter will return a result larger than itself, we should be
2387 // able to remove this constraint.
2388 // skbug.com/4526
2389 //
2390 SkPoint pt;
2391 ctm.mapXY(x, y, &pt);
2392 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2393 return ir.contains(fMCRec->fRasterClip.getBounds());
2394}
2395
Mike Reedf441cfc2018-04-11 14:50:16 -04002396// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2397// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2398// null.
2399static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2400 if (paintParam) {
2401 *real = *paintParam;
2402 real->setStyle(SkPaint::kFill_Style);
2403 real->setPathEffect(nullptr);
2404 paintParam = real;
2405 }
2406 return paintParam;
2407}
2408
reeda85d4d02015-05-06 12:56:48 -07002409void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002410 SkPaint realPaint;
2411 paint = init_image_paint(&realPaint, paint);
2412
reeda85d4d02015-05-06 12:56:48 -07002413 SkRect bounds = SkRect::MakeXYWH(x, y,
2414 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002415 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002416 SkRect tmp = bounds;
2417 if (paint) {
2418 paint->computeFastBounds(tmp, &tmp);
2419 }
2420 if (this->quickReject(tmp)) {
2421 return;
2422 }
reeda85d4d02015-05-06 12:56:48 -07002423 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002424 // At this point we need a real paint object. If the caller passed null, then we should
2425 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2426 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2427 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002428
reeda2217ef2016-07-20 06:04:34 -07002429 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002430 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2431 *paint);
2432 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002433 special = this->getDevice()->makeSpecial(image);
2434 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002435 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002436 }
2437 }
2438
Mike Reed38992392019-07-30 10:48:15 -04002439 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002440
reeda85d4d02015-05-06 12:56:48 -07002441 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002442 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002443 if (special) {
2444 SkPoint pt;
Mike Reeda1361362017-03-07 09:37:29 -05002445 iter.fDevice->ctm().mapXY(x, y, &pt);
2446 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002447 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002448 SkScalarRoundToInt(pt.fY), pnt,
2449 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002450 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002451 iter.fDevice->drawImageRect(
2452 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2453 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002454 }
reeda85d4d02015-05-06 12:56:48 -07002455 }
halcanary9d524f22016-03-29 09:03:52 -07002456
Mike Reed38992392019-07-30 10:48:15 -04002457 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002458}
2459
reed41af9662015-01-05 07:49:08 -08002460void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002461 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002462 SkPaint realPaint;
2463 paint = init_image_paint(&realPaint, paint);
2464
halcanary96fcdcc2015-08-27 07:41:13 -07002465 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002466 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002467 if (paint) {
2468 paint->computeFastBounds(dst, &storage);
2469 }
2470 if (this->quickReject(storage)) {
2471 return;
2472 }
reeda85d4d02015-05-06 12:56:48 -07002473 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002474 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002475
Mike Reed38992392019-07-30 10:48:15 -04002476 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002477
reeda85d4d02015-05-06 12:56:48 -07002478 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002479 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002480 }
halcanary9d524f22016-03-29 09:03:52 -07002481
Mike Reed38992392019-07-30 10:48:15 -04002482 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002483}
2484
reed41af9662015-01-05 07:49:08 -08002485void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 SkDEBUGCODE(bitmap.validate();)
2487
reed33366972015-10-08 09:22:02 -07002488 if (bitmap.drawsNothing()) {
2489 return;
2490 }
2491
Mike Reedf441cfc2018-04-11 14:50:16 -04002492 SkPaint realPaint;
2493 init_image_paint(&realPaint, paint);
2494 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002495
Mike Reed822128b2017-02-28 16:41:03 -05002496 SkRect bounds;
2497 bitmap.getBounds(&bounds);
2498 bounds.offset(x, y);
2499 bool canFastBounds = paint->canComputeFastBounds();
2500 if (canFastBounds) {
2501 SkRect storage;
2502 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002503 return;
2504 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 }
reed@google.com4b226022011-01-11 18:32:13 +00002506
reeda2217ef2016-07-20 06:04:34 -07002507 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002508 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2509 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002510 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002511 special = this->getDevice()->makeSpecial(bitmap);
2512 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002513 drawAsSprite = false;
2514 }
2515 }
2516
Mike Reed38992392019-07-30 10:48:15 -04002517 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002518
2519 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002520 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002521 if (special) {
reed262a71b2015-12-05 13:07:27 -08002522 SkPoint pt;
Mike Reeda1361362017-03-07 09:37:29 -05002523 iter.fDevice->ctm().mapXY(x, y, &pt);
2524 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002525 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002526 SkScalarRoundToInt(pt.fY), pnt,
2527 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002528 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002529 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2530 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2531 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002532 }
reed33366972015-10-08 09:22:02 -07002533 }
msarettfbfa2582016-08-12 08:29:08 -07002534
Mike Reed38992392019-07-30 10:48:15 -04002535 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002536}
2537
reed@google.com9987ec32011-09-07 11:57:52 +00002538// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002539void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002540 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002541 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002542 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543 return;
2544 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002545
halcanary96fcdcc2015-08-27 07:41:13 -07002546 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002547 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002548 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2549 return;
2550 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551 }
reed@google.com3d608122011-11-21 15:16:16 +00002552
reed@google.com33535f32012-09-25 15:37:50 +00002553 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002554 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002555 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002557
Mike Reed38992392019-07-30 10:48:15 -04002558 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002559
reed@google.com33535f32012-09-25 15:37:50 +00002560 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002561 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002562 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002563
Mike Reed38992392019-07-30 10:48:15 -04002564 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565}
2566
reed41af9662015-01-05 07:49:08 -08002567void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002568 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002569 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002570 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002571}
2572
reed4c21dc52015-06-25 12:32:03 -07002573void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2574 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002575 SkPaint realPaint;
2576 paint = init_image_paint(&realPaint, paint);
2577
halcanary96fcdcc2015-08-27 07:41:13 -07002578 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002579 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002580 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2581 return;
2582 }
reed@google.com3d608122011-11-21 15:16:16 +00002583 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002584 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002585
Mike Reed38992392019-07-30 10:48:15 -04002586 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002587
reed4c21dc52015-06-25 12:32:03 -07002588 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002589 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002590 }
halcanary9d524f22016-03-29 09:03:52 -07002591
Mike Reed38992392019-07-30 10:48:15 -04002592 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002593}
2594
reed41af9662015-01-05 07:49:08 -08002595void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2596 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002597 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002598 SkPaint realPaint;
2599 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002600
halcanary96fcdcc2015-08-27 07:41:13 -07002601 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002602 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002603 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2604 return;
2605 }
reed4c21dc52015-06-25 12:32:03 -07002606 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002607 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002608
Mike Reed38992392019-07-30 10:48:15 -04002609 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002610
reed4c21dc52015-06-25 12:32:03 -07002611 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002612 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002613 }
halcanary9d524f22016-03-29 09:03:52 -07002614
Mike Reed38992392019-07-30 10:48:15 -04002615 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002616}
2617
msarett16882062016-08-16 09:31:08 -07002618void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2619 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002620 SkPaint realPaint;
2621 paint = init_image_paint(&realPaint, paint);
2622
msarett16882062016-08-16 09:31:08 -07002623 if (nullptr == paint || paint->canComputeFastBounds()) {
2624 SkRect storage;
2625 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2626 return;
2627 }
2628 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002629 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002630
Mike Reed38992392019-07-30 10:48:15 -04002631 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002632
2633 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002634 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002635 }
2636
Mike Reed38992392019-07-30 10:48:15 -04002637 DRAW_END
msarett16882062016-08-16 09:31:08 -07002638}
2639
2640void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2641 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002642 SkPaint realPaint;
2643 paint = init_image_paint(&realPaint, paint);
2644
msarett16882062016-08-16 09:31:08 -07002645 if (nullptr == paint || paint->canComputeFastBounds()) {
2646 SkRect storage;
2647 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2648 return;
2649 }
2650 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002651 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002652
Mike Reed38992392019-07-30 10:48:15 -04002653 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002654
2655 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002656 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002657 }
2658
Mike Reed38992392019-07-30 10:48:15 -04002659 DRAW_END
msarett16882062016-08-16 09:31:08 -07002660}
2661
fmalita00d5c2c2014-08-21 08:53:26 -07002662void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2663 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002664 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002665 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002666 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002667 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002668 SkRect tmp;
2669 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2670 return;
2671 }
2672 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002673 }
2674
fmalita024f9962015-03-03 19:08:17 -08002675 // We cannot filter in the looper as we normally do, because the paint is
2676 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002677 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002678
fmalitaaa1b9122014-08-28 14:32:24 -07002679 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002680 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002681 }
2682
Mike Reed38992392019-07-30 10:48:15 -04002683 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002684}
2685
Mike Reed358fcad2018-11-23 15:27:51 -05002686// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002687void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002688 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2689 TRACE_EVENT0("skia", TRACE_FUNC);
2690 if (byteLength) {
2691 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002692 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002693 }
2694}
Mike Reed4f81bb72019-01-23 09:23:00 -05002695
fmalita00d5c2c2014-08-21 08:53:26 -07002696void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2697 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002698 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002699 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002700 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002701 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002702}
reed@google.come0d9ce82014-04-23 04:00:17 +00002703
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002704void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002705 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002706 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002707
2708 while (iter.next()) {
2709 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002710 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002711 }
2712
Mike Reed38992392019-07-30 10:48:15 -04002713 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002714}
2715
dandovb3c9d1c2014-08-12 08:34:29 -07002716void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002717 const SkPoint texCoords[4], SkBlendMode bmode,
2718 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002719 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002720 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002721 return;
2722 }
mtklein6cfa73a2014-08-13 13:33:49 -07002723
Mike Reedfaba3712016-11-03 14:45:31 -04002724 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002725}
2726
2727void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002728 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002729 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002730 // Since a patch is always within the convex hull of the control points, we discard it when its
2731 // bounding rectangle is completely outside the current clip.
2732 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002733 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002734 if (this->quickReject(bounds)) {
2735 return;
2736 }
mtklein6cfa73a2014-08-13 13:33:49 -07002737
Mike Reed38992392019-07-30 10:48:15 -04002738 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002739
dandovecfff212014-08-04 10:02:00 -07002740 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002741 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002742 }
mtklein6cfa73a2014-08-13 13:33:49 -07002743
Mike Reed38992392019-07-30 10:48:15 -04002744 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002745}
2746
reeda8db7282015-07-07 10:22:31 -07002747void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002748#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002749 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002750#endif
reede3b38ce2016-01-08 09:18:44 -08002751 RETURN_ON_NULL(dr);
2752 if (x || y) {
2753 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2754 this->onDrawDrawable(dr, &matrix);
2755 } else {
2756 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002757 }
2758}
2759
reeda8db7282015-07-07 10:22:31 -07002760void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002761#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002762 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002763#endif
reede3b38ce2016-01-08 09:18:44 -08002764 RETURN_ON_NULL(dr);
2765 if (matrix && matrix->isIdentity()) {
2766 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002767 }
reede3b38ce2016-01-08 09:18:44 -08002768 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002769}
2770
2771void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002772 // drawable bounds are no longer reliable (e.g. android displaylist)
2773 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002774 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002775}
2776
reed71c3c762015-06-24 10:29:17 -07002777void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002778 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002779 const SkRect* cull, const SkPaint* paint) {
2780 if (cull && this->quickReject(*cull)) {
2781 return;
2782 }
2783
2784 SkPaint pnt;
2785 if (paint) {
2786 pnt = *paint;
2787 }
halcanary9d524f22016-03-29 09:03:52 -07002788
Mike Reed38992392019-07-30 10:48:15 -04002789 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002790 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002791 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002792 }
Mike Reed38992392019-07-30 10:48:15 -04002793 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002794}
2795
reedf70b5312016-03-04 16:36:20 -08002796void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2797 SkASSERT(key);
2798
2799 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002800 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002801 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002802 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002803 }
Mike Reed38992392019-07-30 10:48:15 -04002804 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002805}
2806
Michael Ludwiga595f862019-08-27 15:25:49 -04002807void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2808 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002809 SkASSERT(r.isSorted());
2810
2811 // If this used a paint, it would be a filled color with blend mode, which does not
2812 // need to use an autodraw loop, so use SkDrawIter directly.
2813 if (this->quickReject(r)) {
2814 return;
2815 }
2816
Michael Ludwiga4b44882019-08-28 14:34:58 -04002817 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002818 SkDrawIter iter(this);
2819 while(iter.next()) {
2820 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2821 }
2822}
2823
2824void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2825 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2826 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002827 if (count <= 0) {
2828 // Nothing to draw
2829 return;
2830 }
2831
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002832 SkPaint realPaint;
2833 init_image_paint(&realPaint, paint);
2834
Michael Ludwiga4b44882019-08-28 14:34:58 -04002835 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2836 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2837 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2838 // or we need it for the autolooper (since it greatly improves image filter perf).
2839 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2840 bool setBoundsValid = count == 1 || needsAutoLooper;
2841 SkRect setBounds = imageSet[0].fDstRect;
2842 if (imageSet[0].fMatrixIndex >= 0) {
2843 // Account for the per-entry transform that is applied prior to the CTM when drawing
2844 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002845 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002846 if (needsAutoLooper) {
2847 for (int i = 1; i < count; ++i) {
2848 SkRect entryBounds = imageSet[i].fDstRect;
2849 if (imageSet[i].fMatrixIndex >= 0) {
2850 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2851 }
2852 setBounds.joinPossiblyEmptyRect(entryBounds);
2853 }
2854 }
2855
2856 // If we happen to have the draw bounds, though, might as well check quickReject().
2857 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2858 SkRect tmp;
2859 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2860 return;
2861 }
2862 }
2863
2864 if (needsAutoLooper) {
2865 SkASSERT(setBoundsValid);
2866 DRAW_BEGIN(realPaint, &setBounds)
2867 while (iter.next()) {
2868 iter.fDevice->drawEdgeAAImageSet(
2869 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2870 }
2871 DRAW_END
2872 } else {
2873 this->predrawNotify();
2874 SkDrawIter iter(this);
2875 while(iter.next()) {
2876 iter.fDevice->drawEdgeAAImageSet(
2877 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2878 }
2879 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002880}
2881
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882//////////////////////////////////////////////////////////////////////////////
2883// These methods are NOT virtual, and therefore must call back into virtual
2884// methods, rather than actually drawing themselves.
2885//////////////////////////////////////////////////////////////////////////////
2886
reed374772b2016-10-05 17:33:02 -07002887void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002889 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002890 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002891 this->drawPaint(paint);
2892}
2893
2894void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002895 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2897}
2898
Mike Reed3661bc92017-02-22 13:21:42 -05002899void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002900 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002901 pts[0].set(x0, y0);
2902 pts[1].set(x1, y1);
2903 this->drawPoints(kLines_PointMode, 2, pts, paint);
2904}
2905
Mike Reed3661bc92017-02-22 13:21:42 -05002906void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002907 if (radius < 0) {
2908 radius = 0;
2909 }
2910
2911 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002912 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002913 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914}
2915
2916void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2917 const SkPaint& paint) {
2918 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002919 SkRRect rrect;
2920 rrect.setRectXY(r, rx, ry);
2921 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002922 } else {
2923 this->drawRect(r, paint);
2924 }
2925}
2926
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2928 SkScalar sweepAngle, bool useCenter,
2929 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002930 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002931 if (oval.isEmpty() || !sweepAngle) {
2932 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933 }
bsalomon21af9ca2016-08-25 12:29:23 -07002934 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935}
2936
reed@android.comf76bacf2009-05-13 14:00:33 +00002937///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002938#ifdef SK_DISABLE_SKPICTURE
2939void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002940
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002941
2942void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2943 const SkPaint* paint) {}
2944#else
Mike Klein88d90712018-01-27 17:30:04 +00002945/**
2946 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2947 * against the playback cost of recursing into the subpicture to get at its actual ops.
2948 *
2949 * For now we pick a conservatively small value, though measurement (and other heuristics like
2950 * the type of ops contained) may justify changing this value.
2951 */
2952#define kMaxPictureOpsToUnrollInsteadOfRef 1
2953
reedd5fa1a42014-08-09 11:08:05 -07002954void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002955 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002956 RETURN_ON_NULL(picture);
2957
reede3b38ce2016-01-08 09:18:44 -08002958 if (matrix && matrix->isIdentity()) {
2959 matrix = nullptr;
2960 }
Mike Klein88d90712018-01-27 17:30:04 +00002961 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2962 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2963 picture->playback(this);
2964 } else {
2965 this->onDrawPicture(picture, matrix, paint);
2966 }
reedd5fa1a42014-08-09 11:08:05 -07002967}
robertphillips9b14f262014-06-04 05:40:44 -07002968
reedd5fa1a42014-08-09 11:08:05 -07002969void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2970 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002971 if (!paint || paint->canComputeFastBounds()) {
2972 SkRect bounds = picture->cullRect();
2973 if (paint) {
2974 paint->computeFastBounds(bounds, &bounds);
2975 }
2976 if (matrix) {
2977 matrix->mapRect(&bounds);
2978 }
2979 if (this->quickReject(bounds)) {
2980 return;
2981 }
2982 }
2983
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002984 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002985 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002987#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989///////////////////////////////////////////////////////////////////////////////
2990///////////////////////////////////////////////////////////////////////////////
2991
reed3aafe112016-08-18 12:45:34 -07002992SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002993 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994
2995 SkASSERT(canvas);
2996
reed3aafe112016-08-18 12:45:34 -07002997 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002998 fDone = !fImpl->next();
2999}
3000
3001SkCanvas::LayerIter::~LayerIter() {
3002 fImpl->~SkDrawIter();
3003}
3004
3005void SkCanvas::LayerIter::next() {
3006 fDone = !fImpl->next();
3007}
3008
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003009SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003010 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011}
3012
3013const SkMatrix& SkCanvas::LayerIter::matrix() const {
Mike Reeda1361362017-03-07 09:37:29 -05003014 return fImpl->fDevice->ctm();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003015}
3016
3017const SkPaint& SkCanvas::LayerIter::paint() const {
3018 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003019 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020 paint = &fDefaultPaint;
3021 }
3022 return *paint;
3023}
3024
Mike Reedca37f322018-03-08 13:22:16 -05003025SkIRect SkCanvas::LayerIter::clipBounds() const {
3026 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003027}
3028
reed@android.com8a1c16f2008-12-17 15:59:43 +00003029int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3030int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003031
3032///////////////////////////////////////////////////////////////////////////////
3033
Brian Osmane8a98632019-04-10 10:26:10 -04003034SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3035SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3036SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3037SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3038
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003039SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3040 const SkRect& dstRect, int matrixIndex, float alpha,
3041 unsigned aaFlags, bool hasClip)
3042 : fImage(std::move(image))
3043 , fSrcRect(srcRect)
3044 , fDstRect(dstRect)
3045 , fMatrixIndex(matrixIndex)
3046 , fAlpha(alpha)
3047 , fAAFlags(aaFlags)
3048 , fHasClip(hasClip) {}
3049
3050SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3051 const SkRect& dstRect, float alpha, unsigned aaFlags)
3052 : fImage(std::move(image))
3053 , fSrcRect(srcRect)
3054 , fDstRect(dstRect)
3055 , fAlpha(alpha)
3056 , fAAFlags(aaFlags) {}
3057
3058///////////////////////////////////////////////////////////////////////////////
3059
Mike Reed5df49342016-11-12 08:06:55 -06003060std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003061 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003062 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003063 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003064 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003065
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003066 SkBitmap bitmap;
3067 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003068 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003069 }
Mike Reed12f77342017-11-08 11:19:52 -05003070
3071 return props ?
3072 skstd::make_unique<SkCanvas>(bitmap, *props) :
3073 skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003074}
reedd5fa1a42014-08-09 11:08:05 -07003075
3076///////////////////////////////////////////////////////////////////////////////
3077
Florin Malitaee424ac2016-12-01 12:47:59 -05003078SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003079 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003080
Florin Malita439ace92016-12-02 12:05:41 -05003081SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003082 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003083
Herb Derbyefe39bc2018-05-01 17:06:20 -04003084SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003085 : INHERITED(device) {}
3086
Florin Malitaee424ac2016-12-01 12:47:59 -05003087SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3088 (void)this->INHERITED::getSaveLayerStrategy(rec);
3089 return kNoLayer_SaveLayerStrategy;
3090}
3091
Mike Reed148b7fd2018-12-18 17:38:18 -05003092bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3093 return false;
3094}
3095
Florin Malitaee424ac2016-12-01 12:47:59 -05003096///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003097
reed73603f32016-09-20 08:42:38 -07003098static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3099static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3100static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3101static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3102static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3103static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003104
3105///////////////////////////////////////////////////////////////////////////////////////////////////
3106
3107SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3108 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003109 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003110 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
3111 SkIPoint origin = dev->getOrigin();
3112 SkMatrix ctm = this->getTotalMatrix();
3113 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3114
3115 SkIRect clip = fMCRec->fRasterClip.getBounds();
3116 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003117 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003118 clip.setEmpty();
3119 }
3120
3121 fAllocator->updateHandle(handle, ctm, clip);
3122 return handle;
3123 }
3124 return nullptr;
3125}
3126
3127static bool install(SkBitmap* bm, const SkImageInfo& info,
3128 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003129 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003130}
3131
3132SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3133 SkBitmap* bm) {
3134 SkRasterHandleAllocator::Rec rec;
3135 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3136 return nullptr;
3137 }
3138 return rec.fHandle;
3139}
3140
3141std::unique_ptr<SkCanvas>
3142SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3143 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003144 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003145 return nullptr;
3146 }
3147
3148 SkBitmap bm;
3149 Handle hndl;
3150
3151 if (rec) {
3152 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3153 } else {
3154 hndl = alloc->allocBitmap(info, &bm);
3155 }
3156 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3157}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003158
3159///////////////////////////////////////////////////////////////////////////////////////////////////