xref: /aosp_15_r20/external/skia/src/effects/imagefilters/SkMergeImageFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2012 The Android Open Source Project
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 
10 #include "include/core/SkFlattenable.h"
11 #include "include/core/SkImageFilter.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkTypes.h"
15 #include "src/core/SkImageFilterTypes.h"
16 #include "src/core/SkImageFilter_Base.h"
17 #include "src/core/SkReadBuffer.h"
18 
19 #include <optional>
20 #include <utility>
21 
22 namespace {
23 
24 class SkMergeImageFilter final : public SkImageFilter_Base {
25 public:
SkMergeImageFilter(sk_sp<SkImageFilter> * const filters,int count)26     SkMergeImageFilter(sk_sp<SkImageFilter>* const filters, int count)
27             : SkImageFilter_Base(filters, count) {
28         SkASSERT(filters && count > 0);
29     }
30 
31     SkRect computeFastBounds(const SkRect&) const override;
32 
33     // No need to override flatten() since there's no additional state to write over base class.
34 
35 private:
36     friend void ::SkRegisterMergeImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkMergeImageFilter)37     SK_FLATTENABLE_HOOKS(SkMergeImageFilter)
38 
39     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
40 
41     skif::FilterResult onFilterImage(const skif::Context& ctx) const override;
42 
43     skif::LayerSpace<SkIRect> onGetInputLayerBounds(
44             const skif::Mapping& mapping,
45             const skif::LayerSpace<SkIRect>& desiredOutput,
46             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
47 
48     std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
49             const skif::Mapping& mapping,
50             std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
51 };
52 
53 } // end namespace
54 
Merge(sk_sp<SkImageFilter> * const filters,int count,const CropRect & cropRect)55 sk_sp<SkImageFilter> SkImageFilters::Merge(sk_sp<SkImageFilter>* const filters, int count,
56                                            const CropRect& cropRect) {
57     if (count <= 0 || !filters) {
58         return SkImageFilters::Empty();
59     }
60 
61     sk_sp<SkImageFilter> filter{new SkMergeImageFilter(filters, count)};
62     if (cropRect) {
63         filter = SkImageFilters::Crop(*cropRect, std::move(filter));
64     }
65     return filter;
66 }
67 
SkRegisterMergeImageFilterFlattenable()68 void SkRegisterMergeImageFilterFlattenable() {
69     SK_REGISTER_FLATTENABLE(SkMergeImageFilter);
70     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
71     SkFlattenable::Register("SkMergeImageFilterImpl", SkMergeImageFilter::CreateProc);
72 }
73 
CreateProc(SkReadBuffer & buffer)74 sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
75     Common common;
76     if (!common.unflatten(buffer, -1) || !buffer.isValid()) {
77         return nullptr;
78     }
79     return SkImageFilters::Merge(common.inputs(), common.inputCount(), common.cropRect());
80 }
81 
82 ///////////////////////////////////////////////////////////////////////////////
83 
onFilterImage(const skif::Context & ctx) const84 skif::FilterResult SkMergeImageFilter::onFilterImage(const skif::Context& ctx) const {
85     const int inputCount = this->countInputs();
86     skif::FilterResult::Builder builder{ctx};
87     for (int i = 0; i < inputCount; ++i) {
88         builder.add(this->getChildOutput(i, ctx));
89     }
90     return builder.merge();
91 }
92 
onGetInputLayerBounds(const skif::Mapping & mapping,const skif::LayerSpace<SkIRect> & desiredOutput,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const93 skif::LayerSpace<SkIRect> SkMergeImageFilter::onGetInputLayerBounds(
94         const skif::Mapping& mapping,
95         const skif::LayerSpace<SkIRect>& desiredOutput,
96         std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
97     const int inputCount = this->countInputs();
98     // Union of all child input bounds so that one source image can provide for all of them.
99     return skif::LayerSpace<SkIRect>::Union(
100             inputCount,
101             [&](int i) {
102                 return this->getChildInputLayerBounds(i, mapping, desiredOutput, contentBounds);
103             });
104 }
105 
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>> contentBounds) const106 std::optional<skif::LayerSpace<SkIRect>> SkMergeImageFilter::onGetOutputLayerBounds(
107         const skif::Mapping& mapping,
108         std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
109     const int inputCount = this->countInputs();
110     // Merge is src-over of all child outputs, so covers their union but no more
111     bool childIsUnbounded = false;
112     auto childOutput = skif::LayerSpace<SkIRect>::Union(
113             inputCount,
114             [&](int i) {
115                 auto o = this->getChildOutputLayerBounds(i, mapping, contentBounds);
116                 if (o) {
117                     return *o;
118                 } else {
119                     childIsUnbounded = true;
120                     // This value doesn't matter once childIsUnbounded is true
121                     return skif::LayerSpace<SkIRect>::Empty();
122                 }
123             });
124     if (childIsUnbounded) {
125         return skif::LayerSpace<SkIRect>::Unbounded();
126     } else {
127         return childOutput;
128     }
129 }
130 
computeFastBounds(const SkRect & rect) const131 SkRect SkMergeImageFilter::computeFastBounds(const SkRect& rect) const {
132     // The base computeFastBounds() implementation is the union of all fast bounds from children,
133     // or 'rect' if there are none. For merge, zero children means zero output so only call the
134     // base implementation when there are filters to merge.
135     // TODO: When the bounds update is complete, this default implementation may go away and we
136     // can move the union'ing logic here.
137     return SkImageFilter_Base::computeFastBounds(rect);
138 }
139