Skip to content

Commit f8f23b2

Browse files
lhkbobSkia Commit-Bot
authored andcommitted
Draw image filters directly under non-axis-aligned transforms
This removes hacking the canvas CTM and wrapping the paint's image filter in a special MatrixTransform that computed a post-transform instead of its documented pre-transform effect. Performance-wise, the computed layer sizes should be about the same, but we avoid one less render target switch because we apply the transformation while drawing to the dst device, vs. transforming into another temporary layer and then drawing that to the dst device. Several important changes in behavior here: 1. The DeviceCM record no longer has a stashed matrix to restore and holds its restoration paint directly. 2. Devices for image filter inputs can now have device-to-global transforms that are not integer translates. 3. The MatrixTransform hack punted when there was perspective because it could produce excessively large temporary images, but the new version appears to work around that. We now impose a maximum layer size to protect against that and automatically scale the layer to prevent it. Perspective image filters otherwise now draw correctly. 6. Updated layer sizing code to use the new image filter APIs 7. Updated backdrop filter and restore filters to go through the same code paths, although restore filters skip the intermediate image transform. - layer bounds and transforms now go through the updated skif API and is hopefully more straight forward to understand. 8. Now we can optimize root color filter nodes of a filter DAG, even if the entire DAG can't be represented as a color filter. The last node is pulled off and composed with the restoration paint instead. Bug: skia:9074,skia:9283 Change-Id: I1fa1d50135b9d6d453b02f89aa3cc3b54deab678 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/328376 Commit-Queue: Michael Ludwig <[email protected]> Reviewed-by: Brian Salomon <[email protected]>
1 parent 88d7e62 commit f8f23b2

File tree

8 files changed

+462
-498
lines changed

8 files changed

+462
-498
lines changed

RELEASE_NOTES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Milestone 88
99

1010
* <insert new release notes here>
1111

12+
* Image filters with perspective, for saveLayer and draws, are now drawn correctly. Performance
13+
and quality is highest if user bounds are provided to saveLayer.
14+
https://review.skia.org/328376
15+
1216
* Add AVIF support to SkHeifCodec.
1317

1418
* Add support for creating SkSurfaceCharacterizations directly for use by a

include/core/SkCanvas.h

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,13 +2557,6 @@ class SK_API SkCanvas {
25572557

25582558
virtual void onDiscard();
25592559

2560-
// Clip rectangle bounds. Called internally by saveLayer.
2561-
// returns false if the entire rectangle is entirely clipped out
2562-
// If non-NULL, The imageFilter parameter will be used to expand the clip
2563-
// and offscreen bounds for any margin required by the filter DAG.
2564-
bool clipRectBounds(const SkRect* bounds, SaveLayerFlags flags, SkIRect* intersection,
2565-
const SkImageFilter* imageFilter = nullptr);
2566-
25672560
SkBaseDevice* getTopDevice() const;
25682561

25692562
private:
@@ -2601,17 +2594,10 @@ class SK_API SkCanvas {
26012594
// safely with 32 and 64 bit machines (to ensure the storage is enough)
26022595
intptr_t fStorage[32];
26032596
class SkDrawIter* fImpl; // this points at fStorage
2604-
SkPaint fDefaultPaint;
26052597
SkIPoint fDeviceOrigin;
26062598
bool fDone;
26072599
};
26082600

2609-
static bool BoundsAffectsClip(SaveLayerFlags);
2610-
2611-
static void DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
2612-
SkBaseDevice* dst, const SkIPoint& dstOrigin,
2613-
const SkMatrix& ctm);
2614-
26152601
enum ShaderOverrideOpacity {
26162602
kNone_ShaderOverrideOpacity, //!< there is no overriding shader (bitmap or image)
26172603
kOpaque_ShaderOverrideOpacity, //!< the overriding shader is opaque
@@ -2640,7 +2626,7 @@ class SK_API SkCanvas {
26402626
// the first N recs that can fit here mean we won't call malloc
26412627
static constexpr int kMCRecSize = 128; // most recent measurement
26422628
static constexpr int kMCRecCount = 32; // common depth for save/restores
2643-
static constexpr int kDeviceCMSize = 64; // most recent measurement
2629+
static constexpr int kDeviceCMSize = 96; // most recent measurement
26442630

26452631
intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
26462632
intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
@@ -2723,14 +2709,31 @@ class SK_API SkCanvas {
27232709
void internalDrawPaint(const SkPaint& paint);
27242710
void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy);
27252711
void internalSaveBehind(const SkRect*);
2726-
void internalDrawDevice(SkBaseDevice*, const SkPaint*);
27272712

27282713
void internalConcat44(const SkM44&);
27292714

27302715
// shared by save() and saveLayer()
27312716
void internalSave();
27322717
void internalRestore();
27332718

2719+
enum class DeviceCompatibleWithFilter : bool {
2720+
// Check the src device's local-to-device matrix for compatibility with the filter, and if
2721+
// it is not compatible, introduce an intermediate image and transformation that allows the
2722+
// filter to be evaluated on the modified src content.
2723+
kUnknown = false,
2724+
// Assume that the src device's local-to-device matrix is compatible with the filter.
2725+
kYes = true
2726+
};
2727+
/**
2728+
* Filters the contents of 'src' and draws the result into 'dst'. The filter is evaluated
2729+
* relative to the current canvas matrix, and src is drawn to dst using their relative transform
2730+
* 'paint' is applied after the filter and must not have a mask or image filter of its own.
2731+
* A null 'filter' behaves as if the identity filter were used.
2732+
*/
2733+
void internalDrawDeviceWithFilter(SkBaseDevice* src, SkBaseDevice* dst,
2734+
const SkPaint& paint, const SkImageFilter* filter,
2735+
DeviceCompatibleWithFilter compat);
2736+
27342737
/*
27352738
* Returns true if drawing the specified rect (or all if it is null) with the specified
27362739
* paint (or default if null) would overwrite the entire root device of the canvas

0 commit comments

Comments
 (0)