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