1 /*
2 * Copyright 2021 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 "modules/skottie/src/effects/Effects.h"
9
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "modules/skottie/src/Adapter.h"
12 #include "modules/skottie/src/SkottieJson.h"
13 #include "modules/skottie/src/SkottieValue.h"
14 #include "modules/sksg/include/SkSGColorFilter.h"
15
16 namespace skottie::internal {
17
18 #ifdef SK_ENABLE_SKSL
19
20 namespace {
21 class SkSLShaderNode final : public sksg::CustomRenderNode {
22 public:
SkSLShaderNode(sk_sp<RenderNode> child)23 explicit SkSLShaderNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {}
24
25 SG_ATTRIBUTE(Shader, sk_sp<SkShader>, fEffectShader)
26 private:
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)27 SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
28 const auto& child = this->children()[0];
29 return child->revalidate(ic, ctm);
30 }
31
onRender(SkCanvas * canvas,const RenderContext * ctx) const32 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
33 const auto& bounds = this->bounds();
34 const auto local_ctx = ScopedRenderContext(canvas, ctx)
35 .setIsolation(bounds, canvas->getTotalMatrix(), true);
36
37 canvas->saveLayer(&bounds, nullptr);
38 this->children()[0]->render(canvas, local_ctx);
39
40 SkPaint effect_paint;
41 effect_paint.setShader(fEffectShader);
42 effect_paint.setBlendMode(SkBlendMode::kSrcIn);
43
44 canvas->drawPaint(effect_paint);
45 }
46
onNodeAt(const SkPoint &) const47 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
48
49 sk_sp<SkShader> fEffectShader;
50
51 using INHERITED = sksg::CustomRenderNode;
52 };
53
54 class SkSLEffectAdapter final : public DiscardableAdapterBase<SkSLEffectAdapter,
55 SkSLShaderNode> {
56 public:
SkSLEffectAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<SkSLShaderNode> node)57 SkSLEffectAdapter(const skjson::ArrayValue& jprops,
58 const AnimationBuilder& abuilder,
59 sk_sp<SkSLShaderNode> node)
60 : INHERITED(std::move(node))
61 {
62 enum : size_t {
63 kSkSL_index = 0,
64 kFirstUniform_index = 1,
65 };
66 if (jprops.size() < 1) {
67 return;
68 }
69 const skjson::ObjectValue* jSkSL = jprops[kSkSL_index];
70 if (!jSkSL) {
71 return;
72 }
73 const skjson::StringValue* jShader = (*jSkSL)["sh"];
74 if (!jShader) {
75 return;
76 }
77 SkString shader = SkString(jShader->begin(), jShader->size());
78 auto result = SkRuntimeEffect::MakeForShader(shader, {});
79 if (!result.effect) {
80 abuilder.log(Logger::Level::kError, nullptr, "Failed to parse SkSL shader: %s",
81 result.errorText.c_str());
82 return;
83 }
84 fEffect = std::move(result.effect);
85
86 // construct dynamic uniform list from jprops, skip SkSL property
87 for (size_t i = kFirstUniform_index; i < jprops.size(); i++) {
88 const skjson::ObjectValue* jprop = jprops[i];
89 if (!jprop) { continue; }
90 const skjson::StringValue* uniformName = (*jprop)["nm"];
91 if (!uniformName) { continue; }
92 auto uniformTuple = std::make_tuple(SkString(uniformName->begin(),
93 uniformName->size()),
94 std::make_unique<VectorValue>());
95 fUniforms.push_back(std::move(uniformTuple));
96 this->bind(abuilder, (*jprop)["v"], std::get<1>(fUniforms.back()).get());
97 }
98 }
99
100 private:
onSync()101 void onSync() override {
102 this->node()->setShader(buildEffectShader());
103 }
104
buildEffectShader() const105 sk_sp<SkShader> buildEffectShader() const {
106 if (!fEffect) {
107 return nullptr;
108 }
109 // TODO: consider dumping builder and work with lower level API
110 SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(fEffect);
111 for (const auto& uniform : fUniforms) {
112 const auto& name = std::get<0>(uniform);
113 const auto& data = std::get<1>(uniform);
114 auto metadata = fEffect->findUniform(name.c_str());
115 // TODO: build SkData from SkRuntimeEffect::Uniform data
116 switch (metadata->type) {
117 case SkRuntimeEffect::Uniform::Type::kFloat:
118 builder.uniform(name.c_str()) = data->at(0); break;
119 default:
120 printf("!!! %s\n", "uniform data type not supported");
121 }
122 }
123 return builder.makeShader(&SkMatrix::I(), false);
124 }
125 sk_sp<SkRuntimeEffect> fEffect;
126 std::vector<std::tuple<SkString, std::unique_ptr<VectorValue>>> fUniforms;
127 using INHERITED = DiscardableAdapterBase<SkSLEffectAdapter, SkSLShaderNode>;
128 };
129
130 } // namespace
131
132 #endif // SK_ENABLE_SKSL
133
attachSkSLEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const134 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLEffect(const skjson::ArrayValue& jprops,
135 sk_sp<sksg::RenderNode> layer) const {
136 #ifdef SK_ENABLE_SKSL
137 auto shaderNode = sk_make_sp<SkSLShaderNode>(std::move(layer));
138 return fBuilder->attachDiscardableAdapter<SkSLEffectAdapter>(jprops, *fBuilder, shaderNode);
139 #else
140 return layer;
141 #endif
142 }
143
144 } // namespace skottie::internal
145