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