1 /*
2  * Copyright 2021 Google LLC
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 "src/effects/imagefilters/SkRuntimeImageFilter.h"
9 
10 #ifdef SK_ENABLE_SKSL
11 
12 #include "include/core/SkBlendMode.h"
13 #include "include/core/SkCanvas.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkFlattenable.h"
16 #include "include/core/SkImageFilter.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkPaint.h"
19 #include "include/core/SkPoint.h"
20 #include "include/core/SkRect.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkShader.h"
23 #include "include/core/SkSpan.h"
24 #include "include/core/SkString.h"
25 #include "include/effects/SkImageFilters.h"
26 #include "include/effects/SkRuntimeEffect.h"
27 #include "include/private/SkSpinlock.h"
28 #include "include/private/base/SkTArray.h"
29 #include "src/core/SkImageFilterTypes.h"
30 #include "src/core/SkImageFilter_Base.h"
31 #include "src/core/SkReadBuffer.h"
32 #include "src/core/SkRuntimeEffectPriv.h"
33 #include "src/core/SkSpecialImage.h"
34 #include "src/core/SkSpecialSurface.h"
35 #include "src/core/SkWriteBuffer.h"
36 
37 #include <cstddef>
38 #include <string>
39 #include <string_view>
40 #include <utility>
41 
42 class SkRuntimeImageFilter final : public SkImageFilter_Base {
43 public:
SkRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,sk_sp<SkImageFilter> input)44     SkRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
45                          sk_sp<SkData> uniforms,
46                          sk_sp<SkImageFilter> input)
47             : INHERITED(&input, 1, /*cropRect=*/nullptr)
48             , fShaderBuilder(std::move(effect), std::move(uniforms)) {
49         std::string_view childName = fShaderBuilder.effect()->children().front().name;
50         fChildShaderNames.push_back(SkString(childName));
51     }
SkRuntimeImageFilter(const SkRuntimeShaderBuilder & builder,std::string_view childShaderNames[],const sk_sp<SkImageFilter> inputs[],int inputCount)52     SkRuntimeImageFilter(const SkRuntimeShaderBuilder& builder,
53                          std::string_view childShaderNames[],
54                          const sk_sp<SkImageFilter> inputs[],
55                          int inputCount)
56             : INHERITED(inputs, inputCount, /*cropRect=*/nullptr)
57             , fShaderBuilder(builder) {
58         fChildShaderNames.reserve_back(inputCount);
59         for (int i = 0; i < inputCount; i++) {
60             fChildShaderNames.push_back(SkString(childShaderNames[i]));
61         }
62     }
63 
onAffectsTransparentBlack() const64     bool onAffectsTransparentBlack() const override { return true; }
onGetCTMCapability() const65     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; }
66 
67 protected:
68     void flatten(SkWriteBuffer&) const override;
69     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
70 
71 private:
72     friend void ::SkRegisterRuntimeImageFilterFlattenable();
73     SK_FLATTENABLE_HOOKS(SkRuntimeImageFilter)
74 
75     mutable SkSpinlock fShaderBuilderLock;
76     mutable SkRuntimeShaderBuilder fShaderBuilder;
77     SkSTArray<1, SkString> fChildShaderNames;
78 
79     using INHERITED = SkImageFilter_Base;
80 };
81 
SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,sk_sp<SkImageFilter> input)82 sk_sp<SkImageFilter> SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
83                                               sk_sp<SkData> uniforms,
84                                               sk_sp<SkImageFilter> input) {
85     // Rather than replicate all of the checks from makeShader here, just try to create a shader
86     // once, to determine if everything is valid.
87     sk_sp<SkShader> child = nullptr;
88     auto shader = effect->makeShader(uniforms, &child, 1);
89     if (!shader) {
90         // Could be wrong signature, wrong uniform block size, wrong number/type of children, etc...
91         return nullptr;
92     }
93 
94     return sk_sp<SkImageFilter>(
95             new SkRuntimeImageFilter(std::move(effect), std::move(uniforms), std::move(input)));
96 }
97 
SkRegisterRuntimeImageFilterFlattenable()98 void SkRegisterRuntimeImageFilterFlattenable() {
99     SK_REGISTER_FLATTENABLE(SkRuntimeImageFilter);
100 }
101 
CreateProc(SkReadBuffer & buffer)102 sk_sp<SkFlattenable> SkRuntimeImageFilter::CreateProc(SkReadBuffer& buffer) {
103     // We don't know how many inputs to expect yet. Passing -1 allows any number of children.
104     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, -1);
105     if (common.cropRect()) {
106         return nullptr;
107     }
108 
109     // Read the SkSL string and convert it into a runtime effect
110     SkString sksl;
111     buffer.readString(&sksl);
112     auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
113     if (!buffer.validate(effect != nullptr)) {
114         return nullptr;
115     }
116 
117     // Read the uniform data and make sure it matches the size from the runtime effect
118     sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
119     if (!buffer.validate(uniforms->size() == effect->uniformSize())) {
120         return nullptr;
121     }
122 
123     // Read the child shader names
124     SkSTArray<4, std::string_view> childShaderNames;
125     SkSTArray<4, SkString> childShaderNameStrings;
126     childShaderNames.resize(common.inputCount());
127     childShaderNameStrings.resize(common.inputCount());
128     for (int i = 0; i < common.inputCount(); i++) {
129         buffer.readString(&childShaderNameStrings[i]);
130         childShaderNames[i] = childShaderNameStrings[i].c_str();
131     }
132 
133     SkRuntimeShaderBuilder builder(std::move(effect), std::move(uniforms));
134 
135     // Populate the builder with the corresponding children
136     for (const SkRuntimeEffect::Child& child : builder.effect()->children()) {
137         std::string_view name = child.name;
138         switch (child.type) {
139             case SkRuntimeEffect::ChildType::kBlender: {
140                 builder.child(name) = buffer.readBlender();
141                 break;
142             }
143             case SkRuntimeEffect::ChildType::kColorFilter: {
144                 builder.child(name) = buffer.readColorFilter();
145                 break;
146             }
147             case SkRuntimeEffect::ChildType::kShader: {
148                 builder.child(name) = buffer.readShader();
149                 break;
150             }
151         }
152     }
153 
154     if (!buffer.isValid()) {
155         return nullptr;
156     }
157 
158     return SkImageFilters::RuntimeShader(builder, childShaderNames.data(),
159                                          common.inputs(), common.inputCount());
160 }
161 
flatten(SkWriteBuffer & buffer) const162 void SkRuntimeImageFilter::flatten(SkWriteBuffer& buffer) const {
163     this->INHERITED::flatten(buffer);
164     fShaderBuilderLock.acquire();
165     buffer.writeString(fShaderBuilder.effect()->source().c_str());
166     buffer.writeDataAsByteArray(fShaderBuilder.uniforms().get());
167     for (const SkString& name : fChildShaderNames) {
168         buffer.writeString(name.c_str());
169     }
170     for (size_t x = 0; x < fShaderBuilder.children().size(); x++) {
171         buffer.writeFlattenable(fShaderBuilder.children()[x].flattenable());
172     }
173     fShaderBuilderLock.release();
174 }
175 
176 ///////////////////////////////////////////////////////////////////////////////////////////////////
177 
onFilterImage(const Context & ctx,SkIPoint * offset) const178 sk_sp<SkSpecialImage> SkRuntimeImageFilter::onFilterImage(const Context& ctx,
179                                                           SkIPoint* offset) const {
180     SkIRect outputBounds = SkIRect(ctx.desiredOutput());
181     sk_sp<SkSpecialSurface> surf(ctx.makeSurface(outputBounds.size()));
182     if (!surf) {
183         return nullptr;
184     }
185 
186     SkMatrix ctm = ctx.ctm();
187     SkMatrix inverse;
188     SkAssertResult(ctm.invert(&inverse));
189 
190     const int inputCount = this->countInputs();
191     SkASSERT(inputCount == fChildShaderNames.size());
192 
193     SkSTArray<1, sk_sp<SkShader>> inputShaders;
194     for (int i = 0; i < inputCount; i++) {
195         SkIPoint inputOffset = SkIPoint::Make(0, 0);
196         sk_sp<SkSpecialImage> input(this->filterInput(i, ctx, &inputOffset));
197         if (!input) {
198             return nullptr;
199         }
200 
201         SkMatrix localM = inverse * SkMatrix::Translate(inputOffset);
202         sk_sp<SkShader> inputShader =
203                 input->asShader(SkSamplingOptions(SkFilterMode::kLinear), localM);
204         SkASSERT(inputShader);
205         inputShaders.push_back(std::move(inputShader));
206     }
207 
208     // lock the mutation of the builder and creation of the shader so that the builder's state is
209     // const and is safe for multi-threaded access.
210     fShaderBuilderLock.acquire();
211     for (int i = 0; i < inputCount; i++) {
212         fShaderBuilder.child(fChildShaderNames[i].c_str()) = inputShaders[i];
213     }
214     sk_sp<SkShader> shader = fShaderBuilder.makeShader();
215     // Remove the inputs from the builder to avoid unnecessarily prolonging the shader's lifetime
216     for (int i = 0; i < inputCount; i++) {
217         fShaderBuilder.child(fChildShaderNames[i].c_str()) = nullptr;
218     }
219     fShaderBuilderLock.release();
220 
221     SkASSERT(shader.get());
222 
223     SkPaint paint;
224     paint.setShader(std::move(shader));
225     paint.setBlendMode(SkBlendMode::kSrc);
226 
227     SkCanvas* canvas = surf->getCanvas();
228     SkASSERT(canvas);
229 
230     // Translate from layer space into surf's image space
231     canvas->translate(-outputBounds.fLeft, -outputBounds.fTop);
232     // Ensure shader parameters are relative to parameter space, not layer space
233     canvas->concat(ctx.ctm());
234 
235     canvas->drawPaint(paint);
236 
237     *offset = outputBounds.topLeft();
238     return surf->makeImageSnapshot();
239 }
240 
child_is_shader(const SkRuntimeEffect::Child * child)241 static bool child_is_shader(const SkRuntimeEffect::Child* child) {
242     return child && child->type == SkRuntimeEffect::ChildType::kShader;
243 }
244 
RuntimeShader(const SkRuntimeShaderBuilder & builder,std::string_view childShaderName,sk_sp<SkImageFilter> input)245 sk_sp<SkImageFilter> SkImageFilters::RuntimeShader(const SkRuntimeShaderBuilder& builder,
246                                                    std::string_view childShaderName,
247                                                    sk_sp<SkImageFilter> input) {
248     // If no childShaderName is provided, check to see if we can implicitly assign it to the only
249     // child in the effect.
250     if (childShaderName.empty()) {
251         auto children = builder.effect()->children();
252         if (children.size() != 1) {
253             return nullptr;
254         }
255         childShaderName = children.front().name;
256     }
257 
258     return SkImageFilters::RuntimeShader(builder, &childShaderName, &input, 1);
259 }
260 
RuntimeShader(const SkRuntimeShaderBuilder & builder,std::string_view childShaderNames[],const sk_sp<SkImageFilter> inputs[],int inputCount)261 sk_sp<SkImageFilter> SkImageFilters::RuntimeShader(const SkRuntimeShaderBuilder& builder,
262                                                    std::string_view childShaderNames[],
263                                                    const sk_sp<SkImageFilter> inputs[],
264                                                    int inputCount) {
265     for (int i = 0; i < inputCount; i++) {
266         std::string_view name = childShaderNames[i];
267         // All names must be non-empty, and present as a child shader in the effect:
268         if (name.empty() || !child_is_shader(builder.effect()->findChild(name))) {
269             return nullptr;
270         }
271 
272         // We don't allow duplicates, either:
273         for (int j = 0; j < i; j++) {
274             if (name == childShaderNames[j]) {
275                 return nullptr;
276             }
277         }
278     }
279 
280     return sk_sp<SkImageFilter>(new SkRuntimeImageFilter(builder, childShaderNames,
281                                                          inputs, inputCount));
282 }
283 
284 #endif  // SK_ENABLE_SKSL
285