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