xref: /aosp_15_r20/external/skia/src/effects/imagefilters/SkMatrixTransformImageFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkFlattenable.h"
9 #include "include/core/SkImageFilter.h"
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSamplingOptions.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkTypes.h"
17 #include "include/effects/SkImageFilters.h"
18 #include "src/core/SkImageFilterTypes.h"
19 #include "src/core/SkImageFilter_Base.h"
20 #include "src/core/SkPicturePriv.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkSamplingPriv.h"
23 #include "src/core/SkWriteBuffer.h"
24 
25 #include <optional>
26 #include <utility>
27 
28 struct SkISize;
29 
30 namespace {
31 
32 class SkMatrixTransformImageFilter final : public SkImageFilter_Base {
33 public:
34     // TODO(michaelludwig): Update this to use SkM44.
SkMatrixTransformImageFilter(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)35     SkMatrixTransformImageFilter(const SkMatrix& transform,
36                                  const SkSamplingOptions& sampling,
37                                  sk_sp<SkImageFilter> input)
38             : SkImageFilter_Base(&input, 1)
39             , fTransform(transform)
40             , fSampling(sampling) {
41         // Pre-cache so future calls to fTransform.getType() are threadsafe.
42         (void) static_cast<const SkMatrix&>(fTransform).getType();
43     }
44 
45     SkRect computeFastBounds(const SkRect&) const override;
46 
47 protected:
48     void flatten(SkWriteBuffer&) const override;
49 
50 private:
51     friend void ::SkRegisterMatrixTransformImageFilterFlattenable();
52     SK_FLATTENABLE_HOOKS(SkMatrixTransformImageFilter)
53     static sk_sp<SkFlattenable> LegacyOffsetCreateProc(SkReadBuffer& buffer);
54 
onGetCTMCapability() const55     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
56 
57     skif::FilterResult onFilterImage(const skif::Context& context) const override;
58 
59     skif::LayerSpace<SkIRect> onGetInputLayerBounds(
60             const skif::Mapping& mapping,
61             const skif::LayerSpace<SkIRect>& desiredOutput,
62             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
63 
64     std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
65             const skif::Mapping& mapping,
66             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
67 
68     skif::LayerSpace<SkIRect> requiredInput(const skif::Mapping& mapping,
69                                             const skif::LayerSpace<SkIRect>& desiredOutput) const;
70 
71     skif::ParameterSpace<SkMatrix> fTransform;
72     SkSamplingOptions fSampling;
73 };
74 
75 } // namespace
76 
MatrixTransform(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)77 sk_sp<SkImageFilter> SkImageFilters::MatrixTransform(const SkMatrix& transform,
78                                                      const SkSamplingOptions& sampling,
79                                                      sk_sp<SkImageFilter> input) {
80     if (!transform.invert(/*inverse=*/nullptr)) {
81         return nullptr;
82     }
83     return sk_sp<SkImageFilter>(new SkMatrixTransformImageFilter(transform,
84                                                                  sampling,
85                                                                  std::move(input)));
86 }
87 
Offset(SkScalar dx,SkScalar dy,sk_sp<SkImageFilter> input,const CropRect & cropRect)88 sk_sp<SkImageFilter> SkImageFilters::Offset(SkScalar dx, SkScalar dy,
89                                             sk_sp<SkImageFilter> input,
90                                             const CropRect& cropRect) {
91     // The legacy ::Offset() implementation rounded its offset vector to layer-space pixels, which
92     // is roughly equivalent to using nearest-neighbor sampling with the translation matrix.
93     sk_sp<SkImageFilter> offset = SkImageFilters::MatrixTransform(
94             SkMatrix::Translate(dx, dy),
95             SkFilterMode::kNearest,
96             std::move(input));
97     // The legacy 'cropRect' applies only to the output of the offset filter.
98     if (cropRect) {
99         offset = SkImageFilters::Crop(*cropRect, std::move(offset));
100     }
101     return offset;
102 }
103 
SkRegisterMatrixTransformImageFilterFlattenable()104 void SkRegisterMatrixTransformImageFilterFlattenable() {
105     SK_REGISTER_FLATTENABLE(SkMatrixTransformImageFilter);
106     // TODO(michaelludwig): Remove after grace period for SKPs to stop using old name
107     SkFlattenable::Register("SkMatrixImageFilter", SkMatrixTransformImageFilter::CreateProc);
108     // TODO(michaelludwig): Remove after grace period for SKPs to stop using old serialization
109     SkFlattenable::Register("SkOffsetImageFilter",
110                             SkMatrixTransformImageFilter::LegacyOffsetCreateProc);
111     SkFlattenable::Register("SkOffsetImageFilterImpl",
112                             SkMatrixTransformImageFilter::LegacyOffsetCreateProc);
113 }
114 
LegacyOffsetCreateProc(SkReadBuffer & buffer)115 sk_sp<SkFlattenable> SkMatrixTransformImageFilter::LegacyOffsetCreateProc(SkReadBuffer& buffer) {
116     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
117     SkPoint offset;
118     buffer.readPoint(&offset);
119     return SkImageFilters::Offset(offset.x(), offset.y(), common.getInput(0), common.cropRect());
120 }
121 
CreateProc(SkReadBuffer & buffer)122 sk_sp<SkFlattenable> SkMatrixTransformImageFilter::CreateProc(SkReadBuffer& buffer) {
123     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
124     SkMatrix matrix;
125     buffer.readMatrix(&matrix);
126 
127     auto sampling = [&]() {
128         if (buffer.isVersionLT(SkPicturePriv::kMatrixImageFilterSampling_Version)) {
129             return SkSamplingPriv::FromFQ(buffer.read32LE(kLast_SkLegacyFQ), kLinear_SkMediumAs);
130         } else {
131             return buffer.readSampling();
132         }
133     }();
134     return SkImageFilters::MatrixTransform(matrix, sampling, common.getInput(0));
135 }
136 
flatten(SkWriteBuffer & buffer) const137 void SkMatrixTransformImageFilter::flatten(SkWriteBuffer& buffer) const {
138     this->SkImageFilter_Base::flatten(buffer);
139     buffer.writeMatrix(SkMatrix(fTransform));
140     buffer.writeSampling(fSampling);
141 }
142 
143 ///////////////////////////////////////////////////////////////////////////////////////////////////
144 
onFilterImage(const skif::Context & context) const145 skif::FilterResult SkMatrixTransformImageFilter::onFilterImage(const skif::Context& context) const {
146     skif::LayerSpace<SkIRect> requiredInput =
147             this->requiredInput(context.mapping(), context.desiredOutput());
148     skif::FilterResult childOutput =
149             this->getChildOutput(0, context.withNewDesiredOutput(requiredInput));
150 
151     skif::LayerSpace<SkMatrix> transform = context.mapping().paramToLayer(fTransform);
152     return childOutput.applyTransform(context, transform, fSampling);
153 }
154 
computeFastBounds(const SkRect & src) const155 SkRect SkMatrixTransformImageFilter::computeFastBounds(const SkRect& src) const {
156     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
157     return static_cast<const SkMatrix&>(fTransform).mapRect(bounds);
158 }
159 
requiredInput(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput) const160 skif::LayerSpace<SkIRect> SkMatrixTransformImageFilter::requiredInput(
161         const skif::Mapping& mapping,
162         const skif::LayerSpace<SkIRect>& desiredOutput) const {
163     // The required input for this filter to cover 'desiredOutput' is the smallest rectangle such
164     // that after being transformed by the layer-space adjusted 'fTransform', it contains the output
165     skif::LayerSpace<SkIRect> requiredInput;
166     if (!mapping.paramToLayer(fTransform).inverseMapRect(desiredOutput, &requiredInput)) {
167         return skif::LayerSpace<SkIRect>::Empty();
168     }
169 
170     // Additionally if there is any filtering beyond nearest neighbor, we request an extra buffer of
171     // pixels so that the content is available to the bilerp/bicubic kernel.
172     if (fSampling != SkSamplingOptions()) {
173         requiredInput.outset(skif::LayerSpace<SkISize>({1, 1}));
174     }
175     return requiredInput;
176 }
177 
178 
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const179 skif::LayerSpace<SkIRect> SkMatrixTransformImageFilter::onGetInputLayerBounds(
180         const skif::Mapping& mapping,
181         const skif::LayerSpace<SkIRect>& desiredOutput,
182         std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
183     // Our required input is the desired output for our child image filter.
184     skif::LayerSpace<SkIRect> requiredInput = this->requiredInput(mapping, desiredOutput);
185     return this->getChildInputLayerBounds(0, mapping, requiredInput, contentBounds);
186 }
187 
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const188 std::optional<skif::LayerSpace<SkIRect>> SkMatrixTransformImageFilter::onGetOutputLayerBounds(
189         const skif::Mapping& mapping,
190         std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
191     // The output of this filter is the transformed bounds of its child's output.
192     auto childOutput = this->getChildOutputLayerBounds(0, mapping, contentBounds);
193     if (childOutput) {
194         return mapping.paramToLayer(fTransform).mapRect(*childOutput);
195     } else {
196         return skif::LayerSpace<SkIRect>::Unbounded();
197     }
198 }
199