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