• 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/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/SkScalar.h"
15 #include "include/core/SkTypes.h"
16 #include "include/effects/SkImageFilters.h"
17 #include "src/core/SkImageFilterTypes.h"
18 #include "src/core/SkImageFilter_Base.h"
19 #include "src/core/SkSpecialImage.h"
20 
21 #include <utility>
22 
23 class SkReadBuffer;
24 
25 namespace {
26 
27 class SkComposeImageFilter final : public SkImageFilter_Base {
28 public:
SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])29     explicit SkComposeImageFilter(sk_sp<SkImageFilter> inputs[2])
30             : INHERITED(inputs, 2, nullptr) {
31         SkASSERT(inputs[0].get());
32         SkASSERT(inputs[1].get());
33     }
34 
35     SkRect computeFastBounds(const SkRect& src) const override;
36 
37 protected:
38     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
39     SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
40                            MapDirection, const SkIRect* inputRect) const override;
onGetCTMCapability() const41     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
42 
43 private:
44     friend void ::SkRegisterComposeImageFilterFlattenable();
45     SK_FLATTENABLE_HOOKS(SkComposeImageFilter)
46 
47     using INHERITED = SkImageFilter_Base;
48 };
49 
50 } // end namespace
51 
Compose(sk_sp<SkImageFilter> outer,sk_sp<SkImageFilter> inner)52 sk_sp<SkImageFilter> SkImageFilters::Compose(sk_sp<SkImageFilter> outer,
53                                              sk_sp<SkImageFilter> inner) {
54     if (!outer) {
55         return inner;
56     }
57     if (!inner) {
58         return outer;
59     }
60     sk_sp<SkImageFilter> inputs[2] = { std::move(outer), std::move(inner) };
61     return sk_sp<SkImageFilter>(new SkComposeImageFilter(inputs));
62 }
63 
SkRegisterComposeImageFilterFlattenable()64 void SkRegisterComposeImageFilterFlattenable() {
65     SK_REGISTER_FLATTENABLE(SkComposeImageFilter);
66     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
67     SkFlattenable::Register("SkComposeImageFilterImpl", SkComposeImageFilter::CreateProc);
68 }
69 
CreateProc(SkReadBuffer & buffer)70 sk_sp<SkFlattenable> SkComposeImageFilter::CreateProc(SkReadBuffer& buffer) {
71     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
72     return SkImageFilters::Compose(common.getInput(0), common.getInput(1));
73 }
74 
75 ///////////////////////////////////////////////////////////////////////////////////////////////////
76 
computeFastBounds(const SkRect & src) const77 SkRect SkComposeImageFilter::computeFastBounds(const SkRect& src) const {
78     const SkImageFilter* outer = this->getInput(0);
79     const SkImageFilter* inner = this->getInput(1);
80 
81     return outer->computeFastBounds(inner->computeFastBounds(src));
82 }
83 
onFilterImage(const Context & ctx,SkIPoint * offset) const84 sk_sp<SkSpecialImage> SkComposeImageFilter::onFilterImage(const Context& ctx,
85                                                           SkIPoint* offset) const {
86     // The bounds passed to the inner filter must be filtered by the outer
87     // filter, so that the inner filter produces the pixels that the outer
88     // filter requires as input. This matters if the outer filter moves pixels.
89     SkIRect innerClipBounds;
90     innerClipBounds = this->getInput(0)->filterBounds(ctx.clipBounds(), ctx.ctm(),
91                                                       kReverse_MapDirection, &ctx.clipBounds());
92     Context innerContext = ctx.withNewDesiredOutput(skif::LayerSpace<SkIRect>(innerClipBounds));
93     SkIPoint innerOffset = SkIPoint::Make(0, 0);
94     sk_sp<SkSpecialImage> inner(this->filterInput(1, innerContext, &innerOffset));
95     if (!inner) {
96         return nullptr;
97     }
98 
99     // TODO (michaelludwig) - Once all filters are updated to process coordinate spaces more
100     // robustly, we can allow source images to have non-(0,0) origins, which will mean that the
101     // CTM/clipBounds modifications for the outerContext can go away.
102     SkMatrix outerMatrix(ctx.ctm());
103     outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y()));
104     SkIRect clipBounds = ctx.clipBounds();
105     clipBounds.offset(-innerOffset.x(), -innerOffset.y());
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     Context outerContext(outerMatrix, clipBounds, ctx.cache(), ctx.colorType(), ctx.colorSpace(),
112                          inner.get());
113 
114     SkIPoint outerOffset = SkIPoint::Make(0, 0);
115     sk_sp<SkSpecialImage> outer(this->filterInput(0, outerContext, &outerOffset));
116     if (!outer) {
117         return nullptr;
118     }
119 
120     *offset = innerOffset + outerOffset;
121     return outer;
122 }
123 
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const124 SkIRect SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
125                                              MapDirection dir, const SkIRect* inputRect) const {
126     const SkImageFilter* outer = this->getInput(0);
127     const SkImageFilter* inner = this->getInput(1);
128 
129     if (dir == kReverse_MapDirection) {
130         // The output 'src' is processed by the outer filter, producing its required input bounds,
131         // which is then the output bounds required of the inner filter. We pass the inputRect to
132         // outer and not inner to match the default recursion logic of onGetInputLayerBounds
133         const SkIRect outerRect = outer->filterBounds(src, ctm, dir, inputRect);
134         return inner->filterBounds(outerRect, ctm, dir);
135     } else {
136         // The input 'src' is processed by the inner filter, producing the input bounds for the
137         // outer filter of the composition, which then produces the final forward output bounds
138         const SkIRect innerRect = inner->filterBounds(src, ctm, dir);
139         return outer->filterBounds(innerRect, ctm, dir);
140     }
141 }
142