1 /*
2 * Copyright 2008 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkCanvas.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkBlendMode.h"
13 #include "include/core/SkBlender.h"
14 #include "include/core/SkBlurTypes.h"
15 #include "include/core/SkColorFilter.h"
16 #include "include/core/SkColorSpace.h"
17 #include "include/core/SkColorType.h"
18 #include "include/core/SkImage.h"
19 #include "include/core/SkImageFilter.h"
20 #include "include/core/SkMaskFilter.h"
21 #include "include/core/SkPath.h"
22 #include "include/core/SkPathEffect.h"
23 #include "include/core/SkPicture.h"
24 #include "include/core/SkPixmap.h"
25 #include "include/core/SkRRect.h"
26 #include "include/core/SkRSXform.h"
27 #include "include/core/SkRasterHandleAllocator.h"
28 #include "include/core/SkRefCnt.h"
29 #include "include/core/SkRegion.h"
30 #include "include/core/SkShader.h"
31 #include "include/core/SkStrokeRec.h"
32 #include "include/core/SkSurface.h"
33 #include "include/core/SkTextBlob.h"
34 #include "include/core/SkTileMode.h"
35 #include "include/core/SkTypes.h"
36 #include "include/core/SkVertices.h"
37 #include "include/private/base/SkDebug.h"
38 #include "include/private/base/SkFloatingPoint.h"
39 #include "include/private/base/SkSafe32.h"
40 #include "include/private/base/SkTPin.h"
41 #include "include/private/base/SkTemplates.h"
42 #include "include/private/base/SkTo.h"
43 #include "include/private/chromium/Slug.h"
44 #include "include/utils/SkNoDrawCanvas.h"
45 #include "src/base/SkEnumBitMask.h"
46 #include "src/base/SkMSAN.h"
47 #include "src/core/SkBlenderBase.h"
48 #include "src/core/SkBlurMaskFilterImpl.h"
49 #include "src/core/SkCanvasPriv.h"
50 #include "src/core/SkDevice.h"
51 #include "src/core/SkImageFilterTypes.h"
52 #include "src/core/SkImageFilter_Base.h"
53 #include "src/core/SkImagePriv.h"
54 #include "src/core/SkLatticeIter.h"
55 #include "src/core/SkMaskFilterBase.h"
56 #include "src/core/SkMatrixPriv.h"
57 #include "src/core/SkPaintPriv.h"
58 #include "src/core/SkSpecialImage.h"
59 #include "src/core/SkSurfacePriv.h"
60 #include "src/core/SkTraceEvent.h"
61 #include "src/core/SkVerticesPriv.h"
62 #include "src/effects/colorfilters/SkColorFilterBase.h"
63 #include "src/image/SkSurface_Base.h"
64 #include "src/text/GlyphRun.h"
65 #include "src/utils/SkPatchUtils.h"
66
67 #include <algorithm>
68 #include <memory>
69 #include <new>
70 #include <optional>
71 #include <tuple>
72 #include <utility>
73
74 #define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
75 #define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0)
76
77 // This is a test: static_assert with no message is a c++17 feature,
78 // and std::max() is constexpr only since the c++14 stdlib.
79 static_assert(std::max(3,4) == 4);
80
81 using Slug = sktext::gpu::Slug;
82
83 ///////////////////////////////////////////////////////////////////////////////////////////////////
84
SK_MAKE_BITMASK_OPS(SkCanvas::PredrawFlags)85 SK_MAKE_BITMASK_OPS(SkCanvas::PredrawFlags)
86
87 /*
88 * Return true if the drawing this rect would hit every pixels in the canvas.
89 *
90 * Returns false if
91 * - rect does not contain the canvas' bounds
92 * - paint is not fill
93 * - paint would blur or otherwise change the coverage of the rect
94 */
95 bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
96 SkEnumBitMask<PredrawFlags> flags) const {
97 // Convert flags to a ShaderOverrideOpacity enum
98 auto overrideOpacity = (flags & PredrawFlags::kOpaqueShaderOverride) ?
99 SkPaintPriv::kOpaque_ShaderOverrideOpacity :
100 (flags & PredrawFlags::kNonOpaqueShaderOverride) ?
101 SkPaintPriv::kNotOpaque_ShaderOverrideOpacity :
102 SkPaintPriv::kNone_ShaderOverrideOpacity;
103
104 const SkISize size = this->getBaseLayerSize();
105 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
106
107 // if we're clipped at all, we can't overwrite the entire surface
108 {
109 const SkDevice* root = this->rootDevice();
110 const SkDevice* top = this->topDevice();
111 if (root != top) {
112 return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite
113 }
114 if (!root->isClipWideOpen()) {
115 return false;
116 }
117 }
118
119 if (rect) {
120 if (!this->getTotalMatrix().isScaleTranslate()) {
121 return false; // conservative
122 }
123
124 SkRect devRect;
125 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
126 if (!devRect.contains(bounds)) {
127 return false;
128 }
129 }
130
131 if (paint) {
132 SkPaint::Style paintStyle = paint->getStyle();
133 if (!(paintStyle == SkPaint::kFill_Style ||
134 paintStyle == SkPaint::kStrokeAndFill_Style)) {
135 return false;
136 }
137 if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) {
138 return false; // conservative
139 }
140 }
141 return SkPaintPriv::Overwrites(paint, overrideOpacity);
142 }
143
144 ///////////////////////////////////////////////////////////////////////////////////////////////////
145
predrawNotify(bool willOverwritesEntireSurface)146 bool SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
147 if (fSurfaceBase) {
148 if (!fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
149 ? SkSurface::kDiscard_ContentChangeMode
150 : SkSurface::kRetain_ContentChangeMode)) {
151 return false;
152 }
153 }
154 return true;
155 }
156
predrawNotify(const SkRect * rect,const SkPaint * paint,SkEnumBitMask<PredrawFlags> flags)157 bool SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
158 SkEnumBitMask<PredrawFlags> flags) {
159 if (fSurfaceBase) {
160 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
161 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
162 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
163 // and therefore we don't care which mode we're in.
164 //
165 if (fSurfaceBase->outstandingImageSnapshot()) {
166 if (this->wouldOverwriteEntireSurface(rect, paint, flags)) {
167 mode = SkSurface::kDiscard_ContentChangeMode;
168 }
169 }
170 if (!fSurfaceBase->aboutToDraw(mode)) {
171 return false;
172 }
173 }
174 return true;
175 }
176
177 ///////////////////////////////////////////////////////////////////////////////
178
Layer(sk_sp<SkDevice> device,FilterSpan imageFilters,const SkPaint & paint,bool isCoverage)179 SkCanvas::Layer::Layer(sk_sp<SkDevice> device,
180 FilterSpan imageFilters,
181 const SkPaint& paint,
182 bool isCoverage)
183 : fDevice(std::move(device))
184 , fImageFilters(imageFilters.data(), imageFilters.size())
185 , fPaint(paint)
186 , fIsCoverage(isCoverage)
187 , fDiscard(false) {
188 SkASSERT(fDevice);
189 // Any image filter should have been pulled out and stored in 'imageFilter' so that 'paint'
190 // can be used as-is to draw the result of the filter to the dst device.
191 SkASSERT(!fPaint.getImageFilter());
192 }
193
BackImage(sk_sp<SkSpecialImage> img,SkIPoint loc)194 SkCanvas::BackImage::BackImage(sk_sp<SkSpecialImage> img, SkIPoint loc)
195 :fImage(img), fLoc(loc) {}
196 SkCanvas::BackImage::BackImage(const BackImage&) = default;
197 SkCanvas::BackImage::BackImage(BackImage&&) = default;
198 SkCanvas::BackImage& SkCanvas::BackImage::operator=(const BackImage&) = default;
199 SkCanvas::BackImage::~BackImage() = default;
200
MCRec(SkDevice * device)201 SkCanvas::MCRec::MCRec(SkDevice* device) : fDevice(device) {
202 SkASSERT(fDevice);
203 }
204
MCRec(const MCRec * prev)205 SkCanvas::MCRec::MCRec(const MCRec* prev) : fDevice(prev->fDevice), fMatrix(prev->fMatrix) {
206 SkASSERT(fDevice);
207 }
208
~MCRec()209 SkCanvas::MCRec::~MCRec() {}
210
newLayer(sk_sp<SkDevice> layerDevice,FilterSpan filters,const SkPaint & restorePaint,bool layerIsCoverage)211 void SkCanvas::MCRec::newLayer(sk_sp<SkDevice> layerDevice,
212 FilterSpan filters,
213 const SkPaint& restorePaint,
214 bool layerIsCoverage) {
215 SkASSERT(!fBackImage);
216 fLayer =
217 std::make_unique<Layer>(std::move(layerDevice), filters, restorePaint, layerIsCoverage);
218 fDevice = fLayer->fDevice.get();
219 }
220
reset(SkDevice * device)221 void SkCanvas::MCRec::reset(SkDevice* device) {
222 SkASSERT(!fLayer);
223 SkASSERT(device);
224 SkASSERT(fDeferredSaveCount == 0);
225 fDevice = device;
226 fMatrix.setIdentity();
227 }
228
229 class SkCanvas::AutoUpdateQRBounds {
230 public:
AutoUpdateQRBounds(SkCanvas * canvas)231 explicit AutoUpdateQRBounds(SkCanvas* canvas) : fCanvas(canvas) {
232 // pre-condition, fQuickRejectBounds and other state should be valid before anything
233 // modifies the device's clip.
234 fCanvas->validateClip();
235 }
~AutoUpdateQRBounds()236 ~AutoUpdateQRBounds() {
237 fCanvas->fQuickRejectBounds = fCanvas->computeDeviceClipBounds();
238 // post-condition, we should remain valid after re-computing the bounds
239 fCanvas->validateClip();
240 }
241
242 private:
243 SkCanvas* fCanvas;
244
245 AutoUpdateQRBounds(AutoUpdateQRBounds&&) = delete;
246 AutoUpdateQRBounds(const AutoUpdateQRBounds&) = delete;
247 AutoUpdateQRBounds& operator=(AutoUpdateQRBounds&&) = delete;
248 AutoUpdateQRBounds& operator=(const AutoUpdateQRBounds&) = delete;
249 };
250
251 /////////////////////////////////////////////////////////////////////////////
252
aboutToDraw(const SkPaint & paint,const SkRect * rawBounds,SkEnumBitMask<PredrawFlags> flags)253 std::optional<AutoLayerForImageFilter> SkCanvas::aboutToDraw(
254 const SkPaint& paint,
255 const SkRect* rawBounds,
256 SkEnumBitMask<PredrawFlags> flags) {
257 if (flags & PredrawFlags::kCheckForOverwrite) {
258 if (!this->predrawNotify(rawBounds, &paint, flags)) {
259 return std::nullopt;
260 }
261 } else {
262 if (!this->predrawNotify()) {
263 return std::nullopt;
264 }
265 }
266
267 // TODO: Eventually all devices will use this code path and this will just test 'flags'.
268 const bool skipMaskFilterLayer = (flags & PredrawFlags::kSkipMaskFilterAutoLayer) ||
269 !this->topDevice()->useDrawCoverageMaskForMaskFilters();
270 return std::optional<AutoLayerForImageFilter>(
271 std::in_place, this, paint, rawBounds, skipMaskFilterLayer);
272 }
273
aboutToDraw(const SkPaint & paint,const SkRect * rawBounds)274 std::optional<AutoLayerForImageFilter> SkCanvas::aboutToDraw(
275 const SkPaint& paint,
276 const SkRect* rawBounds) {
277 return this->aboutToDraw(paint, rawBounds, PredrawFlags::kNone);
278 }
279
280 ////////////////////////////////////////////////////////////////////////////
281
resetForNextPicture(const SkIRect & bounds)282 void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
283 this->restoreToCount(1);
284
285 // We're peering through a lot of structs here. Only at this scope do we know that the device
286 // is a SkNoPixelsDevice.
287 SkASSERT(fRootDevice->isNoPixelsDevice());
288 SkNoPixelsDevice* asNoPixelsDevice = static_cast<SkNoPixelsDevice*>(fRootDevice.get());
289 if (!asNoPixelsDevice->resetForNextPicture(bounds)) {
290 fRootDevice = sk_make_sp<SkNoPixelsDevice>(bounds,
291 fRootDevice->surfaceProps(),
292 fRootDevice->imageInfo().refColorSpace());
293 }
294
295 fMCRec->reset(fRootDevice.get());
296 fQuickRejectBounds = this->computeDeviceClipBounds();
297 }
298
init(sk_sp<SkDevice> device)299 void SkCanvas::init(sk_sp<SkDevice> device) {
300 // SkCanvas.h declares internal storage for the hidden struct MCRec, and this
301 // assert ensure it's sufficient. <= is used because the struct has pointer fields, so the
302 // declared size is an upper bound across architectures. When the size is smaller, more stack
303 static_assert(sizeof(MCRec) <= kMCRecSize);
304
305 if (!device) {
306 device = sk_make_sp<SkNoPixelsDevice>(SkIRect::MakeEmpty(), fProps);
307 }
308
309 // From this point on, SkCanvas will always have a device
310 SkASSERT(device);
311
312 fSaveCount = 1;
313 fMCRec = new (fMCStack.push_back()) MCRec(device.get());
314
315 // The root device and the canvas should always have the same pixel geometry
316 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
317
318 fSurfaceBase = nullptr;
319 fRootDevice = std::move(device);
320 fScratchGlyphRunBuilder = std::make_unique<sktext::GlyphRunBuilder>();
321 fQuickRejectBounds = this->computeDeviceClipBounds();
322 }
323
SkCanvas()324 SkCanvas::SkCanvas() : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
325 this->init(nullptr);
326 }
327
SkCanvas(int width,int height,const SkSurfaceProps * props)328 SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
329 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
330 , fProps(SkSurfacePropsCopyOrDefault(props)) {
331 this->init(sk_make_sp<SkNoPixelsDevice>(
332 SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps));
333 }
334
SkCanvas(const SkIRect & bounds)335 SkCanvas::SkCanvas(const SkIRect& bounds)
336 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
337 SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds;
338 this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps));
339 }
340
SkCanvas(sk_sp<SkDevice> device)341 SkCanvas::SkCanvas(sk_sp<SkDevice> device)
342 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
343 , fProps(device->surfaceProps()) {
344 this->init(std::move(device));
345 }
346
~SkCanvas()347 SkCanvas::~SkCanvas() {
348 // Mark all pending layers to be discarded during restore (rather than drawn)
349 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
350 for (;;) {
351 MCRec* rec = (MCRec*)iter.next();
352 if (!rec) {
353 break;
354 }
355 if (rec->fLayer) {
356 rec->fLayer->fDiscard = true;
357 }
358 }
359
360 // free up the contents of our deque
361 this->restoreToCount(1); // restore everything but the last
362 this->internalRestore(); // restore the last, since we're going away
363 }
364
getSurface() const365 SkSurface* SkCanvas::getSurface() const {
366 return fSurfaceBase;
367 }
368
getBaseLayerSize() const369 SkISize SkCanvas::getBaseLayerSize() const {
370 return this->rootDevice()->imageInfo().dimensions();
371 }
372
topDevice() const373 SkDevice* SkCanvas::topDevice() const {
374 SkASSERT(fMCRec->fDevice);
375 return fMCRec->fDevice;
376 }
377
readPixels(const SkPixmap & pm,int x,int y)378 bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
379 return pm.addr() && this->rootDevice()->readPixels(pm, x, y);
380 }
381
readPixels(const SkImageInfo & dstInfo,void * dstP,size_t rowBytes,int x,int y)382 bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
383 return this->readPixels({ dstInfo, dstP, rowBytes}, x, y);
384 }
385
readPixels(const SkBitmap & bm,int x,int y)386 bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
387 SkPixmap pm;
388 return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
389 }
390
writePixels(const SkBitmap & bitmap,int x,int y)391 bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
392 SkPixmap pm;
393 if (bitmap.peekPixels(&pm)) {
394 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
395 }
396 return false;
397 }
398
writePixels(const SkImageInfo & srcInfo,const void * pixels,size_t rowBytes,int x,int y)399 bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
400 int x, int y) {
401 SkDevice* device = this->rootDevice();
402
403 // This check gives us an early out and prevents generation ID churn on the surface.
404 // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
405 SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
406 if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
407 return false;
408 }
409
410 // Tell our owning surface to bump its generation ID.
411 const bool completeOverwrite = srcRect.size() == device->imageInfo().dimensions();
412 if (!this->predrawNotify(completeOverwrite)) {
413 return false;
414 }
415
416 // This can still fail, most notably in the case of a invalid color type or alpha type
417 // conversion. We could pull those checks into this function and avoid the unnecessary
418 // generation ID bump. But then we would be performing those checks twice, since they
419 // are also necessary at the bitmap/pixmap entry points.
420 return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
421 }
422
423 //////////////////////////////////////////////////////////////////////////////
424
checkForDeferredSave()425 void SkCanvas::checkForDeferredSave() {
426 if (fMCRec->fDeferredSaveCount > 0) {
427 this->doSave();
428 }
429 }
430
getSaveCount() const431 int SkCanvas::getSaveCount() const {
432 #ifdef SK_DEBUG
433 int count = 0;
434 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
435 for (;;) {
436 const MCRec* rec = (const MCRec*)iter.next();
437 if (!rec) {
438 break;
439 }
440 count += 1 + rec->fDeferredSaveCount;
441 }
442 SkASSERT(count == fSaveCount);
443 #endif
444 return fSaveCount;
445 }
446
save()447 int SkCanvas::save() {
448 fSaveCount += 1;
449 fMCRec->fDeferredSaveCount += 1;
450 return this->getSaveCount() - 1; // return our prev value
451 }
452
doSave()453 void SkCanvas::doSave() {
454 this->willSave();
455
456 SkASSERT(fMCRec->fDeferredSaveCount > 0);
457 fMCRec->fDeferredSaveCount -= 1;
458 this->internalSave();
459 }
460
restore()461 void SkCanvas::restore() {
462 if (fMCRec->fDeferredSaveCount > 0) {
463 SkASSERT(fSaveCount > 1);
464 fSaveCount -= 1;
465 fMCRec->fDeferredSaveCount -= 1;
466 } else {
467 // check for underflow
468 if (fMCStack.count() > 1) {
469 this->willRestore();
470 SkASSERT(fSaveCount > 1);
471 fSaveCount -= 1;
472 this->internalRestore();
473 this->didRestore();
474 }
475 }
476 }
477
restoreToCount(int count)478 void SkCanvas::restoreToCount(int count) {
479 // safety check
480 if (count < 1) {
481 count = 1;
482 }
483
484 int n = this->getSaveCount() - count;
485 for (int i = 0; i < n; ++i) {
486 this->restore();
487 }
488 }
489
internalSave()490 void SkCanvas::internalSave() {
491 fMCRec = new (fMCStack.push_back()) MCRec(fMCRec);
492
493 this->topDevice()->pushClipStack();
494 }
495
saveLayer(const SkRect * bounds,const SkPaint * paint)496 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
497 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
498 }
499
saveLayer(const SaveLayerRec & rec)500 int SkCanvas::saveLayer(const SaveLayerRec& rec) {
501 TRACE_EVENT0("skia", TRACE_FUNC);
502 if (rec.fPaint && rec.fPaint->nothingToDraw()) {
503 // no need for the layer (or any of the draws until the matching restore()
504 this->save();
505 this->clipRect({0,0,0,0});
506 } else {
507 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
508 fSaveCount += 1;
509 this->internalSaveLayer(rec, strategy);
510 }
511 return this->getSaveCount() - 1;
512 }
513
only_axis_aligned_saveBehind(const SkRect * bounds)514 int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
515 if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
516 // Assuming clips never expand, if the request bounds is outside of the current clip
517 // there is no need to copy/restore the area, so just devolve back to a regular save.
518 this->save();
519 } else {
520 bool doTheWork = this->onDoSaveBehind(bounds);
521 fSaveCount += 1;
522 this->internalSave();
523 if (doTheWork) {
524 this->internalSaveBehind(bounds);
525 }
526 }
527 return this->getSaveCount() - 1;
528 }
529
530 // Helper function to compute the center reference point used for scale decomposition under
531 // non-linear transformations.
compute_decomposition_center(const SkMatrix & dstToLocal,std::optional<skif::ParameterSpace<SkRect>> contentBounds,const skif::DeviceSpace<SkIRect> & targetOutput)532 static skif::ParameterSpace<SkPoint> compute_decomposition_center(
533 const SkMatrix& dstToLocal,
534 std::optional<skif::ParameterSpace<SkRect>> contentBounds,
535 const skif::DeviceSpace<SkIRect>& targetOutput) {
536 // Will use the inverse and center of the device bounds if the content bounds aren't provided.
537 SkRect rect = contentBounds ? SkRect(*contentBounds) : SkRect::Make(SkIRect(targetOutput));
538 SkPoint center = {rect.centerX(), rect.centerY()};
539 if (!contentBounds) {
540 // Theoretically, the inverse transform could put center's homogeneous coord behind W = 0,
541 // but that case is handled automatically in Mapping::decomposeCTM later.
542 dstToLocal.mapPoints(¢er, 1);
543 }
544
545 return skif::ParameterSpace<SkPoint>(center);
546 }
547
548 // Helper when we need to upgrade a single filter to a FilterSpan
549 struct FilterToSpan {
FilterToSpanFilterToSpan550 FilterToSpan(const SkImageFilter* filter) : fFilter(sk_ref_sp(filter)) {}
551
operator SkCanvas::FilterSpanFilterToSpan552 operator SkCanvas::FilterSpan() {
553 return fFilter ? SkCanvas::FilterSpan{&fFilter, 1} : SkCanvas::FilterSpan{};
554 }
555
556 sk_sp<SkImageFilter> fFilter;
557 };
558
559 // Compute suitable transformations and layer bounds for a new layer that will be used as the source
560 // input into 'filter' before being drawn into 'dst' via the returned skif::Mapping.
561 // Null filters are permitted and act as the identity. The returned mapping will be compatible with
562 // the image filter.
563 //
564 // An empty optional is returned if the layer mapping and bounds couldn't be determined, in which
565 // case the layer should be skipped. An instantiated optional can have an empty layer bounds rect
566 // if the image filter doesn't require an input image to produce a valid output.
567 static std::optional<std::pair<skif::Mapping, skif::LayerSpace<SkIRect>>>
get_layer_mapping_and_bounds(SkCanvas::FilterSpan filters,const SkMatrix & localToDst,const skif::DeviceSpace<SkIRect> & targetOutput,std::optional<skif::ParameterSpace<SkRect>> contentBounds={},SkScalar scaleFactor=1.0f)568 get_layer_mapping_and_bounds(
569 SkCanvas::FilterSpan filters,
570 const SkMatrix& localToDst,
571 const skif::DeviceSpace<SkIRect>& targetOutput,
572 std::optional<skif::ParameterSpace<SkRect>> contentBounds = {},
573 SkScalar scaleFactor = 1.0f) {
574 SkMatrix dstToLocal;
575 if (!localToDst.isFinite() ||
576 !localToDst.invert(&dstToLocal)) {
577 return {};
578 }
579
580 skif::ParameterSpace<SkPoint> center =
581 compute_decomposition_center(dstToLocal, contentBounds, targetOutput);
582
583 // Determine initial mapping and a reasonable maximum dimension to prevent layer-to-device
584 // transforms with perspective and skew from triggering excessive buffer allocations.
585 skif::Mapping mapping;
586 skif::MatrixCapability capability = skif::MatrixCapability::kComplex;
587 for (const sk_sp<SkImageFilter>& filter : filters) {
588 if (filter) {
589 capability = std::min(capability, as_IFB(filter)->getCTMCapability());
590 }
591 }
592 if (!mapping.decomposeCTM(localToDst, capability, center)) {
593 return {};
594 }
595 // Push scale factor into layer matrix and device matrix (net no change, but the layer will have
596 // its resolution adjusted in comparison to the final device).
597 if (scaleFactor != 1.0f &&
598 !mapping.adjustLayerSpace(SkMatrix::Scale(scaleFactor, scaleFactor))) {
599 return {};
600 }
601
602 // Perspective and skew could exceed this since mapping.deviceToLayer(targetOutput) is
603 // theoretically unbounded under those conditions. Under a 45 degree rotation, a layer needs to
604 // be 2X larger per side of the prior device in order to fully cover it. We use the max of that
605 // and 2048 for a reasonable upper limit (this allows small layers under extreme transforms to
606 // use more relative resolution than a larger layer).
607 static const int kMinDimThreshold = 2048;
608 int maxLayerDim = std::max(Sk64_pin_to_s32(2 * std::max(SkIRect(targetOutput).width64(),
609 SkIRect(targetOutput).height64())),
610 kMinDimThreshold);
611
612 auto baseLayerBounds = mapping.deviceToLayer(targetOutput);
613 if (contentBounds) {
614 // For better or for worse, user bounds currently act as a hard clip on the layer's
615 // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
616 skif::LayerSpace<SkIRect> knownBounds = mapping.paramToLayer(*contentBounds).roundOut();
617 if (!baseLayerBounds.intersect(knownBounds)) {
618 baseLayerBounds = skif::LayerSpace<SkIRect>::Empty();
619 }
620 }
621
622 skif::LayerSpace<SkIRect> layerBounds;
623 if (!filters.empty()) {
__anon7533d6760102(int i) 624 layerBounds = skif::LayerSpace<SkIRect>::Union(filters.size(), [&](int i) {
625 return filters[i] ? as_IFB(filters[i])
626 ->getInputBounds(mapping, targetOutput, contentBounds)
627 : baseLayerBounds;
628 });
629 // When a filter is involved, the layer size may be larger than the default maxLayerDim due
630 // to required inputs for filters (e.g. a displacement map with a large radius).
631 if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
632 skif::Mapping idealMapping{mapping.layerMatrix()};
633 for (const sk_sp<SkImageFilter>& filter : filters) {
634 if (filter) {
635 auto idealLayerBounds = as_IFB(filter)->getInputBounds(
636 idealMapping, targetOutput, contentBounds);
637 maxLayerDim = std::max(std::max(idealLayerBounds.width(),
638 idealLayerBounds.height()),
639 maxLayerDim);
640 }
641 }
642 }
643 } else {
644 if (baseLayerBounds.isEmpty()) {
645 return {};
646 }
647 layerBounds = baseLayerBounds;
648 }
649
650 if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
651 skif::LayerSpace<SkIRect> newLayerBounds(
652 SkIRect::MakeWH(std::min(layerBounds.width(), maxLayerDim),
653 std::min(layerBounds.height(), maxLayerDim)));
654 SkMatrix adjust = SkMatrix::MakeRectToRect(SkRect::Make(SkIRect(layerBounds)),
655 SkRect::Make(SkIRect(newLayerBounds)),
656 SkMatrix::kFill_ScaleToFit);
657 if (!mapping.adjustLayerSpace(adjust)) {
658 return {};
659 } else {
660 layerBounds = newLayerBounds;
661 }
662 }
663
664 return std::make_pair(mapping, layerBounds);
665 }
666
667 // Ideally image filters operate in the dst color type, but if there is insufficient alpha bits
668 // we move some bits from color channels into the alpha channel since that can greatly improve
669 // the quality of blurs and other filters.
image_filter_color_type(const SkColorInfo & dstInfo)670 static SkColorType image_filter_color_type(const SkColorInfo& dstInfo) {
671 if (dstInfo.bytesPerPixel() <= 4 &&
672 dstInfo.colorType() != kRGBA_8888_SkColorType &&
673 dstInfo.colorType() != kBGRA_8888_SkColorType) {
674 // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888
675 return kN32_SkColorType;
676 } else {
677 return dstInfo.colorType();
678 }
679 }
680
apply_alpha_and_colorfilter(const skif::Context & ctx,const skif::FilterResult & image,const SkPaint & paint)681 static skif::FilterResult apply_alpha_and_colorfilter(const skif::Context& ctx,
682 const skif::FilterResult& image,
683 const SkPaint& paint) {
684 // The only effects that apply to layers (other than the SkImageFilter that made this image in
685 // the first place) are transparency and color filters.
686 skif::FilterResult result = image;
687 if (paint.getAlphaf() < 1.f) {
688 result = result.applyColorFilter(ctx, SkColorFilters::Blend(paint.getColor4f(),
689 /*colorSpace=*/nullptr,
690 SkBlendMode::kDstIn));
691 }
692 if (paint.getColorFilter()) {
693 result = result.applyColorFilter(ctx, paint.refColorFilter());
694 }
695 return result;
696 }
697
internalDrawDeviceWithFilter(SkDevice * src,SkDevice * dst,FilterSpan filters,const SkPaint & paint,DeviceCompatibleWithFilter compat,const SkColorInfo & filterColorInfo,SkScalar scaleFactor,bool srcIsCoverageLayer)698 void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src,
699 SkDevice* dst,
700 FilterSpan filters,
701 const SkPaint& paint,
702 DeviceCompatibleWithFilter compat,
703 const SkColorInfo& filterColorInfo,
704 SkScalar scaleFactor,
705 bool srcIsCoverageLayer) {
706 // The dst is always required, the src can be null if 'filter' is non-null and does not require
707 // a source image. For regular filters, 'src' is the layer and 'dst' is the parent device. For
708 // backdrop filters, 'src' is the parent device and 'dst' is the layer.
709 SkASSERT(dst);
710
711 sk_sp<SkColorSpace> filterColorSpace = filterColorInfo.refColorSpace();
712
713 const SkColorType filterColorType =
714 srcIsCoverageLayer ? kAlpha_8_SkColorType : image_filter_color_type(filterColorInfo);
715
716 // 'filter' sees the src device's buffer as the implicit input image, and processes the image
717 // in this device space (referred to as the "layer" space). However, the filter
718 // parameters need to respect the current matrix, which is not necessarily the local matrix that
719 // was set on 'src' (e.g. because we've popped src off the stack already).
720 // TODO (michaelludwig): Stay in SkM44 once skif::Mapping supports SkM44 instead of SkMatrix.
721 SkMatrix localToSrc = src ? (src->globalToDevice() * fMCRec->fMatrix).asM33() : SkMatrix::I();
722 SkISize srcDims = src ? src->imageInfo().dimensions() : SkISize::Make(0, 0);
723
724 // Whether or not we need to make a transformed tmp image from 'src', and what that transform is
725 skif::LayerSpace<SkMatrix> srcToLayer;
726
727 skif::Mapping mapping;
728 skif::LayerSpace<SkIRect> requiredInput;
729 skif::DeviceSpace<SkIRect> outputBounds{dst->devClipBounds()};
730 if (compat == DeviceCompatibleWithFilter::kYes) {
731 // Just use the relative transform from src to dst and the src's whole image, since
732 // internalSaveLayer should have already determined what was necessary. We explicitly
733 // construct the inverse (dst->src) to avoid the case where src's and dst's coord transforms
734 // were individually invertible by SkM44::invert() but their product is considered not
735 // invertible by SkMatrix::invert(). When this happens the matrices are already poorly
736 // conditioned so getRelativeTransform() gives us something reasonable.
737 SkASSERT(src);
738 SkASSERT(scaleFactor == 1.0f);
739 SkASSERT(!srcDims.isEmpty());
740
741 mapping = skif::Mapping(src->getRelativeTransform(*dst),
742 dst->getRelativeTransform(*src),
743 localToSrc);
744 requiredInput = skif::LayerSpace<SkIRect>(SkIRect::MakeSize(srcDims));
745 srcToLayer = skif::LayerSpace<SkMatrix>(SkMatrix::I());
746 } else {
747 // Compute the image filter mapping by decomposing the local->device matrix of dst and
748 // re-determining the required input.
749 auto mappingAndBounds = get_layer_mapping_and_bounds(
750 filters, dst->localToDevice(), outputBounds, {}, SkTPin(scaleFactor, 0.f, 1.f));
751 if (!mappingAndBounds) {
752 return;
753 }
754
755 std::tie(mapping, requiredInput) = *mappingAndBounds;
756 if (src) {
757 if (!requiredInput.isEmpty()) {
758 // The above mapping transforms from local to dst's device space, where the layer
759 // space represents the intermediate buffer. Now we need to determine the transform
760 // from src to intermediate to prepare the input to the filter.
761 SkMatrix srcToLocal;
762 if (!localToSrc.invert(&srcToLocal)) {
763 return;
764 }
765 srcToLayer = skif::LayerSpace<SkMatrix>(SkMatrix::Concat(mapping.layerMatrix(),
766 srcToLocal));
767 } // Else no input is needed which can happen if a backdrop filter that doesn't use src
768 } else {
769 // Trust the caller that no input was required, but keep the calculated mapping
770 requiredInput = skif::LayerSpace<SkIRect>::Empty();
771 }
772 }
773
774 // Start out with an empty source image, to be replaced with the snapped 'src' device.
775 auto backend = dst->createImageFilteringBackend(src ? src->surfaceProps() : dst->surfaceProps(),
776 filterColorType);
777 skif::Stats stats;
778 skif::Context ctx{std::move(backend),
779 mapping,
780 requiredInput,
781 skif::FilterResult{},
782 filterColorSpace.get(),
783 &stats};
784
785 skif::FilterResult source;
786 if (src && !requiredInput.isEmpty()) {
787 skif::LayerSpace<SkIRect> srcSubset;
788 if (!srcToLayer.inverseMapRect(requiredInput, &srcSubset)) {
789 return;
790 }
791
792 // Include the layer in the offscreen count
793 ctx.markNewSurface();
794
795 auto availSrc = skif::LayerSpace<SkIRect>(src->size()).relevantSubset(
796 srcSubset, SkTileMode::kClamp);
797
798 if (SkMatrix(srcToLayer).isScaleTranslate()) {
799 // Apply the srcToLayer transformation directly while snapping an image from the src
800 // device. Calculate the subset of requiredInput that corresponds to srcSubset that was
801 // restricted to the actual src dimensions.
802 auto requiredSubset = srcToLayer.mapRect(availSrc);
803 if (requiredSubset.width() == availSrc.width() &&
804 requiredSubset.height() == availSrc.height()) {
805 // Unlike snapSpecialScaled(), snapSpecial() can avoid a copy when the underlying
806 // representation permits it.
807 source = {src->snapSpecial(SkIRect(availSrc)), requiredSubset.topLeft()};
808 } else {
809 SkASSERT(compat == DeviceCompatibleWithFilter::kUnknown);
810 source = {src->snapSpecialScaled(SkIRect(availSrc),
811 SkISize(requiredSubset.size())),
812 requiredSubset.topLeft()};
813 ctx.markNewSurface();
814 }
815 }
816
817 if (compat == DeviceCompatibleWithFilter::kYes) {
818 // Padding was added to the source image when the 'src' SkDevice was created, so inset
819 // to allow bounds tracking to skip shader-based tiling when possible.
820 if (!filters.empty()) {
821 source = source.insetForSaveLayer();
822 }
823 } else if (source) {
824 // A backdrop filter that succeeded in snapSpecial() or snapSpecialScaled(), but since
825 // the 'src' device wasn't prepared with 'requiredInput' in mind, add clamping.
826 source = source.applyCrop(ctx, source.layerBounds(), SkTileMode::kClamp);
827 } else if (!requiredInput.isEmpty()) {
828 // Otherwise snapSpecialScaled() failed or the transform was complex, so snap the source
829 // image at its original resolution and then apply srcToLayer to map to the effective
830 // layer coordinate space.
831 source = {src->snapSpecial(SkIRect(availSrc)), availSrc.topLeft()};
832 // We adjust the desired output of the applyCrop() because ctx was original set to
833 // fulfill 'requiredInput', which is valid *after* we apply srcToLayer. Use the original
834 // 'srcSubset' for the desired output so that the kClamp applied to the available subset
835 // is not discarded as a no-op.
836 source = source.applyCrop(ctx.withNewDesiredOutput(srcSubset),
837 source.layerBounds(),
838 SkTileMode::kClamp)
839 .applyTransform(ctx, srcToLayer, SkFilterMode::kLinear);
840 }
841 } // else leave 'source' as the empty image
842
843 // Evaluate the image filter, with a context pointing to the source snapped from 'src' and
844 // possibly transformed into the intermediate layer coordinate space.
845 ctx = ctx.withNewDesiredOutput(mapping.deviceToLayer(outputBounds))
846 .withNewSource(source);
847
848 // Here, we allow a single-element FilterSpan with a null entry, to simplify the loop:
849 sk_sp<SkImageFilter> nullFilter;
850 FilterSpan filtersOrNull = filters.empty() ? FilterSpan{&nullFilter, 1} : filters;
851
852 for (const sk_sp<SkImageFilter>& filter : filtersOrNull) {
853 auto result = filter ? as_IFB(filter)->filterImage(ctx) : source;
854
855 if (srcIsCoverageLayer) {
856 SkASSERT(dst->useDrawCoverageMaskForMaskFilters());
857 // TODO: Can FilterResult optimize this in any meaningful way if it still has to go
858 // through drawCoverageMask that requires an image (vs a coverage shader)?
859 auto [coverageMask, origin] = result.imageAndOffset(ctx);
860 if (coverageMask) {
861 SkMatrix deviceMatrixWithOffset = mapping.layerToDevice();
862 deviceMatrixWithOffset.preTranslate(origin.x(), origin.y());
863 dst->drawCoverageMask(
864 coverageMask.get(), deviceMatrixWithOffset, result.sampling(), paint);
865 }
866 } else {
867 result = apply_alpha_and_colorfilter(ctx, result, paint);
868 result.draw(ctx, dst, paint.getBlender());
869 }
870 }
871
872 stats.reportStats();
873 }
874
internalSaveLayer(const SaveLayerRec & rec,SaveLayerStrategy strategy,bool coverageOnly)875 void SkCanvas::internalSaveLayer(const SaveLayerRec& rec,
876 SaveLayerStrategy strategy,
877 bool coverageOnly) {
878 TRACE_EVENT0("skia", TRACE_FUNC);
879 // Do this before we create the layer. We don't call the public save() since that would invoke a
880 // possibly overridden virtual.
881 this->internalSave();
882
883 if (this->isClipEmpty()) {
884 // Early out if the layer wouldn't draw anything
885 return;
886 }
887
888 // Build up the paint for restoring the layer, taking only the pieces of rec.fPaint that are
889 // relevant. Filtering is automatically chosen in internalDrawDeviceWithFilter based on the
890 // device's coordinate space.
891 SkPaint restorePaint(rec.fPaint ? *rec.fPaint : SkPaint());
892 restorePaint.setStyle(SkPaint::kFill_Style); // a layer is filled out "infinitely"
893 restorePaint.setPathEffect(nullptr); // path effects are ignored for saved layers
894 restorePaint.setMaskFilter(nullptr); // mask filters are ignored for saved layers
895 restorePaint.setImageFilter(nullptr); // the image filter is held separately
896 // Smooth non-axis-aligned layer edges; this automatically downgrades to non-AA for aligned
897 // layer restores. This is done to match legacy behavior where the post-applied MatrixTransform
898 // bilerp also smoothed cropped edges. See skbug.com/11252
899 restorePaint.setAntiAlias(true);
900
901 sk_sp<SkImageFilter> paintFilter = rec.fPaint ? rec.fPaint->refImageFilter() : nullptr;
902 FilterSpan filters = paintFilter ? FilterSpan{&paintFilter, 1} : rec.fFilters;
903 if (filters.size() > kMaxFiltersPerLayer) {
904 filters = filters.first(kMaxFiltersPerLayer);
905 }
906 const SkColorFilter* cf = restorePaint.getColorFilter();
907 const SkBlender* blender = restorePaint.getBlender();
908
909 // When this is false, restoring the layer filled with unmodified prior contents should be
910 // identical to the prior contents, so we can restrict the layer even more than just the
911 // clip bounds.
912 bool filtersPriorDevice = rec.fBackdrop;
913 #if !defined(SK_LEGACY_INITWITHPREV_LAYER_SIZING)
914 // A regular filter applied to a layer initialized with prior contents is somewhat
915 // analogous to a backdrop filter so they are treated the same.
916 // TODO(b/314968012): Chrome needs to be updated to clip saveAlphaLayer bounds explicitly when
917 // it uses kInitWithPrevious and LCD text.
918 filtersPriorDevice |= ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) &&
919 (!filters.empty() || cf || blender || restorePaint.getAlphaf() < 1.f));
920 #endif
921 // If the restorePaint has a transparency-affecting colorfilter or blender, the output is
922 // unbounded during restore(). `internalDrawDeviceWithFilter` automatically applies these
923 // effects. When there's no image filter, SkDevice::drawDevice is used, which does
924 // not apply effects beyond the layer's image so we mark `trivialRestore` as false too.
925 // TODO: drawDevice() could be updated to apply transparency-affecting effects to a content-
926 // clipped image, but this is the simplest solution when considering document-based SkDevices.
927 const bool drawDeviceMustFillClip = filters.empty() &&
928 ((cf && as_CFB(cf)->affectsTransparentBlack()) ||
929 (blender && as_BB(blender)->affectsTransparentBlack()));
930 const bool trivialRestore = !filtersPriorDevice && !drawDeviceMustFillClip;
931
932 // Size the new layer relative to the prior device, which may already be aligned for filters.
933 SkDevice* priorDevice = this->topDevice();
934 skif::Mapping newLayerMapping;
935 skif::LayerSpace<SkIRect> layerBounds;
936 skif::DeviceSpace<SkIRect> outputBounds{priorDevice->devClipBounds()};
937
938 std::optional<skif::ParameterSpace<SkRect>> contentBounds;
939 // Set the bounds hint if provided and there's no further effects on prior device content
940 if (rec.fBounds && trivialRestore) {
941 contentBounds = skif::ParameterSpace<SkRect>(*rec.fBounds);
942 }
943
944 auto mappingAndBounds = get_layer_mapping_and_bounds(
945 filters, priorDevice->localToDevice(), outputBounds, contentBounds);
946
947 auto abortLayer = [this]() {
948 // The filtered content would not draw anything, or the new device space has an invalid
949 // coordinate system, in which case we mark the current top device as empty so that nothing
950 // draws until the canvas is restored past this saveLayer.
951 AutoUpdateQRBounds aqr(this);
952 this->topDevice()->clipRect(SkRect::MakeEmpty(), SkClipOp::kIntersect, /* aa */ false);
953 };
954
955 if (!mappingAndBounds) {
956 abortLayer();
957 return;
958 }
959
960 std::tie(newLayerMapping, layerBounds) = *mappingAndBounds;
961
962 if (layerBounds.isEmpty()) {
963 // The image filter graph does not require any input, so we don't need to actually render
964 // a new layer for the source image. This could be because the image filter itself will not
965 // produce output, or that the filter DAG has no references to the dynamic source image.
966 // In this case it still has an output that we need to render, but do so now since there is
967 // no new layer pushed on the stack and the paired restore() will be a no-op.
968 if (!filters.empty() && !priorDevice->isNoPixelsDevice()) {
969 SkColorInfo filterColorInfo = priorDevice->imageInfo().colorInfo();
970 if (rec.fColorSpace) {
971 filterColorInfo = filterColorInfo.makeColorSpace(sk_ref_sp(rec.fColorSpace));
972 }
973 this->internalDrawDeviceWithFilter(/*src=*/nullptr, priorDevice, filters, restorePaint,
974 DeviceCompatibleWithFilter::kUnknown,
975 filterColorInfo);
976 }
977
978 // Regardless of if we drew the "restored" image filter or not, mark the layer as empty
979 // until the restore() since we don't care about any of its content.
980 abortLayer();
981 return;
982 } else {
983 // TODO(b/329700315): Once dithers can be anchored more flexibly, we can return to
984 // universally adding padding even for layers w/o filters. This change would simplify layer
985 // prep and restore logic and allow us to flexibly switch the sampling to linear if NN has
986 // issues on certain hardware.
987 if (!filters.empty()) {
988 // Add a buffer of padding so that image filtering can avoid accessing unitialized data
989 // and switch from shader-decal'ing to clamping.
990 layerBounds.outset(skif::LayerSpace<SkISize>({1, 1}));
991 }
992 }
993
994 sk_sp<SkDevice> newDevice;
995 if (strategy == kFullLayer_SaveLayerStrategy) {
996 SkASSERT(!layerBounds.isEmpty());
997
998 SkColorType layerColorType;
999 if (coverageOnly) {
1000 layerColorType = kAlpha_8_SkColorType;
1001 } else {
1002 layerColorType = SkToBool(rec.fSaveLayerFlags & kF16ColorType)
1003 ? kRGBA_F16_SkColorType
1004 : image_filter_color_type(priorDevice->imageInfo().colorInfo());
1005 }
1006 SkImageInfo info =
1007 SkImageInfo::Make(layerBounds.width(),
1008 layerBounds.height(),
1009 layerColorType,
1010 kPremul_SkAlphaType,
1011 rec.fColorSpace ? sk_ref_sp(rec.fColorSpace)
1012 : priorDevice->imageInfo().refColorSpace());
1013
1014 SkPixelGeometry geo = rec.fSaveLayerFlags & kPreserveLCDText_SaveLayerFlag
1015 ? fProps.pixelGeometry()
1016 : kUnknown_SkPixelGeometry;
1017 const auto createInfo = SkDevice::CreateInfo(info, geo, fAllocator.get());
1018 // Use the original paint as a hint so that it includes the image filter
1019 newDevice = priorDevice->createDevice(createInfo, rec.fPaint);
1020 }
1021
1022 bool initBackdrop = (rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop;
1023 if (!newDevice) {
1024 // Either we weren't meant to allocate a full layer, or the full layer creation failed.
1025 // Using an explicit NoPixelsDevice lets us reflect what the layer state would have been
1026 // on success (or kFull_LayerStrategy) while squashing draw calls that target something that
1027 // doesn't exist.
1028 newDevice = sk_make_sp<SkNoPixelsDevice>(SkIRect::MakeWH(layerBounds.width(),
1029 layerBounds.height()),
1030 fProps, this->imageInfo().refColorSpace());
1031 initBackdrop = false;
1032 }
1033
1034 // Clip while the device coordinate space is the identity so it's easy to define the rect that
1035 // excludes the added padding pixels. This ensures they remain cleared to transparent black.
1036 if (!filters.empty()) {
1037 newDevice->clipRect(SkRect::Make(newDevice->devClipBounds().makeInset(1, 1)),
1038 SkClipOp::kIntersect, /*aa=*/false);
1039 }
1040
1041 // Configure device to match determined mapping for any image filters.
1042 // The setDeviceCoordinateSystem applies the prior device's global transform since
1043 // 'newLayerMapping' only defines the transforms between the two devices and it must be updated
1044 // to the global coordinate system.
1045 newDevice->setDeviceCoordinateSystem(
1046 priorDevice->deviceToGlobal() * SkM44(newLayerMapping.layerToDevice()),
1047 SkM44(newLayerMapping.deviceToLayer()) * priorDevice->globalToDevice(),
1048 SkM44(newLayerMapping.layerMatrix()),
1049 layerBounds.left(),
1050 layerBounds.top());
1051
1052 if (initBackdrop) {
1053 SkASSERT(!coverageOnly);
1054 SkPaint backdropPaint;
1055 FilterToSpan backdropAsSpan(rec.fBackdrop);
1056 // The new device was constructed to be compatible with 'filter', not necessarily
1057 // 'rec.fBackdrop', so allow DrawDeviceWithFilter to transform the prior device contents
1058 // if necessary to evaluate the backdrop filter. If no filters are involved, then the
1059 // devices differ by integer translations and are always compatible.
1060 bool scaleBackdrop = rec.fExperimentalBackdropScale != 1.0f;
1061 auto compat = (!filters.empty() || rec.fBackdrop || scaleBackdrop)
1062 ? DeviceCompatibleWithFilter::kUnknown : DeviceCompatibleWithFilter::kYes;
1063 // Using the color info of 'newDevice' is equivalent to using 'rec.fColorSpace'.
1064 this->internalDrawDeviceWithFilter(priorDevice, // src
1065 newDevice.get(), // dst
1066 backdropAsSpan,
1067 backdropPaint,
1068 compat,
1069 newDevice->imageInfo().colorInfo(),
1070 rec.fExperimentalBackdropScale);
1071 }
1072
1073 fMCRec->newLayer(std::move(newDevice), filters, restorePaint, coverageOnly);
1074 fQuickRejectBounds = this->computeDeviceClipBounds();
1075 }
1076
saveLayerAlphaf(const SkRect * bounds,float alpha)1077 int SkCanvas::saveLayerAlphaf(const SkRect* bounds, float alpha) {
1078 if (alpha >= 1.0f) {
1079 return this->saveLayer(bounds, nullptr);
1080 } else {
1081 SkPaint tmpPaint;
1082 tmpPaint.setAlphaf(alpha);
1083 return this->saveLayer(bounds, &tmpPaint);
1084 }
1085 }
1086
internalSaveBehind(const SkRect * localBounds)1087 void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
1088 SkDevice* device = this->topDevice();
1089
1090 // Map the local bounds into the top device's coordinate space (this is not
1091 // necessarily the full global CTM transform).
1092 SkIRect devBounds;
1093 if (localBounds) {
1094 SkRect tmp;
1095 device->localToDevice().mapRect(&tmp, *localBounds);
1096 if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
1097 devBounds.setEmpty();
1098 }
1099 } else {
1100 devBounds = device->devClipBounds();
1101 }
1102 if (devBounds.isEmpty()) {
1103 return;
1104 }
1105
1106 // This is getting the special image from the current device, which is then drawn into (both by
1107 // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
1108 // own device, we need to explicitly copy the back image contents so that its original content
1109 // is available when we splat it back later during restore.
1110 auto backImage = device->snapSpecial(devBounds, /* forceCopy= */ true);
1111 if (!backImage) {
1112 return;
1113 }
1114
1115 // we really need the save, so we can wack the fMCRec
1116 this->checkForDeferredSave();
1117
1118 fMCRec->fBackImage =
1119 std::make_unique<BackImage>(BackImage{std::move(backImage), devBounds.topLeft()});
1120
1121 SkPaint paint;
1122 paint.setBlendMode(SkBlendMode::kClear);
1123 this->drawClippedToSaveBehind(paint);
1124 }
1125
internalRestore()1126 void SkCanvas::internalRestore() {
1127 SkASSERT(!fMCStack.empty());
1128
1129 // now detach these from fMCRec so we can pop(). Gets freed after its drawn
1130 std::unique_ptr<Layer> layer = std::move(fMCRec->fLayer);
1131 std::unique_ptr<BackImage> backImage = std::move(fMCRec->fBackImage);
1132
1133 // now do the normal restore()
1134 fMCRec->~MCRec(); // balanced in save()
1135 fMCStack.pop_back();
1136 fMCRec = (MCRec*) fMCStack.back();
1137
1138 if (!fMCRec) {
1139 // This was the last record, restored during the destruction of the SkCanvas
1140 return;
1141 }
1142
1143 this->topDevice()->popClipStack();
1144 this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
1145
1146 if (backImage) {
1147 SkPaint paint;
1148 paint.setBlendMode(SkBlendMode::kDstOver);
1149 this->topDevice()->drawSpecial(backImage->fImage.get(),
1150 SkMatrix::Translate(backImage->fLoc),
1151 SkSamplingOptions(),
1152 paint);
1153 }
1154
1155 // Draw the layer's device contents into the now-current older device. We can't call public
1156 // draw functions since we don't want to record them.
1157 if (layer && !layer->fDevice->isNoPixelsDevice() && !layer->fDiscard) {
1158 layer->fDevice->setImmutable();
1159
1160 // Don't go through AutoLayerForImageFilter since device draws are so closely tied to
1161 // internalSaveLayer and internalRestore.
1162 if (this->predrawNotify()) {
1163 SkDevice* dstDev = this->topDevice();
1164 if (!layer->fImageFilters.empty()) {
1165 this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src
1166 dstDev, // dst
1167 layer->fImageFilters,
1168 layer->fPaint,
1169 DeviceCompatibleWithFilter::kYes,
1170 layer->fDevice->imageInfo().colorInfo(),
1171 /*scaleFactor=*/1.0f,
1172 layer->fIsCoverage);
1173 } else {
1174 // NOTE: We don't just call internalDrawDeviceWithFilter with a null filter
1175 // because we want to take advantage of overridden drawDevice functions for
1176 // document-based devices.
1177 SkASSERT(!layer->fIsCoverage);
1178 SkSamplingOptions sampling;
1179 dstDev->drawDevice(layer->fDevice.get(), sampling, layer->fPaint);
1180 }
1181 }
1182 }
1183
1184 // Reset the clip restriction if the restore went past the save point that had added it.
1185 if (this->getSaveCount() < fClipRestrictionSaveCount) {
1186 fClipRestrictionRect.setEmpty();
1187 fClipRestrictionSaveCount = -1;
1188 }
1189 // Update the quick-reject bounds in case the restore changed the top device or the
1190 // removed save record had included modifications to the clip stack.
1191 fQuickRejectBounds = this->computeDeviceClipBounds();
1192 this->validateClip();
1193 }
1194
makeSurface(const SkImageInfo & info,const SkSurfaceProps * props)1195 sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1196 if (nullptr == props) {
1197 props = &fProps;
1198 }
1199 return this->onNewSurface(info, *props);
1200 }
1201
onNewSurface(const SkImageInfo & info,const SkSurfaceProps & props)1202 sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1203 return this->rootDevice()->makeSurface(info, props);
1204 }
1205
imageInfo() const1206 SkImageInfo SkCanvas::imageInfo() const {
1207 return this->onImageInfo();
1208 }
1209
onImageInfo() const1210 SkImageInfo SkCanvas::onImageInfo() const {
1211 return this->rootDevice()->imageInfo();
1212 }
1213
getProps(SkSurfaceProps * props) const1214 bool SkCanvas::getProps(SkSurfaceProps* props) const {
1215 return this->onGetProps(props, /*top=*/false);
1216 }
1217
getBaseProps() const1218 SkSurfaceProps SkCanvas::getBaseProps() const {
1219 SkSurfaceProps props;
1220 this->onGetProps(&props, /*top=*/false);
1221 return props;
1222 }
1223
getTopProps() const1224 SkSurfaceProps SkCanvas::getTopProps() const {
1225 SkSurfaceProps props;
1226 this->onGetProps(&props, /*top=*/true);
1227 return props;
1228 }
1229
onGetProps(SkSurfaceProps * props,bool top) const1230 bool SkCanvas::onGetProps(SkSurfaceProps* props, bool top) const {
1231 if (props) {
1232 *props = top ? topDevice()->surfaceProps() : fProps;
1233 }
1234 return true;
1235 }
1236
peekPixels(SkPixmap * pmap)1237 bool SkCanvas::peekPixels(SkPixmap* pmap) {
1238 return this->onPeekPixels(pmap);
1239 }
1240
onPeekPixels(SkPixmap * pmap)1241 bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
1242 return this->rootDevice()->peekPixels(pmap);
1243 }
1244
accessTopLayerPixels(SkImageInfo * info,size_t * rowBytes,SkIPoint * origin)1245 void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1246 SkPixmap pmap;
1247 if (!this->onAccessTopLayerPixels(&pmap)) {
1248 return nullptr;
1249 }
1250 if (info) {
1251 *info = pmap.info();
1252 }
1253 if (rowBytes) {
1254 *rowBytes = pmap.rowBytes();
1255 }
1256 if (origin) {
1257 // If the caller requested the origin, they presumably are expecting the returned pixels to
1258 // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
1259 // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
1260 // instead of an origin, just don't expose the pixels in that case. Note that this means
1261 // that layers with complex coordinate spaces can still report their pixels if the caller
1262 // does not ask for the origin (e.g. just to dump its output to a file, etc).
1263 if (this->topDevice()->isPixelAlignedToGlobal()) {
1264 *origin = this->topDevice()->getOrigin();
1265 } else {
1266 return nullptr;
1267 }
1268 }
1269 return pmap.writable_addr();
1270 }
1271
onAccessTopLayerPixels(SkPixmap * pmap)1272 bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
1273 return this->topDevice()->accessPixels(pmap);
1274 }
1275
1276 /////////////////////////////////////////////////////////////////////////////
1277
translate(SkScalar dx,SkScalar dy)1278 void SkCanvas::translate(SkScalar dx, SkScalar dy) {
1279 if (dx || dy) {
1280 this->checkForDeferredSave();
1281 fMCRec->fMatrix.preTranslate(dx, dy);
1282
1283 this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
1284
1285 this->didTranslate(dx,dy);
1286 }
1287 }
1288
scale(SkScalar sx,SkScalar sy)1289 void SkCanvas::scale(SkScalar sx, SkScalar sy) {
1290 if (sx != 1 || sy != 1) {
1291 this->checkForDeferredSave();
1292 fMCRec->fMatrix.preScale(sx, sy);
1293
1294 this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
1295
1296 this->didScale(sx, sy);
1297 }
1298 }
1299
rotate(SkScalar degrees)1300 void SkCanvas::rotate(SkScalar degrees) {
1301 SkMatrix m;
1302 m.setRotate(degrees);
1303 this->concat(m);
1304 }
1305
rotate(SkScalar degrees,SkScalar px,SkScalar py)1306 void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1307 SkMatrix m;
1308 m.setRotate(degrees, px, py);
1309 this->concat(m);
1310 }
1311
skew(SkScalar sx,SkScalar sy)1312 void SkCanvas::skew(SkScalar sx, SkScalar sy) {
1313 SkMatrix m;
1314 m.setSkew(sx, sy);
1315 this->concat(m);
1316 }
1317
concat(const SkMatrix & matrix)1318 void SkCanvas::concat(const SkMatrix& matrix) {
1319 if (matrix.isIdentity()) {
1320 return;
1321 }
1322 this->concat(SkM44(matrix));
1323 }
1324
internalConcat44(const SkM44 & m)1325 void SkCanvas::internalConcat44(const SkM44& m) {
1326 this->checkForDeferredSave();
1327
1328 fMCRec->fMatrix.preConcat(m);
1329
1330 this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
1331 }
1332
concat(const SkM44 & m)1333 void SkCanvas::concat(const SkM44& m) {
1334 this->internalConcat44(m);
1335 // notify subclasses
1336 this->didConcat44(m);
1337 }
1338
internalSetMatrix(const SkM44 & m)1339 void SkCanvas::internalSetMatrix(const SkM44& m) {
1340 fMCRec->fMatrix = m;
1341
1342 this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
1343 }
1344
setMatrix(const SkMatrix & matrix)1345 void SkCanvas::setMatrix(const SkMatrix& matrix) {
1346 this->setMatrix(SkM44(matrix));
1347 }
1348
setMatrix(const SkM44 & m)1349 void SkCanvas::setMatrix(const SkM44& m) {
1350 this->checkForDeferredSave();
1351 this->internalSetMatrix(m);
1352 this->didSetM44(m);
1353 }
1354
resetMatrix()1355 void SkCanvas::resetMatrix() {
1356 this->setMatrix(SkM44());
1357 }
1358
1359 //////////////////////////////////////////////////////////////////////////////
1360
clipRect(const SkRect & rect,SkClipOp op,bool doAA)1361 void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
1362 if (!rect.isFinite()) {
1363 return;
1364 }
1365 this->checkForDeferredSave();
1366 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1367 this->onClipRect(rect.makeSorted(), op, edgeStyle);
1368 }
1369
onClipRect(const SkRect & rect,SkClipOp op,ClipEdgeStyle edgeStyle)1370 void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
1371 SkASSERT(rect.isSorted());
1372 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1373
1374 AutoUpdateQRBounds aqr(this);
1375 this->topDevice()->clipRect(rect, op, isAA);
1376 }
1377
androidFramework_setDeviceClipRestriction(const SkIRect & rect)1378 void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1379 // The device clip restriction is a surface-space rectangular intersection that cannot be
1380 // drawn outside of. The rectangle is remembered so that subsequent resetClip calls still
1381 // respect the restriction. Other than clip resetting, all clip operations restrict the set
1382 // of renderable pixels, so once set, the restriction will be respected until the canvas
1383 // save stack is restored past the point this function was invoked. Unfortunately, the current
1384 // implementation relies on the clip stack of the underyling SkDevices, which leads to some
1385 // awkward behavioral interactions (see skbug.com/12252).
1386 //
1387 // Namely, a canvas restore() could undo the clip restriction's rect, and if
1388 // setDeviceClipRestriction were called at a nested save level, there's no way to undo just the
1389 // prior restriction and re-apply the new one. It also only makes sense to apply to the base
1390 // device; any other device for a saved layer will be clipped back to the base device during its
1391 // matched restore. As such, we:
1392 // - Remember the save count that added the clip restriction and reset the rect to empty when
1393 // we've restored past that point to keep our state in sync with the device's clip stack.
1394 // - We assert that we're on the base device when this is invoked.
1395 // - We assert that setDeviceClipRestriction() is only called when there was no prior
1396 // restriction (cannot re-restrict, and prior state must have been reset by restoring the
1397 // canvas state).
1398 // - Historically, the empty rect would reset the clip restriction but it only could do so
1399 // partially since the device's clips wasn't adjusted. Resetting is now handled
1400 // automatically via SkCanvas::restore(), so empty input rects are skipped.
1401 SkASSERT(this->topDevice() == this->rootDevice()); // shouldn't be in a nested layer
1402 // and shouldn't already have a restriction
1403 SkASSERT(fClipRestrictionSaveCount < 0 && fClipRestrictionRect.isEmpty());
1404
1405 if (fClipRestrictionSaveCount < 0 && !rect.isEmpty()) {
1406 fClipRestrictionRect = rect;
1407 fClipRestrictionSaveCount = this->getSaveCount();
1408
1409 // A non-empty clip restriction immediately applies an intersection op (ignoring the ctm).
1410 // so we have to resolve the save.
1411 this->checkForDeferredSave();
1412 AutoUpdateQRBounds aqr(this);
1413 // Use clipRegion() since that operates in canvas-space, whereas clipRect() would apply the
1414 // device's current transform first.
1415 this->topDevice()->clipRegion(SkRegion(rect), SkClipOp::kIntersect);
1416 }
1417 }
1418
internal_private_resetClip()1419 void SkCanvas::internal_private_resetClip() {
1420 this->checkForDeferredSave();
1421 this->onResetClip();
1422 }
1423
onResetClip()1424 void SkCanvas::onResetClip() {
1425 SkIRect deviceRestriction = this->topDevice()->imageInfo().bounds();
1426 if (fClipRestrictionSaveCount >= 0 && this->topDevice() == this->rootDevice()) {
1427 // Respect the device clip restriction when resetting the clip if we're on the base device.
1428 // If we're not on the base device, then the "reset" applies to the top device's clip stack,
1429 // and the clip restriction will be respected automatically during a restore of the layer.
1430 if (!deviceRestriction.intersect(fClipRestrictionRect)) {
1431 deviceRestriction = SkIRect::MakeEmpty();
1432 }
1433 }
1434
1435 AutoUpdateQRBounds aqr(this);
1436 this->topDevice()->replaceClip(deviceRestriction);
1437 }
1438
clipRRect(const SkRRect & rrect,SkClipOp op,bool doAA)1439 void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
1440 this->checkForDeferredSave();
1441 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1442 if (rrect.isRect()) {
1443 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1444 } else {
1445 this->onClipRRect(rrect, op, edgeStyle);
1446 }
1447 }
1448
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)1449 void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
1450 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1451
1452 AutoUpdateQRBounds aqr(this);
1453 this->topDevice()->clipRRect(rrect, op, isAA);
1454 }
1455
clipPath(const SkPath & path,SkClipOp op,bool doAA)1456 void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
1457 this->checkForDeferredSave();
1458 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1459
1460 if (!path.isInverseFillType() && fMCRec->fMatrix.asM33().rectStaysRect()) {
1461 SkRect r;
1462 if (path.isRect(&r)) {
1463 this->onClipRect(r, op, edgeStyle);
1464 return;
1465 }
1466 SkRRect rrect;
1467 if (path.isOval(&r)) {
1468 rrect.setOval(r);
1469 this->onClipRRect(rrect, op, edgeStyle);
1470 return;
1471 }
1472 if (path.isRRect(&rrect)) {
1473 this->onClipRRect(rrect, op, edgeStyle);
1474 return;
1475 }
1476 }
1477
1478 this->onClipPath(path, op, edgeStyle);
1479 }
1480
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)1481 void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
1482 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1483
1484 AutoUpdateQRBounds aqr(this);
1485 this->topDevice()->clipPath(path, op, isAA);
1486 }
1487
clipShader(sk_sp<SkShader> sh,SkClipOp op)1488 void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1489 if (sh) {
1490 if (sh->isOpaque()) {
1491 if (op == SkClipOp::kIntersect) {
1492 // we don't occlude anything, so skip this call
1493 } else {
1494 SkASSERT(op == SkClipOp::kDifference);
1495 // we occlude everything, so set the clip to empty
1496 this->clipRect({0,0,0,0});
1497 }
1498 } else {
1499 this->checkForDeferredSave();
1500 this->onClipShader(std::move(sh), op);
1501 }
1502 }
1503 }
1504
onClipShader(sk_sp<SkShader> sh,SkClipOp op)1505 void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
1506 AutoUpdateQRBounds aqr(this);
1507 this->topDevice()->clipShader(sh, op);
1508 }
1509
clipRegion(const SkRegion & rgn,SkClipOp op)1510 void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
1511 this->checkForDeferredSave();
1512 this->onClipRegion(rgn, op);
1513 }
1514
onClipRegion(const SkRegion & rgn,SkClipOp op)1515 void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
1516 AutoUpdateQRBounds aqr(this);
1517 this->topDevice()->clipRegion(rgn, op);
1518 }
1519
validateClip() const1520 void SkCanvas::validateClip() const {
1521 #ifdef SK_DEBUG
1522 SkRect tmp = this->computeDeviceClipBounds();
1523 if (this->isClipEmpty()) {
1524 SkASSERT(fQuickRejectBounds.isEmpty());
1525 } else {
1526 SkASSERT(tmp == fQuickRejectBounds);
1527 }
1528 #endif
1529 }
1530
androidFramework_isClipAA() const1531 bool SkCanvas::androidFramework_isClipAA() const {
1532 return this->topDevice()->isClipAntiAliased();
1533 }
1534
temporary_internal_getRgnClip(SkRegion * rgn)1535 void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1536 rgn->setEmpty();
1537 SkDevice* device = this->topDevice();
1538 if (device && device->isPixelAlignedToGlobal()) {
1539 device->android_utils_clipAsRgn(rgn);
1540 SkIPoint origin = device->getOrigin();
1541 if (origin.x() | origin.y()) {
1542 rgn->translate(origin.x(), origin.y());
1543 }
1544 }
1545 }
1546
1547 ///////////////////////////////////////////////////////////////////////////////
1548
isClipEmpty() const1549 bool SkCanvas::isClipEmpty() const {
1550 return this->topDevice()->isClipEmpty();
1551 }
1552
isClipRect() const1553 bool SkCanvas::isClipRect() const {
1554 return this->topDevice()->isClipRect();
1555 }
1556
quickReject(const SkRect & src) const1557 bool SkCanvas::quickReject(const SkRect& src) const {
1558 #ifdef SK_DEBUG
1559 // Verify that fQuickRejectBounds are set properly.
1560 this->validateClip();
1561 #endif
1562
1563 SkRect devRect = SkMatrixPriv::MapRect(fMCRec->fMatrix, src);
1564 return !devRect.isFinite() || !devRect.intersects(fQuickRejectBounds);
1565 }
1566
quickReject(const SkPath & path) const1567 bool SkCanvas::quickReject(const SkPath& path) const {
1568 return path.isEmpty() || this->quickReject(path.getBounds());
1569 }
1570
internalQuickReject(const SkRect & bounds,const SkPaint & paint,const SkMatrix * matrix)1571 bool SkCanvas::internalQuickReject(const SkRect& bounds, const SkPaint& paint,
1572 const SkMatrix* matrix) {
1573 if (!bounds.isFinite() || paint.nothingToDraw()) {
1574 return true;
1575 }
1576
1577 if (paint.canComputeFastBounds()) {
1578 SkRect tmp = matrix ? matrix->mapRect(bounds) : bounds;
1579 return this->quickReject(paint.computeFastBounds(tmp, &tmp));
1580 }
1581
1582 return false;
1583 }
1584
1585
getLocalClipBounds() const1586 SkRect SkCanvas::getLocalClipBounds() const {
1587 SkIRect ibounds = this->getDeviceClipBounds();
1588 if (ibounds.isEmpty()) {
1589 return SkRect::MakeEmpty();
1590 }
1591
1592 SkMatrix inverse;
1593 // if we can't invert the CTM, we can't return local clip bounds
1594 if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
1595 return SkRect::MakeEmpty();
1596 }
1597
1598 SkRect bounds;
1599 // adjust it outwards in case we are antialiasing
1600 const int margin = 1;
1601
1602 SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
1603 inverse.mapRect(&bounds, r);
1604 return bounds;
1605 }
1606
getDeviceClipBounds() const1607 SkIRect SkCanvas::getDeviceClipBounds() const {
1608 return this->computeDeviceClipBounds(/*outsetForAA=*/false).roundOut();
1609 }
1610
computeDeviceClipBounds(bool outsetForAA) const1611 SkRect SkCanvas::computeDeviceClipBounds(bool outsetForAA) const {
1612 const SkDevice* dev = this->topDevice();
1613 if (dev->isClipEmpty()) {
1614 return SkRect::MakeEmpty();
1615 } else {
1616 SkRect devClipBounds =
1617 SkMatrixPriv::MapRect(dev->deviceToGlobal(), SkRect::Make(dev->devClipBounds()));
1618 if (outsetForAA) {
1619 // Expand bounds out by 1 in case we are anti-aliasing. We store the
1620 // bounds as floats to enable a faster quick reject implementation.
1621 devClipBounds.outset(1.f, 1.f);
1622 }
1623 return devClipBounds;
1624 }
1625 }
1626
1627 ///////////////////////////////////////////////////////////////////////
1628
getTotalMatrix() const1629 SkMatrix SkCanvas::getTotalMatrix() const {
1630 return fMCRec->fMatrix.asM33();
1631 }
1632
getLocalToDevice() const1633 SkM44 SkCanvas::getLocalToDevice() const {
1634 return fMCRec->fMatrix;
1635 }
1636
recordingContext() const1637 GrRecordingContext* SkCanvas::recordingContext() const {
1638 return this->topDevice()->recordingContext();
1639 }
1640
recorder() const1641 skgpu::graphite::Recorder* SkCanvas::recorder() const {
1642 return this->topDevice()->recorder();
1643 }
1644
drawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)1645 void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1646 const SkPaint& paint) {
1647 TRACE_EVENT0("skia", TRACE_FUNC);
1648 if (outer.isEmpty()) {
1649 return;
1650 }
1651 if (inner.isEmpty()) {
1652 this->drawRRect(outer, paint);
1653 return;
1654 }
1655
1656 // We don't have this method (yet), but technically this is what we should
1657 // be able to return ...
1658 // if (!outer.contains(inner))) {
1659 //
1660 // For now at least check for containment of bounds
1661 if (!outer.getBounds().contains(inner.getBounds())) {
1662 return;
1663 }
1664
1665 this->onDrawDRRect(outer, inner, paint);
1666 }
1667
drawPaint(const SkPaint & paint)1668 void SkCanvas::drawPaint(const SkPaint& paint) {
1669 TRACE_EVENT0("skia", TRACE_FUNC);
1670 this->onDrawPaint(paint);
1671 }
1672
drawRect(const SkRect & r,const SkPaint & paint)1673 void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1674 TRACE_EVENT0("skia", TRACE_FUNC);
1675 // To avoid redundant logic in our culling code and various backends, we always sort rects
1676 // before passing them along.
1677 this->onDrawRect(r.makeSorted(), paint);
1678 }
1679
drawClippedToSaveBehind(const SkPaint & paint)1680 void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
1681 TRACE_EVENT0("skia", TRACE_FUNC);
1682 this->onDrawBehind(paint);
1683 }
1684
drawRegion(const SkRegion & region,const SkPaint & paint)1685 void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1686 TRACE_EVENT0("skia", TRACE_FUNC);
1687 if (region.isEmpty()) {
1688 return;
1689 }
1690
1691 if (region.isRect()) {
1692 return this->drawIRect(region.getBounds(), paint);
1693 }
1694
1695 this->onDrawRegion(region, paint);
1696 }
1697
drawOval(const SkRect & r,const SkPaint & paint)1698 void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1699 TRACE_EVENT0("skia", TRACE_FUNC);
1700 // To avoid redundant logic in our culling code and various backends, we always sort rects
1701 // before passing them along.
1702 this->onDrawOval(r.makeSorted(), paint);
1703 }
1704
drawRRect(const SkRRect & rrect,const SkPaint & paint)1705 void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1706 TRACE_EVENT0("skia", TRACE_FUNC);
1707 this->onDrawRRect(rrect, paint);
1708 }
1709
drawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)1710 void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1711 TRACE_EVENT0("skia", TRACE_FUNC);
1712 this->onDrawPoints(mode, count, pts, paint);
1713 }
1714
drawVertices(const sk_sp<SkVertices> & vertices,SkBlendMode mode,const SkPaint & paint)1715 void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
1716 const SkPaint& paint) {
1717 this->drawVertices(vertices.get(), mode, paint);
1718 }
1719
drawVertices(const SkVertices * vertices,SkBlendMode mode,const SkPaint & paint)1720 void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
1721 TRACE_EVENT0("skia", TRACE_FUNC);
1722 RETURN_ON_NULL(vertices);
1723
1724 // We expect fans to be converted to triangles when building or deserializing SkVertices.
1725 SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);
1726
1727 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
1728 // Preserve legacy behavior for Android: ignore the SkShader if there are no texCoords present
1729 if (paint.getShader() && !vertices->priv().hasTexCoords()) {
1730 SkPaint noShaderPaint(paint);
1731 noShaderPaint.setShader(nullptr);
1732 this->onDrawVerticesObject(vertices, mode, noShaderPaint);
1733 return;
1734 }
1735 #endif
1736 this->onDrawVerticesObject(vertices, mode, paint);
1737 }
1738
drawMesh(const SkMesh & mesh,sk_sp<SkBlender> blender,const SkPaint & paint)1739 void SkCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
1740 TRACE_EVENT0("skia", TRACE_FUNC);
1741 if (!blender) {
1742 blender = SkBlender::Mode(SkBlendMode::kModulate);
1743 }
1744 this->onDrawMesh(mesh, std::move(blender), paint);
1745 }
1746
drawPath(const SkPath & path,const SkPaint & paint)1747 void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1748 TRACE_EVENT0("skia", TRACE_FUNC);
1749 this->onDrawPath(path, paint);
1750 }
1751
1752 // Returns true if the rect can be "filled" : non-empty and finite
fillable(const SkRect & r)1753 static bool fillable(const SkRect& r) {
1754 SkScalar w = r.width();
1755 SkScalar h = r.height();
1756 return SkIsFinite(w, h) && w > 0 && h > 0;
1757 }
1758
clean_paint_for_lattice(const SkPaint * paint)1759 static SkPaint clean_paint_for_lattice(const SkPaint* paint) {
1760 SkPaint cleaned;
1761 if (paint) {
1762 cleaned = *paint;
1763 cleaned.setMaskFilter(nullptr);
1764 cleaned.setAntiAlias(false);
1765 }
1766 return cleaned;
1767 }
1768
drawImageNine(const SkImage * image,const SkIRect & center,const SkRect & dst,SkFilterMode filter,const SkPaint * paint)1769 void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1770 SkFilterMode filter, const SkPaint* paint) {
1771 RETURN_ON_NULL(image);
1772
1773 const int xdivs[] = {center.fLeft, center.fRight};
1774 const int ydivs[] = {center.fTop, center.fBottom};
1775
1776 Lattice lat;
1777 lat.fXDivs = xdivs;
1778 lat.fYDivs = ydivs;
1779 lat.fRectTypes = nullptr;
1780 lat.fXCount = lat.fYCount = 2;
1781 lat.fBounds = nullptr;
1782 lat.fColors = nullptr;
1783 this->drawImageLattice(image, lat, dst, filter, paint);
1784 }
1785
drawImageLattice(const SkImage * image,const Lattice & lattice,const SkRect & dst,SkFilterMode filter,const SkPaint * paint)1786 void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1787 SkFilterMode filter, const SkPaint* paint) {
1788 TRACE_EVENT0("skia", TRACE_FUNC);
1789 RETURN_ON_NULL(image);
1790 if (dst.isEmpty()) {
1791 return;
1792 }
1793
1794 SkIRect bounds;
1795 Lattice latticePlusBounds = lattice;
1796 if (!latticePlusBounds.fBounds) {
1797 bounds = SkIRect::MakeWH(image->width(), image->height());
1798 latticePlusBounds.fBounds = &bounds;
1799 }
1800
1801 SkPaint latticePaint = clean_paint_for_lattice(paint);
1802 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1803 this->onDrawImageLattice2(image, latticePlusBounds, dst, filter, &latticePaint);
1804 } else {
1805 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst,
1806 SkSamplingOptions(filter), &latticePaint, kStrict_SrcRectConstraint);
1807 }
1808 }
1809
drawAtlas(const SkImage * atlas,const SkRSXform xform[],const SkRect tex[],const SkColor colors[],int count,SkBlendMode mode,const SkSamplingOptions & sampling,const SkRect * cull,const SkPaint * paint)1810 void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1811 const SkColor colors[], int count, SkBlendMode mode,
1812 const SkSamplingOptions& sampling, const SkRect* cull,
1813 const SkPaint* paint) {
1814 TRACE_EVENT0("skia", TRACE_FUNC);
1815 RETURN_ON_NULL(atlas);
1816 if (count <= 0) {
1817 return;
1818 }
1819 SkASSERT(atlas);
1820 SkASSERT(tex);
1821 this->onDrawAtlas2(atlas, xform, tex, colors, count, mode, sampling, cull, paint);
1822 }
1823
drawAnnotation(const SkRect & rect,const char key[],SkData * value)1824 void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
1825 TRACE_EVENT0("skia", TRACE_FUNC);
1826 if (key) {
1827 this->onDrawAnnotation(rect, key, value);
1828 }
1829 }
1830
private_draw_shadow_rec(const SkPath & path,const SkDrawShadowRec & rec)1831 void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
1832 TRACE_EVENT0("skia", TRACE_FUNC);
1833 this->onDrawShadowRec(path, rec);
1834 }
1835
onDrawShadowRec(const SkPath & path,const SkDrawShadowRec & rec)1836 void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
1837 // We don't test quickReject because the shadow outsets the path's bounds.
1838 // TODO(michaelludwig): Is it worth calling SkDrawShadowMetrics::GetLocalBounds here?
1839 if (!this->predrawNotify()) {
1840 return;
1841 }
1842 this->topDevice()->drawShadow(path, rec);
1843 }
1844
experimental_DrawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)1845 void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
1846 QuadAAFlags aaFlags, const SkColor4f& color,
1847 SkBlendMode mode) {
1848 TRACE_EVENT0("skia", TRACE_FUNC);
1849 // Make sure the rect is sorted before passing it along
1850 this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
1851 }
1852
experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[],int cnt,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)1853 void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
1854 const SkPoint dstClips[],
1855 const SkMatrix preViewMatrices[],
1856 const SkSamplingOptions& sampling,
1857 const SkPaint* paint,
1858 SrcRectConstraint constraint) {
1859 TRACE_EVENT0("skia", TRACE_FUNC);
1860 // Route single, rectangular quads to drawImageRect() to take advantage of image filter
1861 // optimizations that avoid a layer.
1862 if (paint && paint->getImageFilter() && cnt == 1) {
1863 const auto& entry = imageSet[0];
1864 // If the preViewMatrix is skipped or a positive-scale + translate matrix, we can apply it
1865 // to the entry's dstRect w/o changing output behavior.
1866 const bool canMapDstRect = entry.fMatrixIndex < 0 ||
1867 (preViewMatrices[entry.fMatrixIndex].isScaleTranslate() &&
1868 preViewMatrices[entry.fMatrixIndex].getScaleX() > 0.f &&
1869 preViewMatrices[entry.fMatrixIndex].getScaleY() > 0.f);
1870 if (!entry.fHasClip && canMapDstRect) {
1871 SkRect dst = entry.fDstRect;
1872 if (entry.fMatrixIndex >= 0) {
1873 preViewMatrices[entry.fMatrixIndex].mapRect(&dst);
1874 }
1875 this->drawImageRect(entry.fImage.get(), entry.fSrcRect, dst,
1876 sampling, paint, constraint);
1877 return;
1878 } // Else the entry is doing more than can be represented by drawImageRect
1879 } // Else no filter, or many entries that should be filtered together
1880 this->onDrawEdgeAAImageSet2(imageSet, cnt, dstClips, preViewMatrices, sampling, paint,
1881 constraint);
1882 }
1883
1884 //////////////////////////////////////////////////////////////////////////////
1885 // These are the virtual drawing methods
1886 //////////////////////////////////////////////////////////////////////////////
1887
onDiscard()1888 void SkCanvas::onDiscard() {
1889 if (fSurfaceBase) {
1890 sk_ignore_unused_variable(fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode));
1891 }
1892 }
1893
onDrawPaint(const SkPaint & paint)1894 void SkCanvas::onDrawPaint(const SkPaint& paint) {
1895 this->internalDrawPaint(paint);
1896 }
1897
internalDrawPaint(const SkPaint & paint)1898 void SkCanvas::internalDrawPaint(const SkPaint& paint) {
1899 // drawPaint does not call internalQuickReject() because computing its geometry is not free
1900 // (see getLocalClipBounds(), and the two conditions below are sufficient.
1901 if (paint.nothingToDraw() || this->isClipEmpty()) {
1902 return;
1903 }
1904
1905 auto layer = this->aboutToDraw(paint, nullptr, PredrawFlags::kCheckForOverwrite);
1906 if (layer) {
1907 this->topDevice()->drawPaint(layer->paint());
1908 }
1909 }
1910
onDrawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)1911 void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1912 const SkPaint& paint) {
1913 if ((long)count <= 0 || paint.nothingToDraw()) {
1914 return;
1915 }
1916 SkASSERT(pts != nullptr);
1917
1918 SkRect bounds;
1919 // Compute bounds from points (common for drawing a single line)
1920 if (count == 2) {
1921 bounds.set(pts[0], pts[1]);
1922 } else {
1923 bounds.setBounds(pts, SkToInt(count));
1924 }
1925
1926 // Enforce paint style matches implicit behavior of drawPoints
1927 SkPaint strokePaint = paint;
1928 strokePaint.setStyle(SkPaint::kStroke_Style);
1929 if (this->internalQuickReject(bounds, strokePaint)) {
1930 return;
1931 }
1932
1933 auto layer = this->aboutToDraw(strokePaint, &bounds);
1934 if (layer) {
1935 this->topDevice()->drawPoints(mode, count, pts, layer->paint());
1936 }
1937 }
1938
can_attempt_blurred_rrect_draw(const SkPaint & paint)1939 static const SkBlurMaskFilterImpl* can_attempt_blurred_rrect_draw(const SkPaint& paint) {
1940 if (paint.getPathEffect()) {
1941 return nullptr;
1942 }
1943
1944 // TODO: Once stroke-and-fill goes away, we can check the paint's style directly.
1945 if (SkStrokeRec(paint).getStyle() != SkStrokeRec::kFill_Style) {
1946 return nullptr;
1947 }
1948
1949 const SkMaskFilterBase* maskFilter = as_MFB(paint.getMaskFilter());
1950 if (!maskFilter || maskFilter->type() != SkMaskFilterBase::Type::kBlur) {
1951 return nullptr;
1952 }
1953
1954 const SkBlurMaskFilterImpl* blurMaskFilter =
1955 static_cast<const SkBlurMaskFilterImpl*>(maskFilter);
1956 if (blurMaskFilter->blurStyle() != kNormal_SkBlurStyle) {
1957 return nullptr;
1958 }
1959
1960 return blurMaskFilter;
1961 }
1962
attemptBlurredRRectDraw(const SkRRect & rrect,const SkPaint & paint,SkEnumBitMask<PredrawFlags> flags)1963 std::optional<AutoLayerForImageFilter> SkCanvas::attemptBlurredRRectDraw(
1964 const SkRRect& rrect, const SkPaint& paint, SkEnumBitMask<PredrawFlags> flags) {
1965 SkASSERT(!(flags & PredrawFlags::kSkipMaskFilterAutoLayer));
1966 const SkRect& bounds = rrect.getBounds();
1967
1968 if (!this->topDevice()->useDrawCoverageMaskForMaskFilters()) {
1969 // Regular draw in the legacy mask filter case.
1970 return this->aboutToDraw(paint, &bounds, flags);
1971 }
1972
1973 if (!this->getTotalMatrix().isSimilarity()) {
1974 // TODO: If the CTM does more than just translation, rotation, and uniform scale, then the
1975 // results of analytic blurring will be different than mask filter blurring. Skip the
1976 // specialized path in this case.
1977 return this->aboutToDraw(paint, &bounds, flags);
1978 }
1979
1980 const SkBlurMaskFilterImpl* blurMaskFilter = can_attempt_blurred_rrect_draw(paint);
1981 if (!blurMaskFilter) {
1982 // Can't attempt a specialized blurred draw, so do a regular draw.
1983 return this->aboutToDraw(paint, &bounds, flags);
1984 }
1985
1986 auto layer = this->aboutToDraw(paint, &bounds, flags | PredrawFlags::kSkipMaskFilterAutoLayer);
1987 if (!layer) {
1988 // predrawNotify failed.
1989 return std::nullopt;
1990 }
1991
1992 const float deviceSigma = blurMaskFilter->computeXformedSigma(this->getTotalMatrix());
1993 if (this->topDevice()->drawBlurredRRect(rrect, layer->paint(), deviceSigma)) {
1994 // Analytic draw was successful.
1995 return std::nullopt;
1996 }
1997
1998 // Fall back on a regular draw, adding any mask filter layer we skipped earlier. We know the
1999 // paint has a mask filter here, otherwise we would have failed the can_attempt check above.
2000 layer->addMaskFilterLayer(&bounds);
2001 return layer;
2002 }
2003
onDrawRect(const SkRect & r,const SkPaint & paint)2004 void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
2005 SkASSERT(r.isSorted());
2006 if (this->internalQuickReject(r, paint)) {
2007 return;
2008 }
2009
2010 // Returns a layer if a blurred draw is not applicable or was unsuccessful.
2011 std::optional<AutoLayerForImageFilter> layer = this->attemptBlurredRRectDraw(
2012 SkRRect::MakeRect(r), paint, PredrawFlags::kCheckForOverwrite);
2013
2014 if (layer) {
2015 this->topDevice()->drawRect(r, layer->paint());
2016 }
2017 }
2018
onDrawRegion(const SkRegion & region,const SkPaint & paint)2019 void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2020 const SkRect bounds = SkRect::Make(region.getBounds());
2021 if (this->internalQuickReject(bounds, paint)) {
2022 return;
2023 }
2024
2025 auto layer = this->aboutToDraw(paint, &bounds);
2026 if (layer) {
2027 this->topDevice()->drawRegion(region, layer->paint());
2028 }
2029 }
2030
onDrawBehind(const SkPaint & paint)2031 void SkCanvas::onDrawBehind(const SkPaint& paint) {
2032 SkDevice* dev = this->topDevice();
2033 if (!dev) {
2034 return;
2035 }
2036
2037 SkIRect bounds;
2038 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
2039 for (;;) {
2040 const MCRec* rec = (const MCRec*)iter.prev();
2041 if (!rec) {
2042 return; // no backimages, so nothing to draw
2043 }
2044 if (rec->fBackImage) {
2045 // drawBehind should only have been called when the saveBehind record is active;
2046 // if this fails, it means a real saveLayer was made w/o being restored first.
2047 SkASSERT(dev == rec->fDevice);
2048 bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
2049 rec->fBackImage->fImage->width(),
2050 rec->fBackImage->fImage->height());
2051 break;
2052 }
2053 }
2054
2055 // The backimage location (and thus bounds) were defined in the device's space, so mark it
2056 // as a clip. We use a clip instead of just drawing a rect in case the paint has an image
2057 // filter on it (which is applied before any auto-layer so the filter is clipped).
2058 dev->pushClipStack();
2059 {
2060 // We also have to temporarily whack the device matrix since clipRegion is affected by the
2061 // global-to-device matrix and clipRect is affected by the local-to-device.
2062 SkAutoDeviceTransformRestore adtr(dev, SkMatrix::I());
2063 dev->clipRect(SkRect::Make(bounds), SkClipOp::kIntersect, /* aa */ false);
2064 // ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly.
2065 }
2066
2067 auto layer = this->aboutToDraw(paint);
2068 if (layer) {
2069 this->topDevice()->drawPaint(layer->paint());
2070 }
2071
2072 dev->popClipStack();
2073 }
2074
onDrawOval(const SkRect & oval,const SkPaint & paint)2075 void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
2076 SkASSERT(oval.isSorted());
2077 if (this->internalQuickReject(oval, paint)) {
2078 return;
2079 }
2080
2081 // Returns a layer if a blurred draw is not applicable or was unsuccessful.
2082 std::optional<AutoLayerForImageFilter> layer =
2083 this->attemptBlurredRRectDraw(SkRRect::MakeOval(oval), paint, PredrawFlags::kNone);
2084
2085 if (layer) {
2086 this->topDevice()->drawOval(oval, layer->paint());
2087 }
2088 }
2089
onDrawArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)2090 void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2091 SkScalar sweepAngle, bool useCenter,
2092 const SkPaint& paint) {
2093 SkASSERT(oval.isSorted());
2094 if (this->internalQuickReject(oval, paint)) {
2095 return;
2096 }
2097
2098 auto layer = this->aboutToDraw(paint, &oval);
2099 if (layer) {
2100 this->topDevice()->drawArc(SkArc::Make(oval, startAngle, sweepAngle, useCenter),
2101 layer->paint());
2102 }
2103 }
2104
onDrawRRect(const SkRRect & rrect,const SkPaint & paint)2105 void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
2106 const SkRect& bounds = rrect.getBounds();
2107
2108 // Delegating to simpler draw operations
2109 if (rrect.isRect()) {
2110 // call the non-virtual version
2111 this->SkCanvas::drawRect(bounds, paint);
2112 return;
2113 } else if (rrect.isOval()) {
2114 // call the non-virtual version
2115 this->SkCanvas::drawOval(bounds, paint);
2116 return;
2117 }
2118
2119 if (this->internalQuickReject(bounds, paint)) {
2120 return;
2121 }
2122
2123 // Returns a layer if a blurred draw is not applicable or was unsuccessful.
2124 std::optional<AutoLayerForImageFilter> layer =
2125 this->attemptBlurredRRectDraw(rrect, paint, PredrawFlags::kNone);
2126
2127 if (layer) {
2128 this->topDevice()->drawRRect(rrect, layer->paint());
2129 }
2130 }
2131
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)2132 void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
2133 const SkRect& bounds = outer.getBounds();
2134 if (this->internalQuickReject(bounds, paint)) {
2135 return;
2136 }
2137
2138 auto layer = this->aboutToDraw(paint, &bounds);
2139 if (layer) {
2140 this->topDevice()->drawDRRect(outer, inner, layer->paint());
2141 }
2142 }
2143
onDrawPath(const SkPath & path,const SkPaint & paint)2144 void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
2145 if (!path.isFinite()) {
2146 return;
2147 }
2148
2149 const SkRect& pathBounds = path.getBounds();
2150 if (!path.isInverseFillType() && this->internalQuickReject(pathBounds, paint)) {
2151 return;
2152 }
2153 if (path.isInverseFillType() && pathBounds.width() <= 0 && pathBounds.height() <= 0) {
2154 this->internalDrawPaint(paint);
2155 return;
2156 }
2157
2158 auto layer = this->aboutToDraw(paint, path.isInverseFillType() ? nullptr : &pathBounds);
2159 if (layer) {
2160 this->topDevice()->drawPath(path, layer->paint());
2161 }
2162 }
2163
2164 // Clean-up the paint to match the drawing semantics for drawImage et al. (skbug.com/7804).
clean_paint_for_drawImage(const SkPaint * paint)2165 static SkPaint clean_paint_for_drawImage(const SkPaint* paint) {
2166 SkPaint cleaned;
2167 if (paint) {
2168 cleaned = *paint;
2169 cleaned.setStyle(SkPaint::kFill_Style);
2170 cleaned.setPathEffect(nullptr);
2171 }
2172 return cleaned;
2173 }
2174
2175 // drawVertices fills triangles and ignores mask filter and path effect,
2176 // so canonicalize the paint before checking quick reject.
clean_paint_for_drawVertices(SkPaint paint)2177 static SkPaint clean_paint_for_drawVertices(SkPaint paint) {
2178 paint.setStyle(SkPaint::kFill_Style);
2179 paint.setMaskFilter(nullptr);
2180 paint.setPathEffect(nullptr);
2181 return paint;
2182 }
2183
2184 // TODO: Delete this since it is no longer used
onDrawImage2(const SkImage * image,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint)2185 void SkCanvas::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
2186 const SkSamplingOptions& sampling, const SkPaint* paint) {
2187 SkUNREACHABLE;
2188 }
2189
clean_sampling_for_constraint(const SkSamplingOptions & sampling,SkCanvas::SrcRectConstraint constraint)2190 static SkSamplingOptions clean_sampling_for_constraint(
2191 const SkSamplingOptions& sampling,
2192 SkCanvas::SrcRectConstraint constraint) {
2193 if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
2194 if (sampling.mipmap != SkMipmapMode::kNone) {
2195 return SkSamplingOptions(sampling.filter);
2196 }
2197 if (sampling.isAniso()) {
2198 return SkSamplingOptions(SkFilterMode::kLinear);
2199 }
2200 }
2201 return sampling;
2202 }
2203
onDrawImageRect2(const SkImage * image,const SkRect & src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)2204 void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
2205 const SkSamplingOptions& sampling, const SkPaint* paint,
2206 SrcRectConstraint constraint) {
2207 SkPaint realPaint = clean_paint_for_drawImage(paint);
2208 SkSamplingOptions realSampling = clean_sampling_for_constraint(sampling, constraint);
2209
2210 if (this->internalQuickReject(dst, realPaint)) {
2211 return;
2212 }
2213
2214 if (this->topDevice()->shouldDrawAsTiledImageRect()) {
2215 if (this->topDevice()->drawAsTiledImageRect(
2216 this, image, &src, dst, realSampling, realPaint, constraint)) {
2217 return;
2218 }
2219 }
2220
2221 // drawImageRect()'s behavior is modified by the presence of an image filter, a mask filter, a
2222 // color filter, the paint's alpha, the paint's blender, and--when it's an alpha-only image--
2223 // the paint's color or shader. When there's an image filter, the paint's blender is applied to
2224 // the result of the image filter function, but every other aspect would influence the source
2225 // image that's then rendered with src-over blending into a transparent temporary layer.
2226 //
2227 // However, skif::FilterResult can apply the paint alpha and any color filter often without
2228 // requiring a layer, and src-over blending onto a transparent dst is a no-op, so we can use the
2229 // input image directly as the source for filtering. When the image is alpha-only and must be
2230 // colorized, or when a mask filter would change the coverage we skip this optimization for
2231 // simplicity since *somehow* embedding colorization or mask blurring into the filter graph
2232 // would likely be equivalent to using the existing AutoLayerForImageFilter functionality.
2233 if (realPaint.getImageFilter() && !image->isAlphaOnly() && !realPaint.getMaskFilter()) {
2234 SkDevice* device = this->topDevice();
2235
2236 skif::ParameterSpace<SkRect> imageBounds{dst};
2237 skif::DeviceSpace<SkIRect> outputBounds{device->devClipBounds()};
2238 FilterToSpan filterAsSpan(realPaint.getImageFilter());
2239 auto mappingAndBounds = get_layer_mapping_and_bounds(filterAsSpan,
2240 device->localToDevice(),
2241 outputBounds,
2242 imageBounds);
2243 if (!mappingAndBounds) {
2244 return;
2245 }
2246 if (!this->predrawNotify()) {
2247 return;
2248 }
2249
2250 // Start out with an empty source image, to be replaced with the converted 'image', and a
2251 // desired output equal to the calculated initial source layer bounds, which accounts for
2252 // how the image filters will access 'image' (possibly different than just 'outputBounds').
2253 auto backend = device->createImageFilteringBackend(
2254 device->surfaceProps(),
2255 image_filter_color_type(device->imageInfo().colorInfo()));
2256 auto [mapping, srcBounds] = *mappingAndBounds;
2257 skif::Stats stats;
2258 skif::Context ctx{std::move(backend),
2259 mapping,
2260 srcBounds,
2261 skif::FilterResult{},
2262 device->imageInfo().colorSpace(),
2263 &stats};
2264
2265 auto source = skif::FilterResult::MakeFromImage(
2266 ctx, sk_ref_sp(image), src, imageBounds, sampling);
2267 // Apply effects that are normally processed on the draw *before* any layer/image filter.
2268 source = apply_alpha_and_colorfilter(ctx, source, realPaint);
2269
2270 // Evaluate the image filter, with a context pointing to the source created directly from
2271 // 'image' (which will not require intermediate renderpasses when 'src' is integer aligned).
2272 // and a desired output matching the device clip bounds.
2273 ctx = ctx.withNewDesiredOutput(mapping.deviceToLayer(outputBounds))
2274 .withNewSource(source);
2275 auto result = as_IFB(realPaint.getImageFilter())->filterImage(ctx);
2276 result.draw(ctx, device, realPaint.getBlender());
2277 stats.reportStats();
2278 return;
2279 }
2280
2281 // When there's a alpha-only image that must be colorized or a mask filter to apply, go through
2282 // the regular auto-layer-for-imagefilter process
2283 if (realPaint.getMaskFilter() && this->topDevice()->useDrawCoverageMaskForMaskFilters()) {
2284 // Route mask-filtered drawImages to drawRect() to use the auto-layer for mask filters,
2285 // which require all shading to be encoded in the paint.
2286 SkRect drawDst = SkModifyPaintAndDstForDrawImageRect(
2287 image, sampling, src, dst, constraint == kStrict_SrcRectConstraint, &realPaint);
2288 if (drawDst.isEmpty()) {
2289 return;
2290 } else {
2291 this->drawRect(drawDst, realPaint);
2292 return;
2293 }
2294 }
2295
2296 auto layer = this->aboutToDraw(realPaint, &dst,
2297 PredrawFlags::kCheckForOverwrite |
2298 (image->isOpaque() ? PredrawFlags::kOpaqueShaderOverride
2299 : PredrawFlags::kNonOpaqueShaderOverride));
2300 if (layer) {
2301 this->topDevice()->drawImageRect(image, &src, dst, realSampling, layer->paint(),
2302 constraint);
2303 }
2304 }
2305
onDrawImageLattice2(const SkImage * image,const Lattice & lattice,const SkRect & dst,SkFilterMode filter,const SkPaint * paint)2306 void SkCanvas::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2307 SkFilterMode filter, const SkPaint* paint) {
2308 SkPaint realPaint = clean_paint_for_drawImage(paint);
2309
2310 if (this->internalQuickReject(dst, realPaint)) {
2311 return;
2312 }
2313
2314 auto layer = this->aboutToDraw(realPaint, &dst);
2315 if (layer) {
2316 this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer->paint());
2317 }
2318 }
2319
drawImage(const SkImage * image,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint)2320 void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y,
2321 const SkSamplingOptions& sampling, const SkPaint* paint) {
2322 TRACE_EVENT0("skia", TRACE_FUNC);
2323 RETURN_ON_NULL(image);
2324
2325 this->drawImageRect(image,
2326 /*src=*/SkRect::MakeWH(image->width(), image->height()),
2327 /*dst=*/SkRect::MakeXYWH(x, y, image->width(), image->height()),
2328 sampling,
2329 paint,
2330 kFast_SrcRectConstraint);
2331 }
2332
drawImageRect(const SkImage * image,const SkRect & src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)2333 void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2334 const SkSamplingOptions& sampling, const SkPaint* paint,
2335 SrcRectConstraint constraint) {
2336 RETURN_ON_NULL(image);
2337 if (!fillable(dst) || !fillable(src)) {
2338 return;
2339 }
2340 this->onDrawImageRect2(image, src, dst, sampling, paint, constraint);
2341 }
2342
drawImageRect(const SkImage * image,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint)2343 void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst,
2344 const SkSamplingOptions& sampling, const SkPaint* paint) {
2345 RETURN_ON_NULL(image);
2346 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, sampling,
2347 paint, kFast_SrcRectConstraint);
2348 }
2349
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)2350 void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2351 const SkPaint& paint) {
2352 auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(*blob, {x, y});
2353 this->onDrawGlyphRunList(glyphRunList, paint);
2354 }
2355
onDrawGlyphRunList(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)2356 void SkCanvas::onDrawGlyphRunList(const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
2357 SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
2358 if (this->internalQuickReject(bounds, paint)) {
2359 return;
2360 }
2361
2362 // Text attempts to apply any SkMaskFilter internally and save the blurred masks in the
2363 // strike cache; if a glyph must be drawn as a path or drawable, SkDevice routes back to
2364 // this SkCanvas to retry, which will go through a function that does *not* skip the mask
2365 // filter layer.
2366 auto layer = this->aboutToDraw(paint, &bounds, PredrawFlags::kSkipMaskFilterAutoLayer);
2367 if (layer) {
2368 this->topDevice()->drawGlyphRunList(this, glyphRunList, layer->paint());
2369 }
2370 }
2371
convertBlobToSlug(const SkTextBlob & blob,SkPoint origin,const SkPaint & paint)2372 sk_sp<Slug> SkCanvas::convertBlobToSlug(
2373 const SkTextBlob& blob, SkPoint origin, const SkPaint& paint) {
2374 TRACE_EVENT0("skia", TRACE_FUNC);
2375 auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(blob, origin);
2376 return this->onConvertGlyphRunListToSlug(glyphRunList, paint);
2377 }
2378
onConvertGlyphRunListToSlug(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)2379 sk_sp<Slug> SkCanvas::onConvertGlyphRunListToSlug(const sktext::GlyphRunList& glyphRunList,
2380 const SkPaint& paint) {
2381 SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
2382 if (bounds.isEmpty() || !bounds.isFinite() || paint.nothingToDraw()) {
2383 return nullptr;
2384 }
2385 // See comment in onDrawGlyphRunList()
2386 auto layer = this->aboutToDraw(paint, &bounds, PredrawFlags::kSkipMaskFilterAutoLayer);
2387 if (layer) {
2388 return this->topDevice()->convertGlyphRunListToSlug(glyphRunList, layer->paint());
2389 }
2390 return nullptr;
2391 }
2392
drawSlug(const Slug * slug,const SkPaint & paint)2393 void SkCanvas::drawSlug(const Slug* slug, const SkPaint& paint) {
2394 TRACE_EVENT0("skia", TRACE_FUNC);
2395 if (slug) {
2396 this->onDrawSlug(slug, paint);
2397 }
2398 }
2399
onDrawSlug(const Slug * slug,const SkPaint & paint)2400 void SkCanvas::onDrawSlug(const Slug* slug, const SkPaint& paint) {
2401 SkRect bounds = slug->sourceBoundsWithOrigin();
2402 if (this->internalQuickReject(bounds, paint)) {
2403 return;
2404 }
2405 // See comment in onDrawGlyphRunList()
2406 auto layer = this->aboutToDraw(paint, &bounds, PredrawFlags::kSkipMaskFilterAutoLayer);
2407 if (layer) {
2408 this->topDevice()->drawSlug(this, slug, layer->paint());
2409 }
2410 }
2411
2412 // These call the (virtual) onDraw... method
drawSimpleText(const void * text,size_t byteLength,SkTextEncoding encoding,SkScalar x,SkScalar y,const SkFont & font,const SkPaint & paint)2413 void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
2414 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
2415 TRACE_EVENT0("skia", TRACE_FUNC);
2416 if (byteLength) {
2417 sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
2418 const sktext::GlyphRunList& glyphRunList =
2419 fScratchGlyphRunBuilder->textToGlyphRunList(
2420 font, paint, text, byteLength, {x, y}, encoding);
2421 if (!glyphRunList.empty()) {
2422 this->onDrawGlyphRunList(glyphRunList, paint);
2423 }
2424 }
2425 }
2426
drawGlyphs(int count,const SkGlyphID * glyphs,const SkPoint * positions,const uint32_t * clusters,int textByteCount,const char * utf8text,SkPoint origin,const SkFont & font,const SkPaint & paint)2427 void SkCanvas::drawGlyphs(int count, const SkGlyphID* glyphs, const SkPoint* positions,
2428 const uint32_t* clusters, int textByteCount, const char* utf8text,
2429 SkPoint origin, const SkFont& font, const SkPaint& paint) {
2430 if (count <= 0) { return; }
2431
2432 sktext::GlyphRun glyphRun {
2433 font,
2434 SkSpan(positions, count),
2435 SkSpan(glyphs, count),
2436 SkSpan(utf8text, textByteCount),
2437 SkSpan(clusters, count),
2438 SkSpan<SkVector>()
2439 };
2440
2441 sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
2442 glyphRun, paint, origin);
2443 this->onDrawGlyphRunList(glyphRunList, paint);
2444 }
2445
drawGlyphs(int count,const SkGlyphID glyphs[],const SkPoint positions[],SkPoint origin,const SkFont & font,const SkPaint & paint)2446 void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[],
2447 SkPoint origin, const SkFont& font, const SkPaint& paint) {
2448 if (count <= 0) { return; }
2449
2450 sktext::GlyphRun glyphRun {
2451 font,
2452 SkSpan(positions, count),
2453 SkSpan(glyphs, count),
2454 SkSpan<const char>(),
2455 SkSpan<const uint32_t>(),
2456 SkSpan<SkVector>()
2457 };
2458
2459 sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
2460 glyphRun, paint, origin);
2461 this->onDrawGlyphRunList(glyphRunList, paint);
2462 }
2463
drawGlyphs(int count,const SkGlyphID glyphs[],const SkRSXform xforms[],SkPoint origin,const SkFont & font,const SkPaint & paint)2464 void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[],
2465 SkPoint origin, const SkFont& font, const SkPaint& paint) {
2466 if (count <= 0) { return; }
2467
2468 auto [positions, rotateScales] =
2469 fScratchGlyphRunBuilder->convertRSXForm(SkSpan(xforms, count));
2470
2471 sktext::GlyphRun glyphRun {
2472 font,
2473 positions,
2474 SkSpan(glyphs, count),
2475 SkSpan<const char>(),
2476 SkSpan<const uint32_t>(),
2477 rotateScales
2478 };
2479 sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
2480 glyphRun, paint, origin);
2481 this->onDrawGlyphRunList(glyphRunList, paint);
2482 }
2483
drawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)2484 void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2485 const SkPaint& paint) {
2486 TRACE_EVENT0("skia", TRACE_FUNC);
2487 RETURN_ON_NULL(blob);
2488 RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
2489
2490 // Overflow if more than 2^21 glyphs stopping a buffer overflow latter in the stack.
2491 // See chromium:1080481
2492 // TODO: can consider unrolling a few at a time if this limit becomes a problem.
2493 int totalGlyphCount = 0;
2494 constexpr int kMaxGlyphCount = 1 << 21;
2495 SkTextBlob::Iter i(*blob);
2496 SkTextBlob::Iter::Run r;
2497 while (i.next(&r)) {
2498 int glyphsLeft = kMaxGlyphCount - totalGlyphCount;
2499 RETURN_ON_FALSE(r.fGlyphCount <= glyphsLeft);
2500 totalGlyphCount += r.fGlyphCount;
2501 }
2502
2503 this->onDrawTextBlob(blob, x, y, paint);
2504 }
2505
onDrawVerticesObject(const SkVertices * vertices,SkBlendMode bmode,const SkPaint & paint)2506 void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
2507 const SkPaint& paint) {
2508 SkPaint simplePaint = clean_paint_for_drawVertices(paint);
2509
2510 const SkRect& bounds = vertices->bounds();
2511 if (this->internalQuickReject(bounds, simplePaint)) {
2512 return;
2513 }
2514
2515 auto layer = this->aboutToDraw(simplePaint, &bounds);
2516 if (layer) {
2517 this->topDevice()->drawVertices(vertices, SkBlender::Mode(bmode), layer->paint());
2518 }
2519 }
2520
onDrawMesh(const SkMesh & mesh,sk_sp<SkBlender> blender,const SkPaint & paint)2521 void SkCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
2522 SkPaint simplePaint = clean_paint_for_drawVertices(paint);
2523 auto layer = this->aboutToDraw(simplePaint, nullptr);
2524 if (layer) {
2525 this->topDevice()->drawMesh(mesh, std::move(blender), paint);
2526 }
2527 }
2528
drawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode bmode,const SkPaint & paint)2529 void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2530 const SkPoint texCoords[4], SkBlendMode bmode,
2531 const SkPaint& paint) {
2532 TRACE_EVENT0("skia", TRACE_FUNC);
2533 if (nullptr == cubics) {
2534 return;
2535 }
2536
2537 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
2538 }
2539
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode bmode,const SkPaint & paint)2540 void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2541 const SkPoint texCoords[4], SkBlendMode bmode,
2542 const SkPaint& paint) {
2543 // drawPatch has the same behavior restrictions as drawVertices
2544 SkPaint simplePaint = clean_paint_for_drawVertices(paint);
2545
2546 // Since a patch is always within the convex hull of the control points, we discard it when its
2547 // bounding rectangle is completely outside the current clip.
2548 SkRect bounds;
2549 bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
2550 if (this->internalQuickReject(bounds, simplePaint)) {
2551 return;
2552 }
2553
2554 auto layer = this->aboutToDraw(simplePaint, &bounds);
2555 if (layer) {
2556 this->topDevice()->drawPatch(cubics, colors, texCoords, SkBlender::Mode(bmode),
2557 layer->paint());
2558 }
2559 }
2560
drawDrawable(SkDrawable * dr,SkScalar x,SkScalar y)2561 void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2562 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
2563 TRACE_EVENT0("skia", TRACE_FUNC);
2564 #endif
2565 RETURN_ON_NULL(dr);
2566 if (x || y) {
2567 SkMatrix matrix = SkMatrix::Translate(x, y);
2568 this->onDrawDrawable(dr, &matrix);
2569 } else {
2570 this->onDrawDrawable(dr, nullptr);
2571 }
2572 }
2573
drawDrawable(SkDrawable * dr,const SkMatrix * matrix)2574 void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2575 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
2576 TRACE_EVENT0("skia", TRACE_FUNC);
2577 #endif
2578 RETURN_ON_NULL(dr);
2579 if (matrix && matrix->isIdentity()) {
2580 matrix = nullptr;
2581 }
2582 this->onDrawDrawable(dr, matrix);
2583 }
2584
onDrawDrawable(SkDrawable * dr,const SkMatrix * matrix)2585 void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2586 // drawable bounds are no longer reliable (e.g. android displaylist)
2587 // so don't use them for quick-reject
2588 if (this->predrawNotify()) {
2589 this->topDevice()->drawDrawable(this, dr, matrix);
2590 }
2591 }
2592
onDrawAtlas2(const SkImage * atlas,const SkRSXform xform[],const SkRect tex[],const SkColor colors[],int count,SkBlendMode bmode,const SkSamplingOptions & sampling,const SkRect * cull,const SkPaint * paint)2593 void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2594 const SkColor colors[], int count, SkBlendMode bmode,
2595 const SkSamplingOptions& sampling, const SkRect* cull,
2596 const SkPaint* paint) {
2597 // drawAtlas is a combination of drawVertices and drawImage...
2598 SkPaint realPaint = clean_paint_for_drawVertices(clean_paint_for_drawImage(paint));
2599 realPaint.setShader(atlas->makeShader(sampling));
2600
2601 if (cull && this->internalQuickReject(*cull, realPaint)) {
2602 return;
2603 }
2604
2605 // drawAtlas should not have mask filters on its paint, so we don't need to worry about
2606 // converting its "drawImage" behavior into the paint to work with the auto-mask-filter system.
2607 SkASSERT(!realPaint.getMaskFilter());
2608 auto layer = this->aboutToDraw(realPaint);
2609 if (layer) {
2610 this->topDevice()->drawAtlas(xform, tex, colors, count, SkBlender::Mode(bmode),
2611 layer->paint());
2612 }
2613 }
2614
onDrawAnnotation(const SkRect & rect,const char key[],SkData * value)2615 void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2616 SkASSERT(key);
2617
2618 if (this->predrawNotify()) {
2619 this->topDevice()->drawAnnotation(rect, key, value);
2620 }
2621 }
2622
onDrawEdgeAAQuad(const SkRect & r,const SkPoint clip[4],QuadAAFlags edgeAA,const SkColor4f & color,SkBlendMode mode)2623 void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
2624 const SkColor4f& color, SkBlendMode mode) {
2625 SkASSERT(r.isSorted());
2626
2627 SkPaint paint{color};
2628 paint.setBlendMode(mode);
2629 if (this->internalQuickReject(r, paint)) {
2630 return;
2631 }
2632
2633 if (this->predrawNotify()) {
2634 this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
2635 }
2636 }
2637
onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[],int count,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)2638 void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
2639 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
2640 const SkSamplingOptions& sampling, const SkPaint* paint,
2641 SrcRectConstraint constraint) {
2642 if (count <= 0) {
2643 // Nothing to draw
2644 return;
2645 }
2646
2647 SkPaint realPaint = clean_paint_for_drawImage(paint);
2648 SkSamplingOptions realSampling = clean_sampling_for_constraint(sampling, constraint);
2649
2650 // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
2651 // individual entries and Chromium's occlusion culling already makes it likely that at least one
2652 // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
2653 // or we need it for the autolooper (since it greatly improves image filter perf).
2654 bool needsAutoLayer = SkToBool(realPaint.getImageFilter());
2655 bool setBoundsValid = count == 1 || needsAutoLayer;
2656 SkRect setBounds = imageSet[0].fDstRect;
2657 if (imageSet[0].fMatrixIndex >= 0) {
2658 // Account for the per-entry transform that is applied prior to the CTM when drawing
2659 preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
2660 }
2661 if (needsAutoLayer) {
2662 for (int i = 1; i < count; ++i) {
2663 SkRect entryBounds = imageSet[i].fDstRect;
2664 if (imageSet[i].fMatrixIndex >= 0) {
2665 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
2666 }
2667 setBounds.joinPossiblyEmptyRect(entryBounds);
2668 }
2669 }
2670
2671 // If we happen to have the draw bounds, though, might as well check quickReject().
2672 if (setBoundsValid && this->internalQuickReject(setBounds, realPaint)) {
2673 return;
2674 }
2675
2676 auto layer = this->aboutToDraw(realPaint, setBoundsValid ? &setBounds : nullptr);
2677 if (layer) {
2678 this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices,
2679 realSampling, layer->paint(), constraint);
2680 }
2681 }
2682
2683 //////////////////////////////////////////////////////////////////////////////
2684 // These methods are NOT virtual, and therefore must call back into virtual
2685 // methods, rather than actually drawing themselves.
2686 //////////////////////////////////////////////////////////////////////////////
2687
drawColor(const SkColor4f & c,SkBlendMode mode)2688 void SkCanvas::drawColor(const SkColor4f& c, SkBlendMode mode) {
2689 SkPaint paint;
2690 paint.setColor(c);
2691 paint.setBlendMode(mode);
2692 this->drawPaint(paint);
2693 }
2694
drawPoint(SkScalar x,SkScalar y,const SkPaint & paint)2695 void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2696 const SkPoint pt = { x, y };
2697 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2698 }
2699
drawLine(SkScalar x0,SkScalar y0,SkScalar x1,SkScalar y1,const SkPaint & paint)2700 void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
2701 SkPoint pts[2];
2702 pts[0].set(x0, y0);
2703 pts[1].set(x1, y1);
2704 this->drawPoints(kLines_PointMode, 2, pts, paint);
2705 }
2706
drawCircle(SkScalar cx,SkScalar cy,SkScalar radius,const SkPaint & paint)2707 void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
2708 if (radius < 0) {
2709 radius = 0;
2710 }
2711
2712 SkRect r;
2713 r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
2714 this->drawOval(r, paint);
2715 }
2716
drawRoundRect(const SkRect & r,SkScalar rx,SkScalar ry,const SkPaint & paint)2717 void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2718 const SkPaint& paint) {
2719 if (rx > 0 && ry > 0) {
2720 SkRRect rrect;
2721 rrect.setRectXY(r, rx, ry);
2722 this->drawRRect(rrect, paint);
2723 } else {
2724 this->drawRect(r, paint);
2725 }
2726 }
2727
drawArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)2728 void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2729 SkScalar sweepAngle, bool useCenter,
2730 const SkPaint& paint) {
2731 TRACE_EVENT0("skia", TRACE_FUNC);
2732 if (oval.isEmpty() || !sweepAngle) {
2733 return;
2734 }
2735 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
2736 }
2737
2738 ///////////////////////////////////////////////////////////////////////////////
2739 #ifdef SK_DISABLE_SKPICTURE
drawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)2740 void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}
2741
2742
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)2743 void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2744 const SkPaint* paint) {}
2745 #else
2746
drawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)2747 void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2748 TRACE_EVENT0("skia", TRACE_FUNC);
2749 RETURN_ON_NULL(picture);
2750
2751 if (matrix && matrix->isIdentity()) {
2752 matrix = nullptr;
2753 }
2754 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2755 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2756 picture->playback(this);
2757 } else {
2758 this->onDrawPicture(picture, matrix, paint);
2759 }
2760 }
2761
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)2762 void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2763 const SkPaint* paint) {
2764 if (this->internalQuickReject(picture->cullRect(), paint ? *paint : SkPaint{}, matrix)) {
2765 return;
2766 }
2767
2768 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2769 picture->playback(this);
2770 }
2771 #endif
2772
2773 ///////////////////////////////////////////////////////////////////////////////
2774
2775 SkCanvas::ImageSetEntry::ImageSetEntry() = default;
2776 SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
2777 SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
2778 SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;
2779
ImageSetEntry(sk_sp<const SkImage> image,const SkRect & srcRect,const SkRect & dstRect,int matrixIndex,float alpha,unsigned aaFlags,bool hasClip)2780 SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2781 const SkRect& dstRect, int matrixIndex, float alpha,
2782 unsigned aaFlags, bool hasClip)
2783 : fImage(std::move(image))
2784 , fSrcRect(srcRect)
2785 , fDstRect(dstRect)
2786 , fMatrixIndex(matrixIndex)
2787 , fAlpha(alpha)
2788 , fAAFlags(aaFlags)
2789 , fHasClip(hasClip) {}
2790
ImageSetEntry(sk_sp<const SkImage> image,const SkRect & srcRect,const SkRect & dstRect,float alpha,unsigned aaFlags)2791 SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
2792 const SkRect& dstRect, float alpha, unsigned aaFlags)
2793 : fImage(std::move(image))
2794 , fSrcRect(srcRect)
2795 , fDstRect(dstRect)
2796 , fAlpha(alpha)
2797 , fAAFlags(aaFlags) {}
2798
2799 ///////////////////////////////////////////////////////////////////////////////
2800
MakeRasterDirect(const SkImageInfo & info,void * pixels,size_t rowBytes,const SkSurfaceProps * props)2801 std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
2802 size_t rowBytes, const SkSurfaceProps* props) {
2803 if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
2804 return nullptr;
2805 }
2806
2807 SkBitmap bitmap;
2808 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2809 return nullptr;
2810 }
2811
2812 return props ?
2813 std::make_unique<SkCanvas>(bitmap, *props) :
2814 std::make_unique<SkCanvas>(bitmap);
2815 }
2816
2817 ///////////////////////////////////////////////////////////////////////////////
2818
SkNoDrawCanvas(int width,int height)2819 SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
2820 : INHERITED(SkIRect::MakeWH(width, height)) {}
2821
SkNoDrawCanvas(const SkIRect & bounds)2822 SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
2823 : INHERITED(bounds) {}
2824
getSaveLayerStrategy(const SaveLayerRec & rec)2825 SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
2826 (void)this->INHERITED::getSaveLayerStrategy(rec);
2827 return kNoLayer_SaveLayerStrategy;
2828 }
2829
onDoSaveBehind(const SkRect *)2830 bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
2831 return false;
2832 }
2833
2834 ///////////////////////////////////////////////////////////////////////////////
2835
2836 static_assert((int)SkRegion::kDifference_Op == (int)SkClipOp::kDifference, "");
2837 static_assert((int)SkRegion::kIntersect_Op == (int)SkClipOp::kIntersect, "");
2838
2839 ///////////////////////////////////////////////////////////////////////////////////////////////////
2840
accessTopRasterHandle() const2841 SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
2842 const SkDevice* dev = this->topDevice();
2843 if (fAllocator) {
2844 SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
2845 SkIRect clip = dev->devClipBounds();
2846 if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
2847 clip.setEmpty();
2848 }
2849
2850 fAllocator->updateHandle(handle, dev->localToDevice(), clip);
2851 return handle;
2852 }
2853 return nullptr;
2854 }
2855
install(SkBitmap * bm,const SkImageInfo & info,const SkRasterHandleAllocator::Rec & rec)2856 static bool install(SkBitmap* bm, const SkImageInfo& info,
2857 const SkRasterHandleAllocator::Rec& rec) {
2858 return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
2859 }
2860
allocBitmap(const SkImageInfo & info,SkBitmap * bm)2861 SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
2862 SkBitmap* bm) {
2863 SkRasterHandleAllocator::Rec rec;
2864 if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
2865 return nullptr;
2866 }
2867 return rec.fHandle;
2868 }
2869
2870 std::unique_ptr<SkCanvas>
MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,const SkImageInfo & info,const Rec * rec,const SkSurfaceProps * props)2871 SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
2872 const SkImageInfo& info, const Rec* rec,
2873 const SkSurfaceProps* props) {
2874 if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
2875 return nullptr;
2876 }
2877
2878 SkBitmap bm;
2879 Handle hndl;
2880
2881 if (rec) {
2882 hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
2883 } else {
2884 hndl = alloc->allocBitmap(info, &bm);
2885 }
2886 return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl, props))
2887 : nullptr;
2888 }
2889