• 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 #include "src/core/SkImageFilter_Base.h"
10 #include "src/core/SkReadBuffer.h"
11 #include "src/core/SkSpecialImage.h"
12 #include "src/core/SkWriteBuffer.h"
13 
14 namespace {
15 
16 class SkComposeImageFilter final : public SkImageFilter_Base {
17 public:
SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])18     explicit SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])
19             : INHERITED(inputs, 2, nullptr) {
20         SkASSERT(inputs[0].get());
21         SkASSERT(inputs[1].get());
22     }
23 
24     SkRect computeFastBounds(const SkRect& src) const override;
25 
26 protected:
27     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
28     SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
29                            MapDirection, const SkIRect* inputRect) const override;
onGetCTMCapability() const30     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
31 
32 private:
33     friend void ::SkRegisterComposeImageFilterFlattenable();
34     SK_FLATTENABLE_HOOKS(SkComposeImageFilter)
35 
36     using INHERITED = SkImageFilter_Base;
37 };
38 
39 } // end namespace
40 
Compose(sk_sp<SkImageFilter> outer,sk_sp<SkImageFilter> inner)41 sk_sp<SkImageFilter> SkImageFilters::Compose(sk_sp<SkImageFilter> outer,
42                                              sk_sp<SkImageFilter> inner) {
43     if (!outer) {
44         return inner;
45     }
46     if (!inner) {
47         return outer;
48     }
49     sk_sp<SkImageFilter> inputs[2] = { std::move(outer), std::move(inner) };
50     return sk_sp<SkImageFilter>(new SkComposeImageFilter(inputs));
51 }
52 
SkRegisterComposeImageFilterFlattenable()53 void SkRegisterComposeImageFilterFlattenable() {
54     SK_REGISTER_FLATTENABLE(SkComposeImageFilter);
55     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
56     SkFlattenable::Register("SkComposeImageFilterImpl", SkComposeImageFilter::CreateProc);
57 }
58 
CreateProc(SkReadBuffer & buffer)59 sk_sp<SkFlattenable> SkComposeImageFilter::CreateProc(SkReadBuffer& buffer) {
60     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
61     return SkImageFilters::Compose(common.getInput(0), common.getInput(1));
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////////////////////////
65 
computeFastBounds(const SkRect & src) const66 SkRect SkComposeImageFilter::computeFastBounds(const SkRect& src) const {
67     const SkImageFilter* outer = this->getInput(0);
68     const SkImageFilter* inner = this->getInput(1);
69 
70     return outer->computeFastBounds(inner->computeFastBounds(src));
71 }
72 
onFilterImage(const Context & ctx,SkIPoint * offset) const73 sk_sp<SkSpecialImage> SkComposeImageFilter::onFilterImage(const Context& ctx,
74                                                           SkIPoint* offset) const {
75     // The bounds passed to the inner filter must be filtered by the outer
76     // filter, so that the inner filter produces the pixels that the outer
77     // filter requires as input. This matters if the outer filter moves pixels.
78     SkIRect innerClipBounds;
79     innerClipBounds = this->getInput(0)->filterBounds(ctx.clipBounds(), ctx.ctm(),
80                                                       kReverse_MapDirection, &ctx.clipBounds());
81     Context innerContext = ctx.withNewDesiredOutput(skif::LayerSpace<SkIRect>(innerClipBounds));
82     SkIPoint innerOffset = SkIPoint::Make(0, 0);
83     sk_sp<SkSpecialImage> inner(this->filterInput(1, innerContext, &innerOffset));
84     if (!inner) {
85         return nullptr;
86     }
87 
88     // TODO (michaelludwig) - Once all filters are updated to process coordinate spaces more
89     // robustly, we can allow source images to have non-(0,0) origins, which will mean that the
90     // CTM/clipBounds modifications for the outerContext can go away.
91     SkMatrix outerMatrix(ctx.ctm());
92     outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y()));
93     SkIRect clipBounds = ctx.clipBounds();
94     clipBounds.offset(-innerOffset.x(), -innerOffset.y());
95     // NOTE: This is the only spot in image filtering where the source image of the context
96     // is not constant for the entire DAG evaluation. Given that the inner and outer DAG branches
97     // were already created, there's no alternative way for the leaf nodes of the outer DAG to
98     // get the results of the inner DAG. Overriding the source image of the context has the correct
99     // effect, but means that the source image is not fixed for the entire filter process.
100     Context outerContext(outerMatrix, clipBounds, ctx.cache(), ctx.colorType(), ctx.colorSpace(),
101                          inner.get());
102 
103     SkIPoint outerOffset = SkIPoint::Make(0, 0);
104     sk_sp<SkSpecialImage> outer(this->filterInput(0, outerContext, &outerOffset));
105     if (!outer) {
106         return nullptr;
107     }
108 
109     *offset = innerOffset + outerOffset;
110     return outer;
111 }
112 
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const113 SkIRect SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
114                                              MapDirection dir, const SkIRect* inputRect) const {
115     const SkImageFilter* outer = this->getInput(0);
116     const SkImageFilter* inner = this->getInput(1);
117 
118     if (dir == kReverse_MapDirection) {
119         // The output 'src' is processed by the outer filter, producing its required input bounds,
120         // which is then the output bounds required of the inner filter. We pass the inputRect to
121         // outer and not inner to match the default recursion logic of onGetInputLayerBounds
122         const SkIRect outerRect = outer->filterBounds(src, ctm, dir, inputRect);
123         return inner->filterBounds(outerRect, ctm, dir);
124     } else {
125         // The input 'src' is processed by the inner filter, producing the input bounds for the
126         // outer filter of the composition, which then produces the final forward output bounds
127         const SkIRect innerRect = inner->filterBounds(src, ctm, dir);
128         return outer->filterBounds(innerRect, ctm, dir);
129     }
130 }
131