• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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