| /* |
| * Copyright 2012 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/effects/SkImageFilters.h" |
| |
| #include "include/core/SkFlattenable.h" |
| #include "include/core/SkImageFilter.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkTypes.h" |
| #include "src/core/SkImageFilterTypes.h" |
| #include "src/core/SkImageFilter_Base.h" |
| #include "src/core/SkReadBuffer.h" |
| |
| #include <optional> |
| #include <utility> |
| |
| namespace { |
| |
| class SkMergeImageFilter final : public SkImageFilter_Base { |
| public: |
| SkMergeImageFilter(sk_sp<SkImageFilter>* const filters, int count) |
| : SkImageFilter_Base(filters, count) { |
| SkASSERT(filters && count > 0); |
| } |
| |
| SkRect computeFastBounds(const SkRect&) const override; |
| |
| // No need to override flatten() since there's no additional state to write over base class. |
| |
| private: |
| friend void ::SkRegisterMergeImageFilterFlattenable(); |
| SK_FLATTENABLE_HOOKS(SkMergeImageFilter) |
| |
| MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; } |
| |
| skif::FilterResult onFilterImage(const skif::Context& ctx) const override; |
| |
| skif::LayerSpace<SkIRect> onGetInputLayerBounds( |
| const skif::Mapping& mapping, |
| const skif::LayerSpace<SkIRect>& desiredOutput, |
| std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override; |
| |
| std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds( |
| const skif::Mapping& mapping, |
| std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override; |
| }; |
| |
| } // end namespace |
| |
| sk_sp<SkImageFilter> SkImageFilters::Merge(sk_sp<SkImageFilter>* const filters, int count, |
| const CropRect& cropRect) { |
| if (count <= 0 || !filters) { |
| return SkImageFilters::Empty(); |
| } |
| |
| sk_sp<SkImageFilter> filter{new SkMergeImageFilter(filters, count)}; |
| if (cropRect) { |
| filter = SkImageFilters::Crop(*cropRect, std::move(filter)); |
| } |
| return filter; |
| } |
| |
| void SkRegisterMergeImageFilterFlattenable() { |
| SK_REGISTER_FLATTENABLE(SkMergeImageFilter); |
| // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name |
| SkFlattenable::Register("SkMergeImageFilterImpl", SkMergeImageFilter::CreateProc); |
| } |
| |
| sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) { |
| Common common; |
| if (!common.unflatten(buffer, -1) || !buffer.isValid()) { |
| return nullptr; |
| } |
| return SkImageFilters::Merge(common.inputs(), common.inputCount(), common.cropRect()); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| skif::FilterResult SkMergeImageFilter::onFilterImage(const skif::Context& ctx) const { |
| const int inputCount = this->countInputs(); |
| skif::FilterResult::Builder builder{ctx}; |
| for (int i = 0; i < inputCount; ++i) { |
| builder.add(this->getChildOutput(i, ctx)); |
| } |
| return builder.merge(); |
| } |
| |
| skif::LayerSpace<SkIRect> SkMergeImageFilter::onGetInputLayerBounds( |
| const skif::Mapping& mapping, |
| const skif::LayerSpace<SkIRect>& desiredOutput, |
| std::optional<skif::LayerSpace<SkIRect>> contentBounds) const { |
| const int inputCount = this->countInputs(); |
| // Union of all child input bounds so that one source image can provide for all of them. |
| return skif::LayerSpace<SkIRect>::Union( |
| inputCount, |
| [&](int i) { |
| return this->getChildInputLayerBounds(i, mapping, desiredOutput, contentBounds); |
| }); |
| } |
| |
| std::optional<skif::LayerSpace<SkIRect>> SkMergeImageFilter::onGetOutputLayerBounds( |
| const skif::Mapping& mapping, |
| std::optional<skif::LayerSpace<SkIRect>> contentBounds) const { |
| const int inputCount = this->countInputs(); |
| // Merge is src-over of all child outputs, so covers their union but no more |
| bool childIsUnbounded = false; |
| auto childOutput = skif::LayerSpace<SkIRect>::Union( |
| inputCount, |
| [&](int i) { |
| auto o = this->getChildOutputLayerBounds(i, mapping, contentBounds); |
| if (o) { |
| return *o; |
| } else { |
| childIsUnbounded = true; |
| // This value doesn't matter once childIsUnbounded is true |
| return skif::LayerSpace<SkIRect>::Empty(); |
| } |
| }); |
| if (childIsUnbounded) { |
| return skif::LayerSpace<SkIRect>::Unbounded(); |
| } else { |
| return childOutput; |
| } |
| } |
| |
| SkRect SkMergeImageFilter::computeFastBounds(const SkRect& rect) const { |
| // The base computeFastBounds() implementation is the union of all fast bounds from children, |
| // or 'rect' if there are none. For merge, zero children means zero output so only call the |
| // base implementation when there are filters to merge. |
| // TODO: When the bounds update is complete, this default implementation may go away and we |
| // can move the union'ing logic here. |
| return SkImageFilter_Base::computeFastBounds(rect); |
| } |