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