blob: 90335e8747ffaf339e95f037789ad8d7b0321d2a [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
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001031 // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001032 // a global CTM instead of a device CTM.
1033 SkMatrix dstCTM = toRoot;
1034 dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y());
Michael Ludwigc89d1b52019-10-18 11:32:56 -04001035 SkAutoDeviceTransformRestore adr(dst, dstCTM);
Michael Ludwig6d1c0d42019-09-04 17:39:42 -04001036
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 Kleine083f7c2018-02-07 12:54:27 -05001048static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) {
Mike Klein649fb732018-02-26 15:09:16 -05001049 SkColorType ct = prev.colorType();
Brian Osmand7a59752019-09-20 11:16:58 -04001050 if (prev.bytesPerPixel() <= 4 &&
1051 prev.colorType() != kRGBA_8888_SkColorType &&
1052 prev.colorType() != kBGRA_8888_SkColorType) {
Mike Klein649fb732018-02-26 15:09:16 -05001053 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888,
1054 // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return.
1055 ct = kN32_SkColorType;
1056 }
1057 return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace());
reed129ed1c2016-02-22 06:42:31 -08001058}
1059
reed4960eee2015-12-18 07:09:18 -08001060void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
Yuqian Lif7c723c2018-08-29 16:25:47 -07001061 TRACE_EVENT0("skia", TRACE_FUNC);
reed4960eee2015-12-18 07:09:18 -08001062 const SkRect* bounds = rec.fBounds;
1063 const SkPaint* paint = rec.fPaint;
1064 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1065
Mike Reed5532c2a2019-02-23 12:00:32 -05001066 // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds)
1067 // regardless of any hint-rect from the caller. skbug.com/8783
1068 if (rec.fBackdrop) {
1069 bounds = nullptr;
1070 }
1071
reed8c30a812016-04-20 16:36:51 -07001072 SkLazyPaint lazyP;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001073 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : nullptr;
reed8c30a812016-04-20 16:36:51 -07001074 SkMatrix stashedMatrix = fMCRec->fMatrix;
Robert Phillips3d0e8502018-04-20 10:27:27 -04001075 MCRec* modifiedRec = nullptr;
Michael Ludwig08b260c2019-05-17 11:21:53 -04001076
reed8c30a812016-04-20 16:36:51 -07001077 /*
Michael Ludwig08b260c2019-05-17 11:21:53 -04001078 * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that
1079 * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to
1080 * accommodate this, but it requires update the CTM we use when drawing into the layer.
reed8c30a812016-04-20 16:36:51 -07001081 *
1082 * 1. Stash off the current CTM
Michael Ludwig08b260c2019-05-17 11:21:53 -04001083 * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms
1084 * if necessary.
1085 * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which
1086 * is a MatrixImageFilter that contains the complex matrix.
reed8c30a812016-04-20 16:36:51 -07001087 * 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 -04001088 * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output
reed8c30a812016-04-20 16:36:51 -07001089 * of the original imagefilter, and draw that (via drawSprite)
1090 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1091 *
1092 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1093 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1094 */
Michael Ludwig08b260c2019-05-17 11:21:53 -04001095 if (imageFilter) {
1096 SkMatrix modifiedCTM;
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001097 sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix,
1098 &modifiedCTM);
1099 if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) {
Michael Ludwig08b260c2019-05-17 11:21:53 -04001100 // The original filter couldn't support the CTM entirely
Michael Ludwig8ee6cf32019-08-02 09:57:04 -04001101 SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM());
Michael Ludwig08b260c2019-05-17 11:21:53 -04001102 modifiedRec = fMCRec;
1103 this->internalSetMatrix(modifiedCTM);
1104 SkPaint* p = lazyP.set(*paint);
1105 p->setImageFilter(std::move(modifiedFilter));
1106 imageFilter = p->getImageFilter();
1107 paint = p;
1108 }
1109 // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing
1110 // left to do since the stack already has that as the CTM.
reed8c30a812016-04-20 16:36:51 -07001111 }
reed8c30a812016-04-20 16:36:51 -07001112
junov@chromium.orga907ac32012-02-24 21:54:07 +00001113 // do this before we create the layer. We don't call the public save() since
1114 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001115 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001116
junov@chromium.orga907ac32012-02-24 21:54:07 +00001117 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001118 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
Robert Phillips3d0e8502018-04-20 10:27:27 -04001119 if (modifiedRec) {
1120 // In this case there will be no layer in which to stash the matrix so we need to
1121 // revert the prior MCRec to its earlier state.
1122 modifiedRec->fMatrix = stashedMatrix;
1123 }
reed2ff1fce2014-12-11 07:07:37 -08001124 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 }
1126
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001127 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1128 // the clipRectBounds() call above?
1129 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001130 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001131 }
1132
reed8dc0ccb2015-03-20 06:32:52 -07001133 SkPixelGeometry geo = fProps.pixelGeometry();
1134 if (paint) {
reed76033be2015-03-14 10:54:31 -07001135 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001136 if (paint->getImageFilter() || paint->getColorFilter()) {
reed8dc0ccb2015-03-20 06:32:52 -07001137 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001138 }
1139 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140
robertphillips5139e502016-07-19 05:10:40 -07001141 SkBaseDevice* priorDevice = this->getTopDevice();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001142 if (nullptr == priorDevice) { // Do we still need this check???
reedb2db8982014-11-13 12:41:02 -08001143 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001144 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001145 }
reedb2db8982014-11-13 12:41:02 -08001146
Mike Kleine083f7c2018-02-07 12:54:27 -05001147 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint);
Mike Reed1f3548c2019-07-12 12:53:11 -04001148 if (rec.fSaveLayerFlags & kF16ColorType) {
1149 info = info.makeColorType(kRGBA_F16_SkColorType);
1150 }
reed129ed1c2016-02-22 06:42:31 -08001151
Hal Canary704cd322016-11-07 14:13:52 -05001152 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001153 {
Florin Malita4571e492019-07-16 10:25:58 -04001154 SkASSERT(info.alphaType() != kOpaque_SkAlphaType);
reeddaa57bf2015-05-15 10:39:17 -07001155 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
Herb Derby41f4f312018-06-06 17:45:53 +00001156 const bool trackCoverage =
1157 SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
reed70ee31b2015-12-10 13:44:45 -08001158 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
Mike Reed910ca0f2018-04-25 13:04:05 -04001159 trackCoverage,
Mike Reed356f7c22017-01-10 11:58:39 -05001160 fAllocator.get());
robertphillips5139e502016-07-19 05:10:40 -07001161 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1162 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001163 return;
reed61f501f2015-04-29 08:34:00 -07001164 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001165 }
Florin Malita53f77bd2017-04-28 13:48:37 -04001166 DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167
Mike Reedb43a3e02017-02-11 10:18:58 -05001168 // only have a "next" if this new layer doesn't affect the clip (rare)
1169 layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 fMCRec->fLayer = layer;
1171 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001172
Mike Reedc61abee2017-02-28 17:45:27 -05001173 if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) {
Mike Reedc42a1cd2017-02-14 14:25:14 -05001174 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop },
Mike Reeda1361362017-03-07 09:37:29 -05001175 fMCRec->fMatrix);
reeda2217ef2016-07-20 06:04:34 -07001176 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001177
Mike Reedc42a1cd2017-02-14 14:25:14 -05001178 newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop);
1179
1180 newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect);
1181 if (layer->fNext) {
1182 // need to punch a hole in the previous device, so we don't draw there, given that
1183 // the new top-layer will allow drawing to happen "below" it.
1184 SkRegion hole(ir);
1185 do {
1186 layer = layer->fNext;
1187 layer->fDevice->clipRegion(hole, SkClipOp::kDifference);
1188 } while (layer->fNext);
1189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190}
1191
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001192int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001193 if (0xFF == alpha) {
1194 return this->saveLayer(bounds, nullptr);
1195 } else {
1196 SkPaint tmpPaint;
1197 tmpPaint.setAlpha(alpha);
1198 return this->saveLayer(bounds, &tmpPaint);
1199 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001200}
1201
Mike Reed148b7fd2018-12-18 17:38:18 -05001202void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
1203 SkIRect devBounds;
1204 if (localBounds) {
1205 SkRect tmp;
1206 fMCRec->fMatrix.mapRect(&tmp, *localBounds);
1207 if (!devBounds.intersect(tmp.round(), this->getDeviceClipBounds())) {
1208 devBounds.setEmpty();
1209 }
1210 } else {
1211 devBounds = this->getDeviceClipBounds();
1212 }
1213 if (devBounds.isEmpty()) {
1214 return;
1215 }
1216
1217 SkBaseDevice* device = this->getTopDevice();
1218 if (nullptr == device) { // Do we still need this check???
1219 return;
1220 }
1221
1222 // need the bounds relative to the device itself
1223 devBounds.offset(-device->fOrigin.fX, -device->fOrigin.fY);
1224
Michael Ludwigac352122019-08-28 21:03:05 +00001225 // This is getting the special image from the current device, which is then drawn into (both by
1226 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1227 // own device, we need to explicitly copy the back image contents so that its original content
1228 // is available when we splat it back later during restore.
1229 auto backImage = device->snapSpecial(devBounds, /* copy */ true);
Mike Reed148b7fd2018-12-18 17:38:18 -05001230 if (!backImage) {
1231 return;
1232 }
1233
1234 // we really need the save, so we can wack the fMCRec
1235 this->checkForDeferredSave();
1236
1237 fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()});
1238
1239 SkPaint paint;
1240 paint.setBlendMode(SkBlendMode::kClear);
Mike Reed9adc82c2019-04-23 10:28:13 -04001241 this->drawClippedToSaveBehind(paint);
Mike Reed148b7fd2018-12-18 17:38:18 -05001242}
1243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244void SkCanvas::internalRestore() {
1245 SkASSERT(fMCStack.count() != 0);
1246
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001247 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 DeviceCM* layer = fMCRec->fLayer; // may be null
1249 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001250 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251
Mike Reed148b7fd2018-12-18 17:38:18 -05001252 // move this out before we do the actual restore
1253 auto backImage = std::move(fMCRec->fBackImage);
1254
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 // now do the normal restore()
1256 fMCRec->~MCRec(); // balanced in save()
1257 fMCStack.pop_back();
1258 fMCRec = (MCRec*)fMCStack.back();
1259
Mike Reedc42a1cd2017-02-14 14:25:14 -05001260 if (fMCRec) {
1261 FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix));
1262 }
Mike Reedc42a1cd2017-02-14 14:25:14 -05001263
Mike Reed148b7fd2018-12-18 17:38:18 -05001264 if (backImage) {
1265 SkPaint paint;
1266 paint.setBlendMode(SkBlendMode::kDstOver);
1267 const int x = backImage->fLoc.x();
1268 const int y = backImage->fLoc.y();
1269 this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint,
1270 nullptr, SkMatrix::I());
1271 }
1272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1274 since if we're being recorded, we don't want to record this (the
1275 recorder will have already recorded the restore).
1276 */
bsalomon49f085d2014-09-05 13:34:00 -07001277 if (layer) {
Mike Reedb43a3e02017-02-11 10:18:58 -05001278 if (fMCRec) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001279 const SkIPoint& origin = layer->fDevice->getOrigin();
Lee Salzman5d8949c2018-09-19 15:36:54 -04001280 layer->fDevice->setImmutable();
Florin Malita713b8ef2017-04-28 10:57:24 -04001281 this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
Florin Malita53f77bd2017-04-28 13:48:37 -04001282 layer->fPaint.get(),
1283 layer->fClipImage.get(), layer->fClipMatrix);
reed8c30a812016-04-20 16:36:51 -07001284 // restore what we smashed in internalSaveLayer
Ben Wagner738a2562018-04-23 17:23:30 -04001285 this->internalSetMatrix(layer->fStashedMatrix);
reed@google.com8926b162012-03-23 15:36:36 +00001286 // reset this, since internalDrawDevice will have set it to true
halcanary385fe4d2015-08-26 13:07:48 -07001287 delete layer;
reedb679ca82015-04-07 04:40:48 -07001288 } else {
1289 // we're at the root
reeda499f902015-05-01 09:34:31 -07001290 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001291 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001292 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001294 }
msarettfbfa2582016-08-12 08:29:08 -07001295
1296 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001297 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001298 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300}
1301
reede8f30622016-03-23 18:59:25 -07001302sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001303 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001304 props = &fProps;
1305 }
1306 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001307}
1308
reede8f30622016-03-23 18:59:25 -07001309sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001310 SkBaseDevice* dev = this->getDevice();
Cary Clarka24712e2018-09-05 18:41:40 +00001311 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001312}
1313
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001314SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001315 return this->onImageInfo();
1316}
1317
1318SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001319 SkBaseDevice* dev = this->getDevice();
1320 if (dev) {
1321 return dev->imageInfo();
1322 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001323 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001324 }
1325}
1326
brianosman898235c2016-04-06 07:38:23 -07001327bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001328 return this->onGetProps(props);
1329}
1330
1331bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001332 SkBaseDevice* dev = this->getDevice();
1333 if (dev) {
1334 if (props) {
1335 *props = fProps;
1336 }
1337 return true;
1338 } else {
1339 return false;
1340 }
1341}
1342
reed6ceeebd2016-03-09 14:26:26 -08001343bool SkCanvas::peekPixels(SkPixmap* pmap) {
1344 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001345}
1346
reed884e97c2015-05-26 11:31:54 -07001347bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001348 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001349 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001350}
1351
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001352void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001353 SkPixmap pmap;
1354 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001355 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001356 }
1357 if (info) {
1358 *info = pmap.info();
1359 }
1360 if (rowBytes) {
1361 *rowBytes = pmap.rowBytes();
1362 }
1363 if (origin) {
Florin Malita0ed3b642017-01-13 16:56:38 +00001364 *origin = this->getTopDevice()->getOrigin();
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001365 }
reed884e97c2015-05-26 11:31:54 -07001366 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001367}
1368
reed884e97c2015-05-26 11:31:54 -07001369bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001370 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001371 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001372}
1373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375
Mike Reed8bcd1282019-03-13 16:51:54 -04001376// In our current design/features, we should never have a layer (src) in a different colorspace
1377// than its parent (dst), so we assert that here. This is called out from other asserts, in case
1378// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
1379// colorspace.
1380static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
1381 SkASSERT(src == dst);
1382}
1383
Florin Malita53f77bd2017-04-28 13:48:37 -04001384void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
1385 SkImage* clipImage, const SkMatrix& clipMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001387 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 paint = &tmp;
1389 }
reed@google.com4b226022011-01-11 18:32:13 +00001390
Mike Reed38992392019-07-30 10:48:15 -04001391 DRAW_BEGIN_DRAWDEVICE(*paint)
reeda2217ef2016-07-20 06:04:34 -07001392
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001394 SkBaseDevice* dstDev = iter.fDevice;
Mike Reed8bcd1282019-03-13 16:51:54 -04001395 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1396 srcDev->imageInfo().colorSpace());
Mike Reed38992392019-07-30 10:48:15 -04001397 paint = &draw.paint();
reed@google.com76dd2772012-01-05 21:15:07 +00001398 SkImageFilter* filter = paint->getImageFilter();
1399 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Florin Malita53f77bd2017-04-28 13:48:37 -04001400 if (filter || clipImage) {
Robert Phillips833dcf42016-11-18 08:44:13 -05001401 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1402 if (specialImage) {
Mike Reed8bcd1282019-03-13 16:51:54 -04001403 check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(),
1404 specialImage->getColorSpace());
Florin Malita53f77bd2017-04-28 13:48:37 -04001405 dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
1406 clipImage, clipMatrix);
Robert Phillips833dcf42016-11-18 08:44:13 -05001407 }
reed@google.com76dd2772012-01-05 21:15:07 +00001408 } else {
Mike Reeda1361362017-03-07 09:37:29 -05001409 dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001410 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 }
reeda2217ef2016-07-20 06:04:34 -07001412
Mike Reed38992392019-07-30 10:48:15 -04001413 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414}
1415
reed32704672015-12-16 08:27:10 -08001416/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001417
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001418void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001419 if (dx || dy) {
1420 this->checkForDeferredSave();
reedfe69b502016-09-12 06:31:48 -07001421 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001422
reedfe69b502016-09-12 06:31:48 -07001423 // Translate shouldn't affect the is-scale-translateness of the matrix.
1424 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001425
Mike Reedc42a1cd2017-02-14 14:25:14 -05001426 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reedc42a1cd2017-02-14 14:25:14 -05001427
reedfe69b502016-09-12 06:31:48 -07001428 this->didTranslate(dx,dy);
1429 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430}
1431
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001432void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001433 SkMatrix m;
1434 m.setScale(sx, sy);
1435 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436}
1437
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001438void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001439 SkMatrix m;
1440 m.setRotate(degrees);
1441 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
bungeman7438bfc2016-07-12 15:01:19 -07001444void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1445 SkMatrix m;
1446 m.setRotate(degrees, px, py);
1447 this->concat(m);
1448}
1449
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001450void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001451 SkMatrix m;
1452 m.setSkew(sx, sy);
1453 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001454}
1455
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001456void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001457 if (matrix.isIdentity()) {
1458 return;
1459 }
1460
reed2ff1fce2014-12-11 07:07:37 -08001461 this->checkForDeferredSave();
reed1f836ee2014-07-07 07:49:34 -07001462 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001463 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
Mike Reed7627fa52017-02-08 10:07:53 -05001464
Mike Reed7627fa52017-02-08 10:07:53 -05001465 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
Mike Reed7627fa52017-02-08 10:07:53 -05001466
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001467 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001468}
1469
reed8c30a812016-04-20 16:36:51 -07001470void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed1f836ee2014-07-07 07:49:34 -07001471 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001472 fIsScaleTranslate = matrix.isScaleTranslate();
Mike Reedc42a1cd2017-02-14 14:25:14 -05001473
Mike Reedc42a1cd2017-02-14 14:25:14 -05001474 FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
reed8c30a812016-04-20 16:36:51 -07001475}
1476
1477void SkCanvas::setMatrix(const SkMatrix& matrix) {
1478 this->checkForDeferredSave();
1479 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001480 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001484 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
1487//////////////////////////////////////////////////////////////////////////////
1488
Mike Reedc1f77742016-12-09 09:00:50 -05001489void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
Mike Reed9cec1bc2018-01-19 12:57:01 -05001490 if (!rect.isFinite()) {
1491 return;
1492 }
reed2ff1fce2014-12-11 07:07:37 -08001493 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001494 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1495 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001496}
1497
Mike Reedc1f77742016-12-09 09:00:50 -05001498void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001499 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Mike Reed7627fa52017-02-08 10:07:53 -05001500
Mike Reed7627fa52017-02-08 10:07:53 -05001501 FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA));
Mike Reed7627fa52017-02-08 10:07:53 -05001502
reedc64eff52015-11-21 12:39:45 -08001503 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001504 fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1505 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001506 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507}
1508
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001509void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1510 fClipRestrictionRect = rect;
Mike Reedd519d482017-02-16 11:04:52 -05001511 if (fClipRestrictionRect.isEmpty()) {
1512 // we notify the device, but we *dont* resolve deferred saves (since we're just
1513 // removing the restriction if the rect is empty. how I hate this api.
Mike Reedd519d482017-02-16 11:04:52 -05001514 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Mike Reedd519d482017-02-16 11:04:52 -05001515 } else {
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001516 this->checkForDeferredSave();
Mike Reedd519d482017-02-16 11:04:52 -05001517 FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001518 AutoValidateClip avc(this);
Mike Reed20800c82017-11-15 16:09:04 -05001519 fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op);
Stan Iliev5f1bb0a2016-12-12 17:39:55 -05001520 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1521 }
1522}
1523
Mike Reedc1f77742016-12-09 09:00:50 -05001524void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001525 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001527 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1529 } else {
1530 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001531 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001532}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001533
Mike Reedc1f77742016-12-09 09:00:50 -05001534void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001535 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001536
Brian Salomona3b45d42016-10-03 11:36:16 -04001537 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001538
Mike Reed7627fa52017-02-08 10:07:53 -05001539 FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA));
Mike Reeda1361362017-03-07 09:37:29 -05001540
Mike Reed20800c82017-11-15 16:09:04 -05001541 fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1542 isAA);
Brian Salomona3b45d42016-10-03 11:36:16 -04001543 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@google.com4ed0fb72012-12-12 20:48:18 +00001544}
1545
Mike Reedc1f77742016-12-09 09:00:50 -05001546void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001547 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001548 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001549
1550 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1551 SkRect r;
1552 if (path.isRect(&r)) {
1553 this->onClipRect(r, op, edgeStyle);
1554 return;
1555 }
1556 SkRRect rrect;
1557 if (path.isOval(&r)) {
1558 rrect.setOval(r);
1559 this->onClipRRect(rrect, op, edgeStyle);
1560 return;
1561 }
1562 if (path.isRRect(&rrect)) {
1563 this->onClipRRect(rrect, op, edgeStyle);
1564 return;
1565 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001566 }
robertphillips39f05382015-11-24 09:30:12 -08001567
1568 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001569}
1570
Mike Reedc1f77742016-12-09 09:00:50 -05001571void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001572 AutoValidateClip avc(this);
1573
Brian Salomona3b45d42016-10-03 11:36:16 -04001574 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
Ben Wagner63fd7602017-10-09 15:45:33 -04001575
Mike Reed7627fa52017-02-08 10:07:53 -05001576 FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577
Brian Salomona3b45d42016-10-03 11:36:16 -04001578 const SkPath* rasterClipPath = &path;
1579 const SkMatrix* matrix = &fMCRec->fMatrix;
Mike Reed20800c82017-11-15 16:09:04 -05001580 fMCRec->fRasterClip.opPath(*rasterClipPath, *matrix, this->getTopLayerBounds(),
1581 (SkRegion::Op)op, isAA);
msarettfbfa2582016-08-12 08:29:08 -07001582 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583}
1584
Mike Reedc1f77742016-12-09 09:00:50 -05001585void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001586 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001587 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588}
1589
Mike Reedc1f77742016-12-09 09:00:50 -05001590void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
Mike Reed7627fa52017-02-08 10:07:53 -05001591 FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op));
Mike Reeda1361362017-03-07 09:37:29 -05001592
reed@google.com5c3d1472011-02-22 19:12:23 +00001593 AutoValidateClip avc(this);
1594
Mike Reed20800c82017-11-15 16:09:04 -05001595 fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001596 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597}
1598
reed@google.com819c9212011-02-23 18:56:55 +00001599#ifdef SK_DEBUG
1600void SkCanvas::validateClip() const {
1601 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001602 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001603 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001604 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001605 return;
1606 }
reed@google.com819c9212011-02-23 18:56:55 +00001607}
1608#endif
1609
Mike Reeda1361362017-03-07 09:37:29 -05001610bool SkCanvas::androidFramework_isClipAA() const {
1611 bool containsAA = false;
1612
1613 FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA());
1614
1615 return containsAA;
1616}
1617
1618class RgnAccumulator {
1619 SkRegion* fRgn;
1620public:
1621 RgnAccumulator(SkRegion* total) : fRgn(total) {}
1622 void accumulate(SkBaseDevice* device, SkRegion* rgn) {
1623 SkIPoint origin = device->getOrigin();
1624 if (origin.x() | origin.y()) {
1625 rgn->translate(origin.x(), origin.y());
1626 }
1627 fRgn->op(*rgn, SkRegion::kUnion_Op);
1628 }
1629};
1630
1631void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1632 RgnAccumulator accum(rgn);
1633 SkRegion tmp;
1634
1635 rgn->setEmpty();
1636 FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp));
reed@google.com90c07ea2012-04-13 13:50:27 +00001637}
1638
reed@google.com5c3d1472011-02-22 19:12:23 +00001639///////////////////////////////////////////////////////////////////////////////
1640
reed@google.com754de5f2014-02-24 19:38:20 +00001641bool SkCanvas::isClipEmpty() const {
Mike Reed02be3c12017-03-23 12:34:15 -04001642 return fMCRec->fRasterClip.isEmpty();
1643
1644 // TODO: should we only use the conservative answer in a recording canvas?
1645#if 0
Mike Reeda1361362017-03-07 09:37:29 -05001646 SkBaseDevice* dev = this->getTopDevice();
1647 // if no device we return true
1648 return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
Mike Reed02be3c12017-03-23 12:34:15 -04001649#endif
reed@google.com754de5f2014-02-24 19:38:20 +00001650}
1651
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001652bool SkCanvas::isClipRect() const {
Mike Reeda1361362017-03-07 09:37:29 -05001653 SkBaseDevice* dev = this->getTopDevice();
1654 // if no device we return false
Dave Tapuska7139f132019-08-15 09:22:11 -04001655 return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect;
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001656}
1657
msarettfbfa2582016-08-12 08:29:08 -07001658static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1659#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1660 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1661 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1662 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1663 return 0xF != _mm_movemask_ps(mask);
1664#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1665 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1666 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1667 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1668 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1669#else
1670 SkRect devRectAsRect;
1671 SkRect devClipAsRect;
1672 devRect.store(&devRectAsRect.fLeft);
1673 devClip.store(&devClipAsRect.fLeft);
1674 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1675#endif
1676}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001677
msarettfbfa2582016-08-12 08:29:08 -07001678// It's important for this function to not be inlined. Otherwise the compiler will share code
1679// between the fast path and the slow path, resulting in two slow paths.
1680static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1681 const SkMatrix& matrix) {
1682 SkRect deviceRect;
1683 matrix.mapRect(&deviceRect, src);
1684 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1685}
1686
1687bool SkCanvas::quickReject(const SkRect& src) const {
1688#ifdef SK_DEBUG
1689 // Verify that fDeviceClipBounds are set properly.
1690 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001691 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001692 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001693 } else {
msarettfbfa2582016-08-12 08:29:08 -07001694 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 }
msarettfbfa2582016-08-12 08:29:08 -07001696
msarett9637ea92016-08-18 14:03:30 -07001697 // Verify that fIsScaleTranslate is set properly.
1698 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001699#endif
1700
msarett9637ea92016-08-18 14:03:30 -07001701 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001702 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1703 }
1704
1705 // We inline the implementation of mapScaleTranslate() for the fast path.
1706 float sx = fMCRec->fMatrix.getScaleX();
1707 float sy = fMCRec->fMatrix.getScaleY();
1708 float tx = fMCRec->fMatrix.getTranslateX();
1709 float ty = fMCRec->fMatrix.getTranslateY();
1710 Sk4f scale(sx, sy, sx, sy);
1711 Sk4f trans(tx, ty, tx, ty);
1712
1713 // Apply matrix.
1714 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1715
1716 // Make sure left < right, top < bottom.
1717 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1718 Sk4f min = Sk4f::Min(ltrb, rblt);
1719 Sk4f max = Sk4f::Max(ltrb, rblt);
1720 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1721 // ARM this sequence generates the fastest (a single instruction).
1722 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1723
1724 // Check if the device rect is NaN or outside the clip.
1725 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
reed@google.com3b3e8952012-08-16 20:53:31 +00001728bool SkCanvas::quickReject(const SkPath& path) const {
1729 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730}
1731
Mike Klein83c8dd92017-11-28 17:08:45 -05001732SkRect SkCanvas::getLocalClipBounds() const {
1733 SkIRect ibounds = this->getDeviceClipBounds();
Mike Reed918e1442017-01-23 11:39:45 -05001734 if (ibounds.isEmpty()) {
Mike Reed42e8c532017-01-23 14:09:13 -05001735 return SkRect::MakeEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 }
1737
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001738 SkMatrix inverse;
1739 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001740 if (!fMCRec->fMatrix.invert(&inverse)) {
Mike Reed42e8c532017-01-23 14:09:13 -05001741 return SkRect::MakeEmpty();
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001742 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743
Mike Reed42e8c532017-01-23 14:09:13 -05001744 SkRect bounds;
Mike Reed42e8c532017-01-23 14:09:13 -05001745 // adjust it outwards in case we are antialiasing
Mike Reedb57b9312018-04-23 12:12:54 -04001746 const int margin = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001747
Mike Reedb57b9312018-04-23 12:12:54 -04001748 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
Mike Reed42e8c532017-01-23 14:09:13 -05001749 inverse.mapRect(&bounds, r);
1750 return bounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751}
1752
Mike Klein83c8dd92017-11-28 17:08:45 -05001753SkIRect SkCanvas::getDeviceClipBounds() const {
Mike Reeda1361362017-03-07 09:37:29 -05001754 return fMCRec->fRasterClip.getBounds();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001755}
1756
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001758 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759}
1760
Brian Osman11052242016-10-27 14:47:55 -04001761GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001762 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001763 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001764}
1765
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001766GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001767 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001768 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001769}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001770
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001771void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1772 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04001773 TRACE_EVENT0("skia", TRACE_FUNC);
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001774 if (outer.isEmpty()) {
1775 return;
1776 }
1777 if (inner.isEmpty()) {
1778 this->drawRRect(outer, paint);
1779 return;
1780 }
1781
1782 // We don't have this method (yet), but technically this is what we should
Cary Clarke0b72872017-04-12 12:03:15 -04001783 // be able to return ...
1784 // if (!outer.contains(inner))) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001785 //
1786 // For now at least check for containment of bounds
Cary Clarke0b72872017-04-12 12:03:15 -04001787 if (!outer.getBounds().contains(inner.getBounds())) {
1788 return;
1789 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001790
1791 this->onDrawDRRect(outer, inner, paint);
1792}
1793
reed41af9662015-01-05 07:49:08 -08001794void SkCanvas::drawPaint(const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001795 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001796 this->onDrawPaint(paint);
1797}
1798
1799void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001800 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001801 // To avoid redundant logic in our culling code and various backends, we always sort rects
1802 // before passing them along.
1803 this->onDrawRect(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001804}
1805
Mike Reedd5674082019-04-19 15:00:47 -04001806void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1807 TRACE_EVENT0("skia", TRACE_FUNC);
1808 this->onDrawBehind(paint);
1809}
1810
msarettdca352e2016-08-26 06:37:45 -07001811void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001812 TRACE_EVENT0("skia", TRACE_FUNC);
msarettdca352e2016-08-26 06:37:45 -07001813 if (region.isEmpty()) {
1814 return;
1815 }
1816
1817 if (region.isRect()) {
1818 return this->drawIRect(region.getBounds(), paint);
1819 }
1820
1821 this->onDrawRegion(region, paint);
1822}
1823
reed41af9662015-01-05 07:49:08 -08001824void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001825 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman6e3ce402017-05-17 15:10:18 -04001826 // To avoid redundant logic in our culling code and various backends, we always sort rects
1827 // before passing them along.
1828 this->onDrawOval(r.makeSorted(), paint);
reed41af9662015-01-05 07:49:08 -08001829}
1830
1831void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001832 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001833 this->onDrawRRect(rrect, paint);
1834}
1835
1836void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001837 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001838 this->onDrawPoints(mode, count, pts, paint);
1839}
1840
Mike Reede88a1cb2017-03-17 09:50:46 -04001841void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1842 const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001843 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Salomon199fb872017-02-06 09:41:10 -05001844 RETURN_ON_NULL(vertices);
Brian Salomoncccafe82018-04-28 16:13:08 -04001845 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1846 SkASSERT(vertices->mode() != SkVertices::kTriangleFan_VertexMode);
Ruiqi Maof5101492018-06-29 14:32:21 -04001847 this->onDrawVerticesObject(vertices.get(), nullptr, 0, mode, paint);
Mike Reede88a1cb2017-03-17 09:50:46 -04001848}
1849
1850void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001851 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reede88a1cb2017-03-17 09:50:46 -04001852 RETURN_ON_NULL(vertices);
Ruiqi Maof5101492018-06-29 14:32:21 -04001853 this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
1854}
1855
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001856void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
1857 int boneCount, SkBlendMode mode, const SkPaint& paint) {
Ruiqi Maof5101492018-06-29 14:32:21 -04001858 TRACE_EVENT0("skia", TRACE_FUNC);
1859 RETURN_ON_NULL(vertices);
Ruiqi Maob609e6d2018-07-17 10:19:38 -04001860 SkASSERT(boneCount <= 80);
Ruiqi Maof5101492018-06-29 14:32:21 -04001861 this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
1862}
1863
Ruiqi Maoc97a3392018-08-15 10:44:19 -04001864void SkCanvas::drawVertices(const 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, bones, boneCount, mode, paint);
reed41af9662015-01-05 07:49:08 -08001870}
1871
1872void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001873 TRACE_EVENT0("skia", TRACE_FUNC);
reed41af9662015-01-05 07:49:08 -08001874 this->onDrawPath(path, paint);
1875}
1876
reeda85d4d02015-05-06 12:56:48 -07001877void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001878 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001879 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001880 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001881}
1882
Mike Reedc4e31092018-01-30 11:15:27 -05001883// Returns true if the rect can be "filled" : non-empty and finite
1884static bool fillable(const SkRect& r) {
1885 SkScalar w = r.width();
1886 SkScalar h = r.height();
1887 return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
1888}
1889
reede47829b2015-08-06 10:02:53 -07001890void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1891 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001892 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001893 RETURN_ON_NULL(image);
Mike Reedc4e31092018-01-30 11:15:27 -05001894 if (!fillable(dst) || !fillable(src)) {
reede47829b2015-08-06 10:02:53 -07001895 return;
1896 }
1897 this->onDrawImageRect(image, &src, dst, paint, constraint);
1898}
reed41af9662015-01-05 07:49:08 -08001899
reed84984ef2015-07-17 07:09:43 -07001900void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1901 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001902 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001903 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001904}
1905
Brian Salomonf08002c2018-10-26 16:15:46 -04001906void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001907 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001908 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
Brian Salomonf08002c2018-10-26 16:15:46 -04001909 kFast_SrcRectConstraint);
reede47829b2015-08-06 10:02:53 -07001910}
reede47829b2015-08-06 10:02:53 -07001911
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001912namespace {
Brian Salomon969be1c2018-05-21 14:37:49 -04001913class LatticePaint : SkNoncopyable {
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001914public:
Brian Salomon969be1c2018-05-21 14:37:49 -04001915 LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) {
1916 if (!origPaint) {
1917 return;
1918 }
1919 if (origPaint->getFilterQuality() > kLow_SkFilterQuality) {
1920 fPaint.writable()->setFilterQuality(kLow_SkFilterQuality);
1921 }
1922 if (origPaint->getMaskFilter()) {
1923 fPaint.writable()->setMaskFilter(nullptr);
1924 }
1925 if (origPaint->isAntiAlias()) {
1926 fPaint.writable()->setAntiAlias(false);
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001927 }
1928 }
1929
1930 const SkPaint* get() const {
1931 return fPaint;
1932 }
1933
1934private:
Brian Salomon969be1c2018-05-21 14:37:49 -04001935 SkTCopyOnFirstWrite<SkPaint> fPaint;
Leon Scroggins III57e1f022018-04-20 14:53:00 -04001936};
1937} // namespace
1938
reed4c21dc52015-06-25 12:32:03 -07001939void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1940 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001941 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08001942 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001943 if (dst.isEmpty()) {
1944 return;
1945 }
msarett552bca92016-08-03 06:53:26 -07001946 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001947 LatticePaint latticePaint(paint);
1948 this->onDrawImageNine(image, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07001949 } else {
reede47829b2015-08-06 10:02:53 -07001950 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001951 }
reed4c21dc52015-06-25 12:32:03 -07001952}
1953
msarett16882062016-08-16 09:31:08 -07001954void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1955 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001956 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07001957 RETURN_ON_NULL(image);
1958 if (dst.isEmpty()) {
1959 return;
1960 }
msarett71df2d72016-09-30 12:41:42 -07001961
1962 SkIRect bounds;
1963 Lattice latticePlusBounds = lattice;
1964 if (!latticePlusBounds.fBounds) {
1965 bounds = SkIRect::MakeWH(image->width(), image->height());
1966 latticePlusBounds.fBounds = &bounds;
1967 }
1968
1969 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04001970 LatticePaint latticePaint(paint);
1971 this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get());
msarett16882062016-08-16 09:31:08 -07001972 } else {
1973 this->drawImageRect(image, dst, paint);
1974 }
1975}
1976
reed41af9662015-01-05 07:49:08 -08001977void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001978 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07001979 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001980 return;
1981 }
reed41af9662015-01-05 07:49:08 -08001982 this->onDrawBitmap(bitmap, dx, dy, paint);
1983}
1984
reede47829b2015-08-06 10:02:53 -07001985void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001986 const SkPaint* paint, SrcRectConstraint constraint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04001987 TRACE_EVENT0("skia", TRACE_FUNC);
reede47829b2015-08-06 10:02:53 -07001988 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001989 return;
1990 }
reede47829b2015-08-06 10:02:53 -07001991 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001992}
1993
reed84984ef2015-07-17 07:09:43 -07001994void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1995 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001996 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001997}
1998
reede47829b2015-08-06 10:02:53 -07001999void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2000 SrcRectConstraint constraint) {
2001 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2002 constraint);
2003}
reede47829b2015-08-06 10:02:53 -07002004
reed41af9662015-01-05 07:49:08 -08002005void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2006 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002007 TRACE_EVENT0("skia", TRACE_FUNC);
reed4c21dc52015-06-25 12:32:03 -07002008 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002009 return;
2010 }
msarett552bca92016-08-03 06:53:26 -07002011 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002012 LatticePaint latticePaint(paint);
2013 this->onDrawBitmapNine(bitmap, center, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002014 } else {
reeda5517e22015-07-14 10:54:12 -07002015 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002016 }
reed41af9662015-01-05 07:49:08 -08002017}
2018
msarettc573a402016-08-02 08:05:56 -07002019void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2020 const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002021 TRACE_EVENT0("skia", TRACE_FUNC);
msarett16882062016-08-16 09:31:08 -07002022 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002023 return;
2024 }
msarett71df2d72016-09-30 12:41:42 -07002025
2026 SkIRect bounds;
2027 Lattice latticePlusBounds = lattice;
2028 if (!latticePlusBounds.fBounds) {
2029 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2030 latticePlusBounds.fBounds = &bounds;
2031 }
2032
2033 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
Brian Salomon969be1c2018-05-21 14:37:49 -04002034 LatticePaint latticePaint(paint);
2035 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, latticePaint.get());
msarett552bca92016-08-03 06:53:26 -07002036 } else {
msarett16882062016-08-16 09:31:08 -07002037 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002038 }
msarettc573a402016-08-02 08:05:56 -07002039}
2040
reed71c3c762015-06-24 10:29:17 -07002041void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002042 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002043 const SkRect* cull, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002044 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002045 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002046 if (count <= 0) {
2047 return;
2048 }
Joe Gregorioc4859072017-01-20 14:21:27 +00002049 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002050 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002051 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002052}
2053
reedf70b5312016-03-04 16:36:20 -08002054void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002055 TRACE_EVENT0("skia", TRACE_FUNC);
reedf70b5312016-03-04 16:36:20 -08002056 if (key) {
2057 this->onDrawAnnotation(rect, key, value);
2058 }
2059}
2060
reede47829b2015-08-06 10:02:53 -07002061void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2062 const SkPaint* paint, SrcRectConstraint constraint) {
2063 if (src) {
2064 this->drawImageRect(image, *src, dst, paint, constraint);
2065 } else {
2066 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2067 dst, paint, constraint);
2068 }
2069}
2070void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2071 const SkPaint* paint, SrcRectConstraint constraint) {
2072 if (src) {
2073 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2074 } else {
2075 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2076 dst, paint, constraint);
2077 }
2078}
2079
Mike Reed4204da22017-05-17 08:53:36 -04002080void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002081 TRACE_EVENT0("skia", TRACE_FUNC);
Mike Reed4204da22017-05-17 08:53:36 -04002082 this->onDrawShadowRec(path, rec);
2083}
2084
2085void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
2086 SkPaint paint;
2087 const SkRect& pathBounds = path.getBounds();
2088
Mike Reed38992392019-07-30 10:48:15 -04002089 DRAW_BEGIN(paint, &pathBounds)
Mike Reed4204da22017-05-17 08:53:36 -04002090 while (iter.next()) {
2091 iter.fDevice->drawShadow(path, rec);
2092 }
Mike Reed38992392019-07-30 10:48:15 -04002093 DRAW_END
Mike Reed4204da22017-05-17 08:53:36 -04002094}
2095
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002096void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
Michael Ludwiga595f862019-08-27 15:25:49 -04002097 QuadAAFlags aaFlags, const SkColor4f& color,
2098 SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002099 TRACE_EVENT0("skia", TRACE_FUNC);
2100 // Make sure the rect is sorted before passing it along
2101 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
2102}
2103
2104void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
2105 const SkPoint dstClips[],
2106 const SkMatrix preViewMatrices[],
2107 const SkPaint* paint,
2108 SrcRectConstraint constraint) {
2109 TRACE_EVENT0("skia", TRACE_FUNC);
2110 this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint);
2111}
2112
reed@android.com8a1c16f2008-12-17 15:59:43 +00002113//////////////////////////////////////////////////////////////////////////////
2114// These are the virtual drawing methods
2115//////////////////////////////////////////////////////////////////////////////
2116
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002117void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002118 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002119 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2120 }
2121}
2122
reed41af9662015-01-05 07:49:08 -08002123void SkCanvas::onDrawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002124 this->internalDrawPaint(paint);
2125}
2126
2127void SkCanvas::internalDrawPaint(const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002128 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002129
2130 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002131 iter.fDevice->drawPaint(draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 }
2133
Mike Reed38992392019-07-30 10:48:15 -04002134 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135}
2136
reed41af9662015-01-05 07:49:08 -08002137void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2138 const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 if ((long)count <= 0) {
2140 return;
2141 }
2142
Mike Reed822128b2017-02-28 16:41:03 -05002143 SkRect r;
halcanary96fcdcc2015-08-27 07:41:13 -07002144 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002145 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002146 // special-case 2 points (common for drawing a single line)
2147 if (2 == count) {
2148 r.set(pts[0], pts[1]);
2149 } else {
Mike Reed92b33352019-08-24 19:39:13 -04002150 r.setBounds(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002151 }
Jim Van Verth5d32b832018-02-21 11:14:32 -05002152 if (!r.isFinite()) {
2153 return;
2154 }
Mike Reed822128b2017-02-28 16:41:03 -05002155 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002156 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2157 return;
2158 }
2159 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002160 }
reed@google.coma584aed2012-05-16 14:06:02 +00002161
halcanary96fcdcc2015-08-27 07:41:13 -07002162 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163
Mike Reed38992392019-07-30 10:48:15 -04002164 DRAW_BEGIN(paint, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002165
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002167 iter.fDevice->drawPoints(mode, count, pts, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168 }
reed@google.com4b226022011-01-11 18:32:13 +00002169
Mike Reed38992392019-07-30 10:48:15 -04002170 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171}
2172
reed4a167172016-08-18 17:15:25 -07002173static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
Mike Reed9dc0b9e2019-07-29 17:52:48 -04002174 return paint.getImageFilter() != nullptr;
reed4a167172016-08-18 17:15:25 -07002175}
2176
reed41af9662015-01-05 07:49:08 -08002177void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002178 SkASSERT(r.isSorted());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002180 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002181 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002182 return;
2183 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 }
reed@google.com4b226022011-01-11 18:32:13 +00002185
reed4a167172016-08-18 17:15:25 -07002186 if (needs_autodrawlooper(this, paint)) {
Mike Reed38992392019-07-30 10:48:15 -04002187 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188
reed4a167172016-08-18 17:15:25 -07002189 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002190 iter.fDevice->drawRect(r, draw.paint());
reed4a167172016-08-18 17:15:25 -07002191 }
2192
Mike Reed38992392019-07-30 10:48:15 -04002193 DRAW_END
Mike Reed49f8da02018-08-27 10:48:52 -04002194 } else if (!paint.nothingToDraw()) {
Mike Reed822128b2017-02-28 16:41:03 -05002195 this->predrawNotify(&r, &paint, false);
reed4a167172016-08-18 17:15:25 -07002196 SkDrawIter iter(this);
2197 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002198 iter.fDevice->drawRect(r, paint);
reed4a167172016-08-18 17:15:25 -07002199 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201}
2202
msarett44df6512016-08-25 13:54:30 -07002203void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
msarett44df6512016-08-25 13:54:30 -07002204 SkRect regionRect = SkRect::Make(region.getBounds());
msarett44df6512016-08-25 13:54:30 -07002205 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002206 SkRect storage;
msarett44df6512016-08-25 13:54:30 -07002207 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2208 return;
2209 }
msarett44df6512016-08-25 13:54:30 -07002210 }
2211
Mike Reed38992392019-07-30 10:48:15 -04002212 DRAW_BEGIN(paint, &regionRect)
msarett44df6512016-08-25 13:54:30 -07002213
2214 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002215 iter.fDevice->drawRegion(region, draw.paint());
msarett44df6512016-08-25 13:54:30 -07002216 }
2217
Mike Reed38992392019-07-30 10:48:15 -04002218 DRAW_END
msarett44df6512016-08-25 13:54:30 -07002219}
2220
Mike Reedd5674082019-04-19 15:00:47 -04002221void SkCanvas::onDrawBehind(const SkPaint& paint) {
2222 SkIRect bounds;
2223 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2224 for (;;) {
2225 const MCRec* rec = (const MCRec*)iter.prev();
2226 if (!rec) {
2227 return; // no backimages, so nothing to draw
2228 }
2229 if (rec->fBackImage) {
2230 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2231 rec->fBackImage->fImage->width(),
2232 rec->fBackImage->fImage->height());
2233 break;
2234 }
2235 }
2236
Mike Reed38992392019-07-30 10:48:15 -04002237 DRAW_BEGIN(paint, nullptr)
Mike Reedd5674082019-04-19 15:00:47 -04002238
2239 while (iter.next()) {
2240 SkBaseDevice* dev = iter.fDevice;
2241
Mike Reedd5674082019-04-19 15:00:47 -04002242 dev->save();
2243 // We use clipRegion because it is already defined to operate in dev-space
2244 // (i.e. ignores the ctm). However, it is going to first translate by -origin,
2245 // but we don't want that, so we undo that before calling in.
Brian Salomon9bd947d2019-10-03 14:57:07 -04002246 SkRegion rgn(bounds.makeOffset(dev->fOrigin));
Mike Reedd5674082019-04-19 15:00:47 -04002247 dev->clipRegion(rgn, SkClipOp::kIntersect);
Mike Reed38992392019-07-30 10:48:15 -04002248 dev->drawPaint(draw.paint());
Mike Reed9adc82c2019-04-23 10:28:13 -04002249 dev->restore(fMCRec->fMatrix);
Mike Reedd5674082019-04-19 15:00:47 -04002250 }
2251
Mike Reed38992392019-07-30 10:48:15 -04002252 DRAW_END
Mike Reedd5674082019-04-19 15:00:47 -04002253}
2254
reed41af9662015-01-05 07:49:08 -08002255void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002256 SkASSERT(oval.isSorted());
reed@google.com4ed0fb72012-12-12 20:48:18 +00002257 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002258 SkRect storage;
Brian Osman6e3ce402017-05-17 15:10:18 -04002259 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002260 return;
2261 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002262 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002263
Mike Reed38992392019-07-30 10:48:15 -04002264 DRAW_BEGIN(paint, &oval)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002265
2266 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002267 iter.fDevice->drawOval(oval, draw.paint());
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002268 }
2269
Mike Reed38992392019-07-30 10:48:15 -04002270 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002271}
2272
bsalomonac3aa242016-08-19 11:25:19 -07002273void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2274 SkScalar sweepAngle, bool useCenter,
2275 const SkPaint& paint) {
Brian Osman6e3ce402017-05-17 15:10:18 -04002276 SkASSERT(oval.isSorted());
bsalomonac3aa242016-08-19 11:25:19 -07002277 if (paint.canComputeFastBounds()) {
2278 SkRect storage;
2279 // Note we're using the entire oval as the bounds.
Brian Osman6e3ce402017-05-17 15:10:18 -04002280 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
bsalomonac3aa242016-08-19 11:25:19 -07002281 return;
2282 }
bsalomonac3aa242016-08-19 11:25:19 -07002283 }
2284
Mike Reed38992392019-07-30 10:48:15 -04002285 DRAW_BEGIN(paint, &oval)
bsalomonac3aa242016-08-19 11:25:19 -07002286
2287 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002288 iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint());
bsalomonac3aa242016-08-19 11:25:19 -07002289 }
2290
Mike Reed38992392019-07-30 10:48:15 -04002291 DRAW_END
bsalomonac3aa242016-08-19 11:25:19 -07002292}
2293
reed41af9662015-01-05 07:49:08 -08002294void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002295 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002296 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002297 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2298 return;
2299 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002300 }
2301
2302 if (rrect.isRect()) {
2303 // call the non-virtual version
2304 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305 return;
2306 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002307 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002308 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2309 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002310 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002311
Mike Reed38992392019-07-30 10:48:15 -04002312 DRAW_BEGIN(paint, &rrect.getBounds())
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002313
2314 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002315 iter.fDevice->drawRRect(rrect, draw.paint());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002316 }
2317
Mike Reed38992392019-07-30 10:48:15 -04002318 DRAW_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002319}
2320
Mike Reed822128b2017-02-28 16:41:03 -05002321void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002322 if (paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002323 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002324 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2325 return;
2326 }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002327 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002328
Mike Reed38992392019-07-30 10:48:15 -04002329 DRAW_BEGIN(paint, &outer.getBounds())
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002330
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002331 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002332 iter.fDevice->drawDRRect(outer, inner, draw.paint());
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002333 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002334
Mike Reed38992392019-07-30 10:48:15 -04002335 DRAW_END
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002336}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002337
reed41af9662015-01-05 07:49:08 -08002338void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00002339 if (!path.isFinite()) {
2340 return;
2341 }
2342
Mike Reed822128b2017-02-28 16:41:03 -05002343 const SkRect& pathBounds = path.getBounds();
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002344 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
Mike Reed822128b2017-02-28 16:41:03 -05002345 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002346 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2347 return;
2348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002350
Mike Reed822128b2017-02-28 16:41:03 -05002351 if (pathBounds.width() <= 0 && pathBounds.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002352 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002353 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002354 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002355 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357
Mike Reed38992392019-07-30 10:48:15 -04002358 DRAW_BEGIN(paint, &pathBounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359
2360 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002361 iter.fDevice->drawPath(path, draw.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 }
2363
Mike Reed38992392019-07-30 10:48:15 -04002364 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365}
2366
reed262a71b2015-12-05 13:07:27 -08002367bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002368 if (!paint.getImageFilter()) {
2369 return false;
2370 }
2371
2372 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002373 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002374 return false;
2375 }
2376
2377 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2378 // Once we can filter and the filter will return a result larger than itself, we should be
2379 // able to remove this constraint.
2380 // skbug.com/4526
2381 //
2382 SkPoint pt;
2383 ctm.mapXY(x, y, &pt);
2384 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2385 return ir.contains(fMCRec->fRasterClip.getBounds());
2386}
2387
Mike Reedf441cfc2018-04-11 14:50:16 -04002388// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
2389// given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original
2390// null.
2391static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) {
2392 if (paintParam) {
2393 *real = *paintParam;
2394 real->setStyle(SkPaint::kFill_Style);
2395 real->setPathEffect(nullptr);
2396 paintParam = real;
2397 }
2398 return paintParam;
2399}
2400
reeda85d4d02015-05-06 12:56:48 -07002401void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002402 SkPaint realPaint;
2403 paint = init_image_paint(&realPaint, paint);
2404
reeda85d4d02015-05-06 12:56:48 -07002405 SkRect bounds = SkRect::MakeXYWH(x, y,
2406 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002407 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002408 SkRect tmp = bounds;
2409 if (paint) {
2410 paint->computeFastBounds(tmp, &tmp);
2411 }
2412 if (this->quickReject(tmp)) {
2413 return;
2414 }
reeda85d4d02015-05-06 12:56:48 -07002415 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002416 // At this point we need a real paint object. If the caller passed null, then we should
2417 // use realPaint (in its default state). If the caller did pass a paint, then we have copied
2418 // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use.
2419 paint = &realPaint;
reed262a71b2015-12-05 13:07:27 -08002420
reeda2217ef2016-07-20 06:04:34 -07002421 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002422 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2423 *paint);
2424 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002425 special = this->getDevice()->makeSpecial(image);
2426 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002427 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002428 }
2429 }
2430
Mike Reed38992392019-07-30 10:48:15 -04002431 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed262a71b2015-12-05 13:07:27 -08002432
reeda85d4d02015-05-06 12:56:48 -07002433 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002434 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002435 if (special) {
2436 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002437 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002438 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002439 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002440 SkScalarRoundToInt(pt.fY), pnt,
2441 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002442 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002443 iter.fDevice->drawImageRect(
2444 image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt,
2445 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002446 }
reeda85d4d02015-05-06 12:56:48 -07002447 }
halcanary9d524f22016-03-29 09:03:52 -07002448
Mike Reed38992392019-07-30 10:48:15 -04002449 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002450}
2451
reed41af9662015-01-05 07:49:08 -08002452void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002453 const SkPaint* paint, SrcRectConstraint constraint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002454 SkPaint realPaint;
2455 paint = init_image_paint(&realPaint, paint);
2456
halcanary96fcdcc2015-08-27 07:41:13 -07002457 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002458 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002459 if (paint) {
2460 paint->computeFastBounds(dst, &storage);
2461 }
2462 if (this->quickReject(storage)) {
2463 return;
2464 }
reeda85d4d02015-05-06 12:56:48 -07002465 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002466 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002467
Mike Reed38992392019-07-30 10:48:15 -04002468 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002469
reeda85d4d02015-05-06 12:56:48 -07002470 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002471 iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002472 }
halcanary9d524f22016-03-29 09:03:52 -07002473
Mike Reed38992392019-07-30 10:48:15 -04002474 DRAW_END
piotaixrb5fae932014-09-24 13:03:30 -07002475}
2476
reed41af9662015-01-05 07:49:08 -08002477void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478 SkDEBUGCODE(bitmap.validate();)
2479
reed33366972015-10-08 09:22:02 -07002480 if (bitmap.drawsNothing()) {
2481 return;
2482 }
2483
Mike Reedf441cfc2018-04-11 14:50:16 -04002484 SkPaint realPaint;
2485 init_image_paint(&realPaint, paint);
2486 paint = &realPaint;
reed33366972015-10-08 09:22:02 -07002487
Mike Reed822128b2017-02-28 16:41:03 -05002488 SkRect bounds;
2489 bitmap.getBounds(&bounds);
2490 bounds.offset(x, y);
2491 bool canFastBounds = paint->canComputeFastBounds();
2492 if (canFastBounds) {
2493 SkRect storage;
2494 if (this->quickReject(paint->computeFastBounds(bounds, &storage))) {
senorblanco87e066e2015-10-28 11:23:36 -07002495 return;
2496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497 }
reed@google.com4b226022011-01-11 18:32:13 +00002498
reeda2217ef2016-07-20 06:04:34 -07002499 sk_sp<SkSpecialImage> special;
Mike Reed822128b2017-02-28 16:41:03 -05002500 bool drawAsSprite = canFastBounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2501 bitmap.height(), *paint);
reed129ed1c2016-02-22 06:42:31 -08002502 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002503 special = this->getDevice()->makeSpecial(bitmap);
2504 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002505 drawAsSprite = false;
2506 }
2507 }
2508
Mike Reed38992392019-07-30 10:48:15 -04002509 DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
reed33366972015-10-08 09:22:02 -07002510
2511 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002512 const SkPaint& pnt = draw.paint();
reeda2217ef2016-07-20 06:04:34 -07002513 if (special) {
reed262a71b2015-12-05 13:07:27 -08002514 SkPoint pt;
Michael Ludwigc89d1b52019-10-18 11:32:56 -04002515 iter.fDevice->localToDevice().mapXY(x, y, &pt);
Mike Reeda1361362017-03-07 09:37:29 -05002516 iter.fDevice->drawSpecial(special.get(),
reeda2217ef2016-07-20 06:04:34 -07002517 SkScalarRoundToInt(pt.fX),
Florin Malita53f77bd2017-04-28 13:48:37 -04002518 SkScalarRoundToInt(pt.fY), pnt,
2519 nullptr, SkMatrix::I());
reed262a71b2015-12-05 13:07:27 -08002520 } else {
Michael Ludwigb7d64b92019-02-11 11:09:15 -05002521 SkRect fullImage = SkRect::MakeWH(bitmap.width(), bitmap.height());
2522 iter.fDevice->drawBitmapRect(bitmap, &fullImage, fullImage.makeOffset(x, y), pnt,
2523 kStrict_SrcRectConstraint);
reed262a71b2015-12-05 13:07:27 -08002524 }
reed33366972015-10-08 09:22:02 -07002525 }
msarettfbfa2582016-08-12 08:29:08 -07002526
Mike Reed38992392019-07-30 10:48:15 -04002527 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528}
2529
reed@google.com9987ec32011-09-07 11:57:52 +00002530// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002531void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002532 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002533 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002534 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535 return;
2536 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002537
halcanary96fcdcc2015-08-27 07:41:13 -07002538 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002539 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002540 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2541 return;
2542 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543 }
reed@google.com3d608122011-11-21 15:16:16 +00002544
reed@google.com33535f32012-09-25 15:37:50 +00002545 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002546 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002547 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002549
Mike Reed38992392019-07-30 10:48:15 -04002550 DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002551
reed@google.com33535f32012-09-25 15:37:50 +00002552 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002553 iter.fDevice->drawBitmapRect(bitmap, src, dst, draw.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002554 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002555
Mike Reed38992392019-07-30 10:48:15 -04002556 DRAW_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002557}
2558
reed41af9662015-01-05 07:49:08 -08002559void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002560 const SkPaint* paint, SrcRectConstraint constraint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002561 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002562 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002563}
2564
reed4c21dc52015-06-25 12:32:03 -07002565void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2566 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002567 SkPaint realPaint;
2568 paint = init_image_paint(&realPaint, paint);
2569
halcanary96fcdcc2015-08-27 07:41:13 -07002570 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002571 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002572 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2573 return;
2574 }
reed@google.com3d608122011-11-21 15:16:16 +00002575 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002576 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002577
Mike Reed38992392019-07-30 10:48:15 -04002578 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002579
reed4c21dc52015-06-25 12:32:03 -07002580 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002581 iter.fDevice->drawImageNine(image, center, dst, draw.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002582 }
halcanary9d524f22016-03-29 09:03:52 -07002583
Mike Reed38992392019-07-30 10:48:15 -04002584 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002585}
2586
reed41af9662015-01-05 07:49:08 -08002587void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2588 const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00002589 SkDEBUGCODE(bitmap.validate();)
Mike Reedf441cfc2018-04-11 14:50:16 -04002590 SkPaint realPaint;
2591 paint = init_image_paint(&realPaint, paint);
reed@google.com9987ec32011-09-07 11:57:52 +00002592
halcanary96fcdcc2015-08-27 07:41:13 -07002593 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002594 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002595 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2596 return;
2597 }
reed4c21dc52015-06-25 12:32:03 -07002598 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002599 paint = &realPaint;
halcanary9d524f22016-03-29 09:03:52 -07002600
Mike Reed38992392019-07-30 10:48:15 -04002601 DRAW_BEGIN(*paint, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002602
reed4c21dc52015-06-25 12:32:03 -07002603 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002604 iter.fDevice->drawBitmapNine(bitmap, center, dst, draw.paint());
reed4c21dc52015-06-25 12:32:03 -07002605 }
halcanary9d524f22016-03-29 09:03:52 -07002606
Mike Reed38992392019-07-30 10:48:15 -04002607 DRAW_END
reed@google.com9987ec32011-09-07 11:57:52 +00002608}
2609
msarett16882062016-08-16 09:31:08 -07002610void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2611 const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002612 SkPaint realPaint;
2613 paint = init_image_paint(&realPaint, paint);
2614
msarett16882062016-08-16 09:31:08 -07002615 if (nullptr == paint || paint->canComputeFastBounds()) {
2616 SkRect storage;
2617 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2618 return;
2619 }
2620 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002621 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002622
Mike Reed38992392019-07-30 10:48:15 -04002623 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002624
2625 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002626 iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002627 }
2628
Mike Reed38992392019-07-30 10:48:15 -04002629 DRAW_END
msarett16882062016-08-16 09:31:08 -07002630}
2631
2632void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2633 const SkRect& dst, const SkPaint* paint) {
Mike Reedf441cfc2018-04-11 14:50:16 -04002634 SkPaint realPaint;
2635 paint = init_image_paint(&realPaint, paint);
2636
msarett16882062016-08-16 09:31:08 -07002637 if (nullptr == paint || paint->canComputeFastBounds()) {
2638 SkRect storage;
2639 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2640 return;
2641 }
2642 }
Mike Reedf441cfc2018-04-11 14:50:16 -04002643 paint = &realPaint;
msarett16882062016-08-16 09:31:08 -07002644
Mike Reed38992392019-07-30 10:48:15 -04002645 DRAW_BEGIN(*paint, &dst)
msarett16882062016-08-16 09:31:08 -07002646
2647 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002648 iter.fDevice->drawBitmapLattice(bitmap, lattice, dst, draw.paint());
msarett16882062016-08-16 09:31:08 -07002649 }
2650
Mike Reed38992392019-07-30 10:48:15 -04002651 DRAW_END
msarett16882062016-08-16 09:31:08 -07002652}
2653
fmalita00d5c2c2014-08-21 08:53:26 -07002654void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2655 const SkPaint& paint) {
fmalita85d5eb92015-03-04 11:20:12 -08002656 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002657 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002658 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002659 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002660 SkRect tmp;
2661 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2662 return;
2663 }
2664 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002665 }
2666
fmalita024f9962015-03-03 19:08:17 -08002667 // We cannot filter in the looper as we normally do, because the paint is
2668 // incomplete at this point (text-related attributes are embedded within blob run paints).
Mike Reed38992392019-07-30 10:48:15 -04002669 DRAW_BEGIN(paint, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002670
fmalitaaa1b9122014-08-28 14:32:24 -07002671 while (iter.next()) {
Mike Reed38992392019-07-30 10:48:15 -04002672 fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice);
fmalita00d5c2c2014-08-21 08:53:26 -07002673 }
2674
Mike Reed38992392019-07-30 10:48:15 -04002675 DRAW_END
fmalita00d5c2c2014-08-21 08:53:26 -07002676}
2677
Mike Reed358fcad2018-11-23 15:27:51 -05002678// These call the (virtual) onDraw... method
Mike Reed7d1eb332018-12-04 17:35:56 -05002679void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
Mike Reed358fcad2018-11-23 15:27:51 -05002680 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2681 TRACE_EVENT0("skia", TRACE_FUNC);
2682 if (byteLength) {
2683 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
Mike Reed704a3422018-12-06 15:44:14 -05002684 this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint);
Mike Reed358fcad2018-11-23 15:27:51 -05002685 }
2686}
Mike Reed4f81bb72019-01-23 09:23:00 -05002687
fmalita00d5c2c2014-08-21 08:53:26 -07002688void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2689 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002690 TRACE_EVENT0("skia", TRACE_FUNC);
Brian Osman5e035ca2017-07-26 10:18:57 -04002691 RETURN_ON_NULL(blob);
Mike Reed74d6e112018-01-23 13:06:12 -05002692 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
reede3b38ce2016-01-08 09:18:44 -08002693 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002694}
reed@google.come0d9ce82014-04-23 04:00:17 +00002695
Ruiqi Maoc97a3392018-08-15 10:44:19 -04002696void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
Ruiqi Maof5101492018-06-29 14:32:21 -04002697 int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Mike Reed38992392019-07-30 10:48:15 -04002698 DRAW_BEGIN(paint, nullptr)
Brian Salomon199fb872017-02-06 09:41:10 -05002699
2700 while (iter.next()) {
2701 // In the common case of one iteration we could std::move vertices here.
Mike Reed38992392019-07-30 10:48:15 -04002702 iter.fDevice->drawVertices(vertices, bones, boneCount, bmode, draw.paint());
Brian Salomon199fb872017-02-06 09:41:10 -05002703 }
2704
Mike Reed38992392019-07-30 10:48:15 -04002705 DRAW_END
Brian Salomon199fb872017-02-06 09:41:10 -05002706}
2707
dandovb3c9d1c2014-08-12 08:34:29 -07002708void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002709 const SkPoint texCoords[4], SkBlendMode bmode,
2710 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002711 TRACE_EVENT0("skia", TRACE_FUNC);
halcanary96fcdcc2015-08-27 07:41:13 -07002712 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002713 return;
2714 }
mtklein6cfa73a2014-08-13 13:33:49 -07002715
Mike Reedfaba3712016-11-03 14:45:31 -04002716 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002717}
2718
2719void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002720 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002721 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002722 // Since a patch is always within the convex hull of the control points, we discard it when its
2723 // bounding rectangle is completely outside the current clip.
2724 SkRect bounds;
Mike Reed92b33352019-08-24 19:39:13 -04002725 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002726 if (this->quickReject(bounds)) {
2727 return;
2728 }
mtklein6cfa73a2014-08-13 13:33:49 -07002729
Mike Reed38992392019-07-30 10:48:15 -04002730 DRAW_BEGIN(paint, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002731
dandovecfff212014-08-04 10:02:00 -07002732 while (iter.next()) {
Brian Osmane0a99622018-07-09 16:12:27 -04002733 iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002734 }
mtklein6cfa73a2014-08-13 13:33:49 -07002735
Mike Reed38992392019-07-30 10:48:15 -04002736 DRAW_END
dandovecfff212014-08-04 10:02:00 -07002737}
2738
reeda8db7282015-07-07 10:22:31 -07002739void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002740#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002741 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002742#endif
reede3b38ce2016-01-08 09:18:44 -08002743 RETURN_ON_NULL(dr);
2744 if (x || y) {
2745 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2746 this->onDrawDrawable(dr, &matrix);
2747 } else {
2748 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002749 }
2750}
2751
reeda8db7282015-07-07 10:22:31 -07002752void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002753#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
Brian Osman5e035ca2017-07-26 10:18:57 -04002754 TRACE_EVENT0("skia", TRACE_FUNC);
Derek Sollenbergeredf3dc02017-08-16 10:35:28 -04002755#endif
reede3b38ce2016-01-08 09:18:44 -08002756 RETURN_ON_NULL(dr);
2757 if (matrix && matrix->isIdentity()) {
2758 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002759 }
reede3b38ce2016-01-08 09:18:44 -08002760 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002761}
2762
2763void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002764 // drawable bounds are no longer reliable (e.g. android displaylist)
2765 // so don't use them for quick-reject
Greg Daniel9ed1a2c2018-10-18 12:43:25 -04002766 this->getDevice()->drawDrawable(dr, matrix, this);
reed6a070dc2014-11-11 19:36:09 -08002767}
2768
reed71c3c762015-06-24 10:29:17 -07002769void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002770 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002771 const SkRect* cull, const SkPaint* paint) {
2772 if (cull && this->quickReject(*cull)) {
2773 return;
2774 }
2775
2776 SkPaint pnt;
2777 if (paint) {
2778 pnt = *paint;
2779 }
halcanary9d524f22016-03-29 09:03:52 -07002780
Mike Reed38992392019-07-30 10:48:15 -04002781 DRAW_BEGIN(pnt, nullptr)
reed71c3c762015-06-24 10:29:17 -07002782 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002783 iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002784 }
Mike Reed38992392019-07-30 10:48:15 -04002785 DRAW_END
reed71c3c762015-06-24 10:29:17 -07002786}
2787
reedf70b5312016-03-04 16:36:20 -08002788void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2789 SkASSERT(key);
2790
2791 SkPaint paint;
Mike Reed38992392019-07-30 10:48:15 -04002792 DRAW_BEGIN(paint, nullptr)
reedf70b5312016-03-04 16:36:20 -08002793 while (iter.next()) {
Mike Reeda1361362017-03-07 09:37:29 -05002794 iter.fDevice->drawAnnotation(rect, key, value);
reedf70b5312016-03-04 16:36:20 -08002795 }
Mike Reed38992392019-07-30 10:48:15 -04002796 DRAW_END
reedf70b5312016-03-04 16:36:20 -08002797}
2798
Michael Ludwiga595f862019-08-27 15:25:49 -04002799void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2800 const SkColor4f& color, SkBlendMode mode) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002801 SkASSERT(r.isSorted());
2802
2803 // If this used a paint, it would be a filled color with blend mode, which does not
2804 // need to use an autodraw loop, so use SkDrawIter directly.
2805 if (this->quickReject(r)) {
2806 return;
2807 }
2808
Michael Ludwiga4b44882019-08-28 14:34:58 -04002809 this->predrawNotify(&r, nullptr, false);
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002810 SkDrawIter iter(this);
2811 while(iter.next()) {
2812 iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2813 }
2814}
2815
2816void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count,
2817 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2818 const SkPaint* paint, SrcRectConstraint constraint) {
Michael Ludwiga4b44882019-08-28 14:34:58 -04002819 if (count <= 0) {
2820 // Nothing to draw
2821 return;
2822 }
2823
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002824 SkPaint realPaint;
2825 init_image_paint(&realPaint, paint);
2826
Michael Ludwiga4b44882019-08-28 14:34:58 -04002827 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2828 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2829 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2830 // or we need it for the autolooper (since it greatly improves image filter perf).
2831 bool needsAutoLooper = needs_autodrawlooper(this, realPaint);
2832 bool setBoundsValid = count == 1 || needsAutoLooper;
2833 SkRect setBounds = imageSet[0].fDstRect;
2834 if (imageSet[0].fMatrixIndex >= 0) {
2835 // Account for the per-entry transform that is applied prior to the CTM when drawing
2836 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
Michael Ludwig977b50a2019-08-27 13:23:20 -04002837 }
Michael Ludwiga4b44882019-08-28 14:34:58 -04002838 if (needsAutoLooper) {
2839 for (int i = 1; i < count; ++i) {
2840 SkRect entryBounds = imageSet[i].fDstRect;
2841 if (imageSet[i].fMatrixIndex >= 0) {
2842 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2843 }
2844 setBounds.joinPossiblyEmptyRect(entryBounds);
2845 }
2846 }
2847
2848 // If we happen to have the draw bounds, though, might as well check quickReject().
2849 if (setBoundsValid && realPaint.canComputeFastBounds()) {
2850 SkRect tmp;
2851 if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) {
2852 return;
2853 }
2854 }
2855
2856 if (needsAutoLooper) {
2857 SkASSERT(setBoundsValid);
2858 DRAW_BEGIN(realPaint, &setBounds)
2859 while (iter.next()) {
2860 iter.fDevice->drawEdgeAAImageSet(
2861 imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint);
2862 }
2863 DRAW_END
2864 } else {
2865 this->predrawNotify();
2866 SkDrawIter iter(this);
2867 while(iter.next()) {
2868 iter.fDevice->drawEdgeAAImageSet(
2869 imageSet, count, dstClips, preViewMatrices, realPaint, constraint);
2870 }
2871 }
Michael Ludwig390f0cc2019-03-19 09:16:38 -04002872}
2873
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874//////////////////////////////////////////////////////////////////////////////
2875// These methods are NOT virtual, and therefore must call back into virtual
2876// methods, rather than actually drawing themselves.
2877//////////////////////////////////////////////////////////////////////////////
2878
reed374772b2016-10-05 17:33:02 -07002879void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002880 SkPaint paint;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002882 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002883 this->drawPaint(paint);
2884}
2885
2886void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
Mike Reed3661bc92017-02-22 13:21:42 -05002887 const SkPoint pt = { x, y };
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2889}
2890
Mike Reed3661bc92017-02-22 13:21:42 -05002891void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002892 SkPoint pts[2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 pts[0].set(x0, y0);
2894 pts[1].set(x1, y1);
2895 this->drawPoints(kLines_PointMode, 2, pts, paint);
2896}
2897
Mike Reed3661bc92017-02-22 13:21:42 -05002898void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002899 if (radius < 0) {
2900 radius = 0;
2901 }
2902
2903 SkRect r;
Mike Reed92b33352019-08-24 19:39:13 -04002904 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002905 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002906}
2907
2908void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2909 const SkPaint& paint) {
2910 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002911 SkRRect rrect;
2912 rrect.setRectXY(r, rx, ry);
2913 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914 } else {
2915 this->drawRect(r, paint);
2916 }
2917}
2918
reed@android.com8a1c16f2008-12-17 15:59:43 +00002919void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2920 SkScalar sweepAngle, bool useCenter,
2921 const SkPaint& paint) {
Brian Osman39c08ac2017-07-26 09:36:09 -04002922 TRACE_EVENT0("skia", TRACE_FUNC);
bsalomon21af9ca2016-08-25 12:29:23 -07002923 if (oval.isEmpty() || !sweepAngle) {
2924 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002925 }
bsalomon21af9ca2016-08-25 12:29:23 -07002926 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927}
2928
reed@android.comf76bacf2009-05-13 14:00:33 +00002929///////////////////////////////////////////////////////////////////////////////
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002930#ifdef SK_DISABLE_SKPICTURE
2931void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
reed1c2c4412015-04-30 13:09:24 -07002932
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002933
2934void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2935 const SkPaint* paint) {}
2936#else
Mike Klein88d90712018-01-27 17:30:04 +00002937/**
2938 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2939 * against the playback cost of recursing into the subpicture to get at its actual ops.
2940 *
2941 * For now we pick a conservatively small value, though measurement (and other heuristics like
2942 * the type of ops contained) may justify changing this value.
2943 */
2944#define kMaxPictureOpsToUnrollInsteadOfRef 1
2945
reedd5fa1a42014-08-09 11:08:05 -07002946void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
Brian Osman5e035ca2017-07-26 10:18:57 -04002947 TRACE_EVENT0("skia", TRACE_FUNC);
reede3b38ce2016-01-08 09:18:44 -08002948 RETURN_ON_NULL(picture);
2949
reede3b38ce2016-01-08 09:18:44 -08002950 if (matrix && matrix->isIdentity()) {
2951 matrix = nullptr;
2952 }
Mike Klein88d90712018-01-27 17:30:04 +00002953 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2954 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2955 picture->playback(this);
2956 } else {
2957 this->onDrawPicture(picture, matrix, paint);
2958 }
reedd5fa1a42014-08-09 11:08:05 -07002959}
robertphillips9b14f262014-06-04 05:40:44 -07002960
reedd5fa1a42014-08-09 11:08:05 -07002961void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2962 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002963 if (!paint || paint->canComputeFastBounds()) {
2964 SkRect bounds = picture->cullRect();
2965 if (paint) {
2966 paint->computeFastBounds(bounds, &bounds);
2967 }
2968 if (matrix) {
2969 matrix->mapRect(&bounds);
2970 }
2971 if (this->quickReject(bounds)) {
2972 return;
2973 }
2974 }
2975
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002976 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002977 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002978}
Kevin Lubick32dfdbe2018-10-18 09:47:01 -04002979#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002980
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981///////////////////////////////////////////////////////////////////////////////
2982///////////////////////////////////////////////////////////////////////////////
2983
reed3aafe112016-08-18 12:45:34 -07002984SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07002985 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986
2987 SkASSERT(canvas);
2988
reed3aafe112016-08-18 12:45:34 -07002989 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002990 fDone = !fImpl->next();
2991}
2992
2993SkCanvas::LayerIter::~LayerIter() {
2994 fImpl->~SkDrawIter();
2995}
2996
2997void SkCanvas::LayerIter::next() {
2998 fDone = !fImpl->next();
2999}
3000
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003001SkBaseDevice* SkCanvas::LayerIter::device() const {
Mike Reed99330ba2017-02-22 11:01:08 -05003002 return fImpl->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003003}
3004
3005const SkMatrix& SkCanvas::LayerIter::matrix() const {
Michael Ludwigc89d1b52019-10-18 11:32:56 -04003006 return fImpl->fDevice->localToDevice();
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007}
3008
3009const SkPaint& SkCanvas::LayerIter::paint() const {
3010 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003011 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003012 paint = &fDefaultPaint;
3013 }
3014 return *paint;
3015}
3016
Mike Reedca37f322018-03-08 13:22:16 -05003017SkIRect SkCanvas::LayerIter::clipBounds() const {
3018 return fImpl->fDevice->getGlobalBounds();
Mike Reeda1361362017-03-07 09:37:29 -05003019}
3020
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3022int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003023
3024///////////////////////////////////////////////////////////////////////////////
3025
Brian Osmane8a98632019-04-10 10:26:10 -04003026SkCanvas::ImageSetEntry::ImageSetEntry() = default;
3027SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
3028SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
3029SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
3030
Michael Ludwig390f0cc2019-03-19 09:16:38 -04003031SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3032 const SkRect& dstRect, int matrixIndex, float alpha,
3033 unsigned aaFlags, bool hasClip)
3034 : fImage(std::move(image))
3035 , fSrcRect(srcRect)
3036 , fDstRect(dstRect)
3037 , fMatrixIndex(matrixIndex)
3038 , fAlpha(alpha)
3039 , fAAFlags(aaFlags)
3040 , fHasClip(hasClip) {}
3041
3042SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
3043 const SkRect& dstRect, float alpha, unsigned aaFlags)
3044 : fImage(std::move(image))
3045 , fSrcRect(srcRect)
3046 , fDstRect(dstRect)
3047 , fAlpha(alpha)
3048 , fAAFlags(aaFlags) {}
3049
3050///////////////////////////////////////////////////////////////////////////////
3051
Mike Reed5df49342016-11-12 08:06:55 -06003052std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
Mike Reed12f77342017-11-08 11:19:52 -05003053 size_t rowBytes, const SkSurfaceProps* props) {
Brian Osman431ac562018-10-10 13:20:38 -04003054 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003055 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003056 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003057
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003058 SkBitmap bitmap;
3059 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003060 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003061 }
Mike Reed12f77342017-11-08 11:19:52 -05003062
3063 return props ?
3064 skstd::make_unique<SkCanvas>(bitmap, *props) :
3065 skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003066}
reedd5fa1a42014-08-09 11:08:05 -07003067
3068///////////////////////////////////////////////////////////////////////////////
3069
Florin Malitaee424ac2016-12-01 12:47:59 -05003070SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
Hal Canary363a3f82018-10-04 11:04:48 -04003071 : INHERITED(SkIRect::MakeWH(width, height)) {}
Florin Malitaee424ac2016-12-01 12:47:59 -05003072
Florin Malita439ace92016-12-02 12:05:41 -05003073SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
Hal Canary363a3f82018-10-04 11:04:48 -04003074 : INHERITED(bounds) {}
Florin Malita439ace92016-12-02 12:05:41 -05003075
Herb Derbyefe39bc2018-05-01 17:06:20 -04003076SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
Herb Derby76d69b42018-03-15 17:34:40 -04003077 : INHERITED(device) {}
3078
Florin Malitaee424ac2016-12-01 12:47:59 -05003079SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3080 (void)this->INHERITED::getSaveLayerStrategy(rec);
3081 return kNoLayer_SaveLayerStrategy;
3082}
3083
Mike Reed148b7fd2018-12-18 17:38:18 -05003084bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
3085 return false;
3086}
3087
Florin Malitaee424ac2016-12-01 12:47:59 -05003088///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003089
reed73603f32016-09-20 08:42:38 -07003090static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3091static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3092static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3093static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3094static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3095static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
Mike Reed356f7c22017-01-10 11:58:39 -05003096
3097///////////////////////////////////////////////////////////////////////////////////////////////////
3098
3099SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
3100 if (fAllocator && fMCRec->fTopLayer->fDevice) {
Florin Malita713b8ef2017-04-28 10:57:24 -04003101 const auto& dev = fMCRec->fTopLayer->fDevice;
Mike Reed356f7c22017-01-10 11:58:39 -05003102 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
3103 SkIPoint origin = dev->getOrigin();
3104 SkMatrix ctm = this->getTotalMatrix();
3105 ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
3106
3107 SkIRect clip = fMCRec->fRasterClip.getBounds();
3108 clip.offset(-origin.x(), -origin.y());
Mike Reed92b33352019-08-24 19:39:13 -04003109 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
Mike Reed356f7c22017-01-10 11:58:39 -05003110 clip.setEmpty();
3111 }
3112
3113 fAllocator->updateHandle(handle, ctm, clip);
3114 return handle;
3115 }
3116 return nullptr;
3117}
3118
3119static bool install(SkBitmap* bm, const SkImageInfo& info,
3120 const SkRasterHandleAllocator::Rec& rec) {
Mike Reed086a4272017-07-18 10:53:11 -04003121 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
Mike Reed356f7c22017-01-10 11:58:39 -05003122}
3123
3124SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
3125 SkBitmap* bm) {
3126 SkRasterHandleAllocator::Rec rec;
3127 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
3128 return nullptr;
3129 }
3130 return rec.fHandle;
3131}
3132
3133std::unique_ptr<SkCanvas>
3134SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
3135 const SkImageInfo& info, const Rec* rec) {
Brian Osman431ac562018-10-10 13:20:38 -04003136 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
Mike Reed356f7c22017-01-10 11:58:39 -05003137 return nullptr;
3138 }
3139
3140 SkBitmap bm;
3141 Handle hndl;
3142
3143 if (rec) {
3144 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
3145 } else {
3146 hndl = alloc->allocBitmap(info, &bm);
3147 }
3148 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
3149}
Mike Reed7c9c9e42018-01-03 09:23:34 -05003150
3151///////////////////////////////////////////////////////////////////////////////////////////////////