1 /*
2 * Copyright 2013 Google Inc.
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/effects/SkImageFilters.h"
9
10 #include "include/core/SkFlattenable.h"
11 #include "include/core/SkImageFilter.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkTypes.h"
15 #include "src/core/SkImageFilterTypes.h"
16 #include "src/core/SkImageFilter_Base.h"
17
18 #include <optional>
19 #include <utility>
20
21 class SkReadBuffer;
22
23 namespace {
24
25 class SkComposeImageFilter final : public SkImageFilter_Base {
26 static constexpr int kOuter = 0;
27 static constexpr int kInner = 1;
28
29 public:
SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])30 explicit SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])
31 : SkImageFilter_Base(inputs, 2,
32 // Compose only uses the source if the inner filter uses the source
33 // image. Any outer reference to source is rebound to the result of
34 // the inner.
35 inputs[kInner] ? as_IFB(inputs[kInner])->usesSource() : false) {
36 SkASSERT(inputs[kOuter].get());
37 SkASSERT(inputs[kInner].get());
38 }
39
40 SkRect computeFastBounds(const SkRect& src) const override;
41
42 protected:
43 // No flatten() needed since this does not add state beyond the input image filters handled
44 // by the parent implementation.
45
46 private:
47 friend void ::SkRegisterComposeImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkComposeImageFilter)48 SK_FLATTENABLE_HOOKS(SkComposeImageFilter)
49
50 MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
51
52 skif::FilterResult onFilterImage(const skif::Context& context) const override;
53
54 skif::LayerSpace<SkIRect> onGetInputLayerBounds(
55 const skif::Mapping& mapping,
56 const skif::LayerSpace<SkIRect>& desiredOutput,
57 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
58
59 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
60 const skif::Mapping& mapping,
61 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
62 };
63
64 } // end namespace
65
Compose(sk_sp<SkImageFilter> outer,sk_sp<SkImageFilter> inner)66 sk_sp<SkImageFilter> SkImageFilters::Compose(sk_sp<SkImageFilter> outer,
67 sk_sp<SkImageFilter> inner) {
68 if (!outer) {
69 return inner;
70 }
71 if (!inner) {
72 return outer;
73 }
74 sk_sp<SkImageFilter> inputs[2] = { std::move(outer), std::move(inner) };
75 return sk_sp<SkImageFilter>(new SkComposeImageFilter(inputs));
76 }
77
SkRegisterComposeImageFilterFlattenable()78 void SkRegisterComposeImageFilterFlattenable() {
79 SK_REGISTER_FLATTENABLE(SkComposeImageFilter);
80 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
81 SkFlattenable::Register("SkComposeImageFilterImpl", SkComposeImageFilter::CreateProc);
82 }
83
CreateProc(SkReadBuffer & buffer)84 sk_sp<SkFlattenable> SkComposeImageFilter::CreateProc(SkReadBuffer& buffer) {
85 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
86 return SkImageFilters::Compose(common.getInput(kOuter), common.getInput(kInner));
87 }
88
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
90
onFilterImage(const skif::Context & ctx) const91 skif::FilterResult SkComposeImageFilter::onFilterImage(const skif::Context& ctx) const {
92 // Get the expected output of the inner filter, given the source image's layer bounds as content
93 auto innerOutputBounds =
94 this->getChildOutputLayerBounds(kInner, ctx.mapping(), ctx.source().layerBounds());
95 // Get the required input for the outer filter, that it needs to cover the desired output.
96 skif::LayerSpace<SkIRect> outerRequiredInput =
97 this->getChildInputLayerBounds(kOuter,
98 ctx.mapping(),
99 ctx.desiredOutput(),
100 innerOutputBounds);
101
102 // Evalute the inner filter and pass that to the outer filter.
103 skif::FilterResult innerResult =
104 this->getChildOutput(kInner, ctx.withNewDesiredOutput(outerRequiredInput));
105
106 // NOTE: This is the only spot in image filtering where the source image of the context
107 // is not constant for the entire DAG evaluation. Given that the inner and outer DAG branches
108 // were already created, there's no alternative way for the leaf nodes of the outer DAG to
109 // get the results of the inner DAG. Overriding the source image of the context has the correct
110 // effect, but means that the source image is not fixed for the entire filter process.
111 return this->getChildOutput(kOuter, ctx.withNewSource(innerResult));
112 }
113
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const114 skif::LayerSpace<SkIRect> SkComposeImageFilter::onGetInputLayerBounds(
115 const skif::Mapping& mapping,
116 const skif::LayerSpace<SkIRect>& desiredOutput,
117 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
118 // The outer filter must produce 'desiredOutput'. Its required input bounds becomes the desired
119 // output of the inner filter. However, 'contentBounds' is the bounds visible to the input
120 // filter. The output bounds of the inner filter represents the content bounds of the outer.
121 std::optional<skif::LayerSpace<SkIRect>> outerContentBounds;
122 if (contentBounds) {
123 outerContentBounds = this->getChildOutputLayerBounds(kInner, mapping, *contentBounds);
124 } // else leave outer's content bounds "unbounded"
125
126 skif::LayerSpace<SkIRect> innerDesiredOutput =
127 this->getChildInputLayerBounds(kOuter, mapping, desiredOutput, outerContentBounds);
128 return this->getChildInputLayerBounds(kInner, mapping, innerDesiredOutput, contentBounds);
129 }
130
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const131 std::optional<skif::LayerSpace<SkIRect>> SkComposeImageFilter::onGetOutputLayerBounds(
132 const skif::Mapping& mapping,
133 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
134 // The 'contentBounds' is processed by the inner filter, producing the content bounds for the
135 // outer filter of the composition, which then produces the final output bounds.
136 auto innerBounds = this->getChildOutputLayerBounds(kInner, mapping, contentBounds);
137 // NOTE: Even if innerBounds is unbounded, the outer image filter may be capable of restricting
138 // it if it contains a crop image filter.
139 return this->getChildOutputLayerBounds(kOuter, mapping, innerBounds);
140 }
141
computeFastBounds(const SkRect & src) const142 SkRect SkComposeImageFilter::computeFastBounds(const SkRect& src) const {
143 return this->getInput(kOuter)->computeFastBounds(
144 this->getInput(kInner)->computeFastBounds(src));
145 }
146