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