• 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 "include/private/SkMalloc.h"
12 #include "modules/skottie/src/Adapter.h"
13 #include "modules/skottie/src/SkottieJson.h"
14 #include "modules/skottie/src/SkottieValue.h"
15 #include "modules/sksg/include/SkSGColorFilter.h"
16 
17 namespace skottie::internal {
18 
19 #ifdef SK_ENABLE_SKSL
20 
21 namespace  {
22 class SkSLShaderNode final : public sksg::CustomRenderNode {
23 public:
SkSLShaderNode(sk_sp<RenderNode> child)24     explicit SkSLShaderNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {}
25 
26     SG_ATTRIBUTE(Shader, sk_sp<SkShader>, fEffectShader)
27 private:
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)28     SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
29         const auto& child = this->children()[0];
30         return child->revalidate(ic, ctm);
31     }
32 
onRender(SkCanvas * canvas,const RenderContext * ctx) const33     void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
34         const auto& bounds = this->bounds();
35         const auto local_ctx = ScopedRenderContext(canvas, ctx)
36                 .setIsolation(bounds, canvas->getTotalMatrix(), true);
37 
38         canvas->saveLayer(&bounds, nullptr);
39         this->children()[0]->render(canvas, local_ctx);
40 
41         SkPaint effect_paint;
42         effect_paint.setShader(fEffectShader);
43         effect_paint.setBlendMode(SkBlendMode::kSrcIn);
44 
45         canvas->drawPaint(effect_paint);
46     }
47 
onNodeAt(const SkPoint &) const48     const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
49 
50     sk_sp<SkShader> fEffectShader;
51 
52     using INHERITED = sksg::CustomRenderNode;
53 };
54 
55 class SkSLEffectBase {
56 public:
SkSLEffectBase(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder)57     SkSLEffectBase(const skjson::ArrayValue& jprops,
58                    const AnimationBuilder& abuilder)
59     {
60         if (jprops.size() < 1) {
61             return;
62         }
63         const skjson::ObjectValue* jSkSL = jprops[kSkSL_index];
64         if (!jSkSL) {
65             return;
66         }
67         const skjson::StringValue* jShader = (*jSkSL)["sh"];
68         if (!jShader) {
69             return;
70         }
71         SkString shader = SkString(jShader->begin(), jShader->size());
72         auto result = SkRuntimeEffect::MakeForShader(shader, {});
73         if (!result.effect) {
74             abuilder.log(Logger::Level::kError, nullptr, "Failed to parse SkSL shader: %s",
75                result.errorText.c_str());
76             return;
77         }
78         fEffect = std::move(result.effect);
79     }
80 protected:
81     enum : size_t {
82         kSkSL_index = 0,
83         kFirstUniform_index = 1,
84     };
85 
bindUniforms(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,AnimatablePropertyContainer * const & container)86     void bindUniforms(const skjson::ArrayValue& jprops,
87                       const AnimationBuilder& abuilder,
88                       AnimatablePropertyContainer * const &container) {
89         // construct dynamic uniform list from jprops, skip SkSL property
90         for (size_t i = kFirstUniform_index; i < jprops.size(); i++) {
91             const skjson::ObjectValue* jprop = jprops[i];
92             if (!jprop) { continue; }
93             const skjson::StringValue* uniformName = (*jprop)["nm"];
94             if (!uniformName) { continue; }
95             auto uniformTuple = std::make_tuple(SkString(uniformName->begin(),
96                                                          uniformName->size()),
97                                                 std::make_unique<VectorValue>());
98             fUniforms.push_back(std::move(uniformTuple));
99             container->bind(abuilder, (*jprop)["v"], std::get<1>(fUniforms.back()).get());
100         }
101     }
102 
buildUniformData() const103     sk_sp<SkData> buildUniformData() const {
104         auto uniformData = SkData::MakeUninitialized(fEffect->uniformSize());
105         SkASSERT(uniformData);
106         sk_bzero(uniformData->writable_data(), uniformData->size());
107         for (const auto& uniform : fUniforms) {
108             const auto& name = std::get<0>(uniform);
109             const auto& data = std::get<1>(uniform);
110             auto metadata = fEffect->findUniform(name.c_str());
111             if (metadata && metadata->count == static_cast<int>(data->size())) {
112                 auto dst = reinterpret_cast<uint8_t*>(uniformData->writable_data()) + metadata->offset;
113                 memcpy(reinterpret_cast<void*>(dst), data->data(), data->size() * sizeof(float));
114             } else {
115                 SkDebugf("cannot set malformed uniform: %s", name.c_str());
116             }
117         }
118         return uniformData;
119     }
120     sk_sp<SkRuntimeEffect> fEffect;
121     std::vector<std::tuple<SkString, std::unique_ptr<VectorValue>>> fUniforms;
122 };
123 
124 class SkSLShaderAdapter final : public DiscardableAdapterBase<SkSLShaderAdapter,
125                                                               SkSLShaderNode>,
126                                 public SkSLEffectBase {
127 public:
SkSLShaderAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<SkSLShaderNode> node)128     SkSLShaderAdapter(const skjson::ArrayValue& jprops,
129                       const AnimationBuilder& abuilder,
130                       sk_sp<SkSLShaderNode> node)
131         : DiscardableAdapterBase<SkSLShaderAdapter, SkSLShaderNode>(std::move(node))
132         , SkSLEffectBase(jprops, abuilder)
133     {
134         this->bindUniforms(jprops, abuilder, this);
135     }
136 
137 private:
onSync()138     void onSync() override {
139         if (!fEffect) {
140             return;
141         }
142         sk_sp<SkShader> shader =
143                 fEffect->makeShader(buildUniformData(), {/* TODO: child support */});
144         this->node()->setShader(std::move(shader));
145     }
146 };
147 
148 class SkSLColorFilterAdapter final : public DiscardableAdapterBase<SkSLColorFilterAdapter,
149                                                              sksg::ExternalColorFilter>
150                                    , public SkSLEffectBase {
151 public:
SkSLColorFilterAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<sksg::ExternalColorFilter> node)152     SkSLColorFilterAdapter(const skjson::ArrayValue& jprops,
153                       const AnimationBuilder& abuilder,
154                       sk_sp<sksg::ExternalColorFilter> node)
155         : DiscardableAdapterBase<SkSLColorFilterAdapter, sksg::ExternalColorFilter>(std::move(node))
156         , SkSLEffectBase(jprops, abuilder)
157     {
158         this->bindUniforms(jprops, abuilder, this);
159     }
160 
161 private:
onSync()162     void onSync() override {
163         if (!fEffect) {
164             return;
165         }
166         auto cf = fEffect->makeColorFilter(buildUniformData());
167         this->node()->setColorFilter(std::move(cf));
168     }
169 };
170 
171 } // namespace
172 
173 #endif  // SK_ENABLE_SKSL
174 
attachSkSLShader(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const175 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLShader(const skjson::ArrayValue& jprops,
176                                                         sk_sp<sksg::RenderNode> layer) const {
177 #ifdef SK_ENABLE_SKSL
178     auto shaderNode = sk_make_sp<SkSLShaderNode>(std::move(layer));
179     return fBuilder->attachDiscardableAdapter<SkSLShaderAdapter>(jprops, *fBuilder,
180                                                                  std::move(shaderNode));
181 #else
182     return layer;
183 #endif
184 }
185 
attachSkSLColorFilter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const186 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLColorFilter(const skjson::ArrayValue& jprops,
187                                                              sk_sp<sksg::RenderNode> layer) const {
188 #ifdef SK_ENABLE_SKSL
189     auto cfNode = sksg::ExternalColorFilter::Make(std::move(layer));
190     return fBuilder->attachDiscardableAdapter<SkSLColorFilterAdapter>(jprops, *fBuilder,
191                                                                       std::move(cfNode));
192 #else
193     return layer;
194 #endif
195 }
196 
197 } // namespace skottie::internal
198