1 /*
2 * Copyright 2015 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
10 #include "include/core/SkFlattenable.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkImageFilter.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkSamplingOptions.h"
17 #include "include/private/base/SkAssert.h"
18 #include "src/core/SkImageFilterTypes.h"
19 #include "src/core/SkImageFilter_Base.h"
20 #include "src/core/SkPicturePriv.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkSamplingPriv.h"
23 #include "src/core/SkWriteBuffer.h"
24
25 #include <optional>
26 #include <utility>
27
28 namespace {
29
30 class SkImageImageFilter final : public SkImageFilter_Base {
31 public:
SkImageImageFilter(sk_sp<SkImage> image,const SkRect & srcRect,const SkRect & dstRect,const SkSamplingOptions & sampling)32 SkImageImageFilter(sk_sp<SkImage> image,
33 const SkRect& srcRect,
34 const SkRect& dstRect,
35 const SkSamplingOptions& sampling)
36 : SkImageFilter_Base(nullptr, 0)
37 , fImage(std::move(image))
38 , fSrcRect(srcRect)
39 , fDstRect(dstRect)
40 , fSampling(sampling) {
41 // The dst rect should be non-empty
42 SkASSERT(fImage && !dstRect.isEmpty());
43 }
44
computeFastBounds(const SkRect &) const45 SkRect computeFastBounds(const SkRect&) const override { return SkRect(fDstRect); }
46
47 protected:
48 void flatten(SkWriteBuffer&) const override;
49
50 private:
51 friend void ::SkRegisterImageImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkImageImageFilter)52 SK_FLATTENABLE_HOOKS(SkImageImageFilter)
53
54 MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
55
56 skif::FilterResult onFilterImage(const skif::Context&) const override;
57
58 skif::LayerSpace<SkIRect> onGetInputLayerBounds(
59 const skif::Mapping& mapping,
60 const skif::LayerSpace<SkIRect>& desiredOutput,
61 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
62
63 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
64 const skif::Mapping& mapping,
65 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
66
67 sk_sp<SkImage> fImage;
68 // The src rect is relative to the image's contents, so is not technically in the parameter
69 // coordinate space that responds to the layer matrix (unlike fDstRect).
70 SkRect fSrcRect;
71 skif::ParameterSpace<SkRect> fDstRect;
72 SkSamplingOptions fSampling;
73 };
74
75 } // end namespace
76
Image(sk_sp<SkImage> image,const SkRect & srcRect,const SkRect & dstRect,const SkSamplingOptions & sampling)77 sk_sp<SkImageFilter> SkImageFilters::Image(sk_sp<SkImage> image,
78 const SkRect& srcRect,
79 const SkRect& dstRect,
80 const SkSamplingOptions& sampling) {
81 if (srcRect.isEmpty() || dstRect.isEmpty() || !image) {
82 // There is no content to draw, so the filter should produce transparent black
83 return SkImageFilters::Empty();
84 } else {
85 SkRect imageBounds = SkRect::Make(image->dimensions());
86 if (imageBounds.contains(srcRect)) {
87 // No change to srcRect and dstRect needed
88 return sk_sp<SkImageFilter>(new SkImageImageFilter(
89 std::move(image), srcRect, dstRect, sampling));
90 } else {
91 SkMatrix srcToDst = SkMatrix::RectToRect(srcRect, dstRect);
92 if (!imageBounds.intersect(srcRect)) {
93 // No overlap, so draw empty
94 return SkImageFilters::Empty();
95 }
96
97 // Adjust dstRect to match the updated src (which is stored in imageBounds)
98 SkRect mappedBounds = srcToDst.mapRect(imageBounds);
99 if (mappedBounds.isEmpty()) {
100 return SkImageFilters::Empty();
101 }
102 return sk_sp<SkImageFilter>(new SkImageImageFilter(
103 std::move(image), imageBounds, mappedBounds, sampling));
104 }
105 }
106 }
107
SkRegisterImageImageFilterFlattenable()108 void SkRegisterImageImageFilterFlattenable() {
109 SK_REGISTER_FLATTENABLE(SkImageImageFilter);
110 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
111 SkFlattenable::Register("SkImageSourceImpl", SkImageImageFilter::CreateProc);
112 }
113
CreateProc(SkReadBuffer & buffer)114 sk_sp<SkFlattenable> SkImageImageFilter::CreateProc(SkReadBuffer& buffer) {
115 SkSamplingOptions sampling;
116 if (buffer.isVersionLT(SkPicturePriv::kImageFilterImageSampling_Version)) {
117 sampling = SkSamplingPriv::FromFQ(buffer.checkFilterQuality(), kLinear_SkMediumAs);
118 } else {
119 sampling = buffer.readSampling();
120 }
121
122 SkRect src, dst;
123 buffer.readRect(&src);
124 buffer.readRect(&dst);
125
126 sk_sp<SkImage> image(buffer.readImage());
127 if (!image) {
128 return nullptr;
129 }
130
131 return SkImageFilters::Image(std::move(image), src, dst, sampling);
132 }
133
flatten(SkWriteBuffer & buffer) const134 void SkImageImageFilter::flatten(SkWriteBuffer& buffer) const {
135 buffer.writeSampling(fSampling);
136 buffer.writeRect(fSrcRect);
137 buffer.writeRect(SkRect(fDstRect));
138 buffer.writeImage(fImage.get());
139 }
140
141 ///////////////////////////////////////////////////////////////////////////////////////////////////
142
onFilterImage(const skif::Context & ctx) const143 skif::FilterResult SkImageImageFilter::onFilterImage(const skif::Context& ctx) const {
144 return skif::FilterResult::MakeFromImage(ctx, fImage, fSrcRect, fDstRect, fSampling);
145 }
146
onGetInputLayerBounds(const skif::Mapping &,const skif::LayerSpace<SkIRect> &,std::optional<skif::LayerSpace<SkIRect>>) const147 skif::LayerSpace<SkIRect> SkImageImageFilter::onGetInputLayerBounds(
148 const skif::Mapping&,
149 const skif::LayerSpace<SkIRect>&,
150 std::optional<skif::LayerSpace<SkIRect>>) const {
151 // This is a leaf filter, it requires no input and no further recursion
152 return skif::LayerSpace<SkIRect>::Empty();
153 }
154
onGetOutputLayerBounds(const skif::Mapping & mapping,std::optional<skif::LayerSpace<SkIRect>>) const155 std::optional<skif::LayerSpace<SkIRect>> SkImageImageFilter::onGetOutputLayerBounds(
156 const skif::Mapping& mapping,
157 std::optional<skif::LayerSpace<SkIRect>>) const {
158 // The output is the transformed bounds of the image.
159 return mapping.paramToLayer(fDstRect).roundOut();
160 }
161