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