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