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 #include "src/effects/imagefilters/SkCropImageFilter.h"
25
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, nullptr)
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 const skif::LayerSpace<SkIRect>& contentBounds,
63 VisitChildren recurse) const override;
64
65 skif::LayerSpace<SkIRect> onGetOutputLayerBounds(
66 const skif::Mapping& mapping,
67 const skif::LayerSpace<SkIRect>& contentBounds) const override;
68
69 skif::ParameterSpace<SkMatrix> fTransform;
70 SkSamplingOptions fSampling;
71 };
72
73 } // namespace
74
MatrixTransform(const SkMatrix & transform,const SkSamplingOptions & sampling,sk_sp<SkImageFilter> input)75 sk_sp<SkImageFilter> SkImageFilters::MatrixTransform(const SkMatrix& transform,
76 const SkSamplingOptions& sampling,
77 sk_sp<SkImageFilter> input) {
78 return sk_sp<SkImageFilter>(new SkMatrixTransformImageFilter(transform,
79 sampling,
80 std::move(input)));
81 }
82
Offset(SkScalar dx,SkScalar dy,sk_sp<SkImageFilter> input,const CropRect & cropRect)83 sk_sp<SkImageFilter> SkImageFilters::Offset(SkScalar dx, SkScalar dy,
84 sk_sp<SkImageFilter> input,
85 const CropRect& cropRect) {
86 // The legacy ::Offset() implementation rounded its offset vector to layer-space pixels, which
87 // is roughly equivalent to using nearest-neighbor sampling with the translation matrix.
88 sk_sp<SkImageFilter> offset = SkImageFilters::MatrixTransform(
89 SkMatrix::Translate(dx, dy),
90 SkSamplingOptions{SkFilterMode::kNearest},
91 std::move(input));
92 // The legacy 'cropRect' applies only to the output of the offset filter.
93 if (cropRect) {
94 offset = SkMakeCropImageFilter(*cropRect, std::move(offset));
95 }
96 return offset;
97 }
98
SkRegisterMatrixTransformImageFilterFlattenable()99 void SkRegisterMatrixTransformImageFilterFlattenable() {
100 SK_REGISTER_FLATTENABLE(SkMatrixTransformImageFilter);
101 // TODO(michaelludwig): Remove after grace period for SKPs to stop using old name
102 SkFlattenable::Register("SkMatrixImageFilter", SkMatrixTransformImageFilter::CreateProc);
103 // TODO(michaelludwig): Remove after grace period for SKPs to stop using old serialization
104 SkFlattenable::Register("SkOffsetImageFilter",
105 SkMatrixTransformImageFilter::LegacyOffsetCreateProc);
106 SkFlattenable::Register("SkOffsetImageFilterImpl",
107 SkMatrixTransformImageFilter::LegacyOffsetCreateProc);
108 }
109
LegacyOffsetCreateProc(SkReadBuffer & buffer)110 sk_sp<SkFlattenable> SkMatrixTransformImageFilter::LegacyOffsetCreateProc(SkReadBuffer& buffer) {
111 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
112 SkPoint offset;
113 buffer.readPoint(&offset);
114 return SkImageFilters::Offset(offset.x(), offset.y(), common.getInput(0), common.cropRect());
115 }
116
CreateProc(SkReadBuffer & buffer)117 sk_sp<SkFlattenable> SkMatrixTransformImageFilter::CreateProc(SkReadBuffer& buffer) {
118 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
119 SkMatrix matrix;
120 buffer.readMatrix(&matrix);
121
122 auto sampling = [&]() {
123 if (buffer.isVersionLT(SkPicturePriv::kMatrixImageFilterSampling_Version)) {
124 return SkSamplingPriv::FromFQ(buffer.read32LE(kLast_SkLegacyFQ), kLinear_SkMediumAs);
125 } else {
126 return buffer.readSampling();
127 }
128 }();
129 return SkImageFilters::MatrixTransform(matrix, sampling, common.getInput(0));
130 }
131
flatten(SkWriteBuffer & buffer) const132 void SkMatrixTransformImageFilter::flatten(SkWriteBuffer& buffer) const {
133 this->SkImageFilter_Base::flatten(buffer);
134 buffer.writeMatrix(SkMatrix(fTransform));
135 buffer.writeSampling(fSampling);
136 }
137
138 ///////////////////////////////////////////////////////////////////////////////////////////////////
139
onFilterImage(const skif::Context & context) const140 skif::FilterResult SkMatrixTransformImageFilter::onFilterImage(const skif::Context& context) const {
141 skif::FilterResult childOutput = this->filterInput(0, context);
142 skif::LayerSpace<SkMatrix> transform = context.mapping().paramToLayer(fTransform);
143 return childOutput.applyTransform(context, transform, fSampling);
144 }
145
computeFastBounds(const SkRect & src) const146 SkRect SkMatrixTransformImageFilter::computeFastBounds(const SkRect& src) const {
147 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
148 return static_cast<const SkMatrix&>(fTransform).mapRect(bounds);
149 }
150
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,const skif::LayerSpace<SkIRect> & contentBounds,VisitChildren recurse) const151 skif::LayerSpace<SkIRect> SkMatrixTransformImageFilter::onGetInputLayerBounds(
152 const skif::Mapping& mapping,
153 const skif::LayerSpace<SkIRect>& desiredOutput,
154 const skif::LayerSpace<SkIRect>& contentBounds,
155 VisitChildren recurse) const {
156 // The required input for this filter to cover 'desiredOutput' is the smallest rectangle such
157 // that after being transformed by the layer-space adjusted 'fTransform', it contains the output
158 skif::LayerSpace<SkMatrix> inverse;
159 if (!mapping.paramToLayer(fTransform).invert(&inverse)) {
160 return skif::LayerSpace<SkIRect>::Empty();
161 }
162 skif::LayerSpace<SkIRect> requiredInput = inverse.mapRect(desiredOutput);
163
164 // Additionally if there is any filtering beyond nearest neighbor, we request an extra buffer of
165 // pixels so that the content is available to the bilerp/bicubic kernel.
166 if (fSampling != SkSamplingOptions()) {
167 requiredInput.outset(skif::LayerSpace<SkISize>({1, 1}));
168 }
169
170 if (recurse == VisitChildren::kNo) {
171 return requiredInput;
172 } else {
173 // Our required input is the desired output for our child image filter.
174 return this->visitInputLayerBounds(mapping, requiredInput, contentBounds);
175 }
176 }
177
onGetOutputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & contentBounds) const178 skif::LayerSpace<SkIRect> SkMatrixTransformImageFilter::onGetOutputLayerBounds(
179 const skif::Mapping& mapping,
180 const skif::LayerSpace<SkIRect>& contentBounds) const {
181 // The output of this filter is the transformed bounds of its child's output.
182 skif::LayerSpace<SkIRect> childOutput = this->visitOutputLayerBounds(mapping, contentBounds);
183 return mapping.paramToLayer(fTransform).mapRect(childOutput);
184 }
185