• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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