• 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/SkImage.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPicture.h"
15 #include "include/core/SkPictureRecorder.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkShader.h"
20 #include "include/core/SkSize.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkTileMode.h"
23 #include "include/effects/SkRuntimeEffect.h"
24 #include "include/private/base/SkAssert.h"
25 #include "include/private/base/SkDebug.h"
26 #include "include/private/base/SkSpan_impl.h"
27 #include "modules/jsonreader/SkJSONReader.h"
28 #include "modules/skottie/include/Skottie.h"
29 #include "modules/skottie/src/Adapter.h"
30 #include "modules/skottie/src/SkottieJson.h"
31 #include "modules/skottie/src/SkottiePriv.h"
32 #include "modules/skottie/src/SkottieValue.h"
33 #include "modules/skottie/src/animator/Animator.h"
34 #include "modules/skottie/src/effects/Effects.h"
35 #include "modules/skresources/include/SkResources.h"
36 #include "modules/sksg/include/SkSGColorFilter.h"
37 #include "modules/sksg/include/SkSGNode.h"
38 #include "modules/sksg/include/SkSGRenderNode.h"
39 
40 #include <cstdint>
41 #include <cstring>
42 #include <memory>
43 #include <tuple>
44 #include <utility>
45 #include <vector>
46 
47 struct SkPoint;
48 
49 namespace sksg {
50 class InvalidationController;
51 }
52 
53 namespace skottie::internal {
54 
55 // https://g-issues.chromium.org/issues/40064011
56 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
57 
58 class SkSLShaderNode final : public sksg::CustomRenderNode {
59 public:
SkSLShaderNode(sk_sp<RenderNode> child,const SkSize & content_size)60     explicit SkSLShaderNode(sk_sp<RenderNode> child, const SkSize& content_size)
61         : INHERITED({std::move(child)})
62         , fContentSize(content_size) {}
63 
contentShader()64     sk_sp<SkShader> contentShader() {
65         if (!fContentShader || this->hasChildrenInval()) {
66             const auto& child = this->children()[0];
67             child->revalidate(nullptr, SkMatrix::I());
68 
69             SkPictureRecorder recorder;
70             child->render(recorder.beginRecording(SkRect::MakeSize(fContentSize)));
71 
72             fContentShader = recorder.finishRecordingAsPicture()
73                     ->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkFilterMode::kLinear,
74                                  nullptr, nullptr);
75         }
76 
77         return fContentShader;
78     }
79 
80     SG_ATTRIBUTE(Shader, sk_sp<SkShader>, fEffectShader)
81 private:
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)82     SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
83         const auto& child = this->children()[0];
84         return child->revalidate(ic, ctm);
85     }
86 
onRender(SkCanvas * canvas,const RenderContext * ctx) const87     void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
88         const auto& bounds = this->bounds();
89         const auto local_ctx = ScopedRenderContext(canvas, ctx)
90                 .setIsolation(bounds, canvas->getTotalMatrix(), true);
91 
92         canvas->saveLayer(&bounds, nullptr);
93         this->children()[0]->render(canvas, local_ctx);
94 
95         SkPaint effect_paint;
96         effect_paint.setShader(fEffectShader);
97         effect_paint.setBlendMode(SkBlendMode::kSrcIn);
98 
99         canvas->drawPaint(effect_paint);
100     }
101 
onNodeAt(const SkPoint &) const102     const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
103 
104     sk_sp<SkShader> fEffectShader;
105     sk_sp<SkShader> fContentShader;
106     const SkSize fContentSize;
107 
108     using INHERITED = sksg::CustomRenderNode;
109 };
110 
111 class SkSLEffectBase {
112 public:
SkSLEffectBase(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder)113     SkSLEffectBase(const skjson::ArrayValue& jprops,
114                    const AnimationBuilder& abuilder)
115     {
116         if (jprops.size() < 1) {
117             return;
118         }
119         const skjson::ObjectValue* jSkSL = jprops[kSkSL_index];
120         if (!jSkSL) {
121             return;
122         }
123         const skjson::StringValue* jShader = (*jSkSL)["sh"];
124         if (!jShader) {
125             return;
126         }
127         SkString shader = SkString(jShader->begin(), jShader->size());
128         auto result = SkRuntimeEffect::MakeForShader(shader, {});
129         if (!result.effect) {
130             abuilder.log(Logger::Level::kError, nullptr, "Failed to parse SkSL shader: %s",
131                result.errorText.c_str());
132             return;
133         }
134         fEffect = std::move(result.effect);
135     }
136 protected:
137     enum : size_t {
138         kSkSL_index = 0,
139         kFirstUniform_index = 1,
140     };
141 
142     enum : size_t {
143         kSkSLProp_uniform = 0,   // Maps to the integer value 1
144         kSkSLProp_image = 98, // Maps to the integer value 98
145         kSkSLProp_layer = 99  // Maps to the integer value 99
146     };
147 
148     struct ChildData {
149         int type;
150         SkString name;
151         SkRuntimeEffect::ChildPtr child;
152     };
153 
bindUniforms(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,AnimatablePropertyContainer * const & container)154     void bindUniforms(const skjson::ArrayValue& jprops,
155                       const AnimationBuilder& abuilder,
156                       AnimatablePropertyContainer * const &container) {
157         // construct dynamic uniform list from jprops, skip SkSL property
158         for (size_t i = kFirstUniform_index; i < jprops.size(); i++) {
159             const skjson::ObjectValue* jprop = jprops[i];
160             if (!jprop) { continue; }
161             const skjson::StringValue* uniformName = (*jprop)["nm"];
162             if (!uniformName) { continue; }
163             int type = ParseDefault<int>((*jprop)["ty"], kSkSLProp_uniform);
164             if (type == kSkSLProp_uniform) {
165                 auto uniformTuple = std::make_tuple(SkString(uniformName->begin(),
166                                                             uniformName->size()),
167                                                     std::make_unique<VectorValue>());
168                 fUniforms.push_back(std::move(uniformTuple));
169                 container->bind(abuilder, (*jprop)["v"], std::get<1>(fUniforms.back()).get());
170             } else if (type == kSkSLProp_image) {
171                 const skjson::ObjectValue* jimageRef = (*jprop)["v"];
172                 if (!jimageRef) {
173                     continue;
174                 }
175 
176                 const AnimationBuilder::ScopedAssetRef footageAsset(&abuilder, *jimageRef);
177                 const auto* asset_info = abuilder.loadFootageAsset(*footageAsset);
178                 if (asset_info && asset_info->fAsset) {
179                     // TODO: instead of resolving shaders here, save a collection of footage assets
180                     // onSync, grab the correct frameData and create a shader then
181                     auto frameData = asset_info->fAsset->getFrameData(0);
182                     SkSamplingOptions sampling(SkFilterMode::kLinear);
183                     fChildren.push_back({type, SkString(uniformName->begin(), uniformName->size()),
184                                             frameData.image->makeShader(sampling)});
185                 } else {
186                     SkDebugf("cannot find asset for custom shader effect");
187                 }
188             } else if (type == kSkSLProp_layer) { /* layer content */
189                 fChildren.push_back({type, SkString(uniformName->begin(), uniformName->size()),
190                     SkRuntimeEffect::ChildPtr()});
191             }
192         }
193     }
194 
buildUniformData() const195     sk_sp<SkData> buildUniformData() const {
196         auto uniformData = SkData::MakeZeroInitialized(fEffect->uniformSize());
197         SkASSERT(uniformData);
198         for (const auto& uniform : fUniforms) {
199             const auto& name = std::get<0>(uniform);
200             const auto& data = std::get<1>(uniform);
201             auto metadata = fEffect->findUniform(name.c_str());
202             if (metadata && metadata->count == static_cast<int>(data->size())) {
203                 auto dst = reinterpret_cast<uint8_t*>(uniformData->writable_data())
204                     + metadata->offset;
205                 memcpy(reinterpret_cast<void*>(dst), data->data(), data->size() * sizeof(float));
206             } else {
207                 SkDebugf("cannot set malformed uniform: %s\n", name.c_str());
208             }
209         }
210         return uniformData;
211     }
212 
buildChildrenData(sk_sp<SkSLShaderNode> node) const213     std::vector<SkRuntimeEffect::ChildPtr> buildChildrenData(sk_sp<SkSLShaderNode> node) const {
214         std::vector<SkRuntimeEffect::ChildPtr> childrenData(fEffect->children().size());
215         for (const auto& childData : fChildren) {
216             auto metadata = fEffect->findChild(childData.name.c_str());
217             if (childData.type == kSkSLProp_layer) {
218                 childrenData[metadata->index] = (node->contentShader());
219             } else if (childData.type == kSkSLProp_image) {
220                 childrenData[metadata->index] = childData.child;
221             }
222         }
223         return childrenData;
224     }
225     sk_sp<SkRuntimeEffect> fEffect;
226     std::vector<std::tuple<SkString, std::unique_ptr<VectorValue>>> fUniforms;
227     std::vector<ChildData> fChildren;
228 };
229 
230 class SkSLShaderAdapter final : public DiscardableAdapterBase<SkSLShaderAdapter,
231                                                               SkSLShaderNode>,
232                                 public SkSLEffectBase {
233 public:
SkSLShaderAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<SkSLShaderNode> node)234     SkSLShaderAdapter(const skjson::ArrayValue& jprops,
235                       const AnimationBuilder& abuilder,
236                       sk_sp<SkSLShaderNode> node)
237         : DiscardableAdapterBase<SkSLShaderAdapter, SkSLShaderNode>(std::move(node))
238         , SkSLEffectBase(jprops, abuilder)
239     {
240         this->bindUniforms(jprops, abuilder, this);
241     }
242 
243 private:
onSync()244     void onSync() override {
245         if (!fEffect) {
246             return;
247         }
248         sk_sp<SkShader> shader =
249                 fEffect->makeShader(buildUniformData(), SkSpan(buildChildrenData(this->node())));
250         this->node()->setShader(std::move(shader));
251     }
252 };
253 
254 class SkSLColorFilterAdapter final : public DiscardableAdapterBase<SkSLColorFilterAdapter,
255                                                              sksg::ExternalColorFilter>
256                                    , public SkSLEffectBase {
257 public:
SkSLColorFilterAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<sksg::ExternalColorFilter> node)258     SkSLColorFilterAdapter(const skjson::ArrayValue& jprops,
259                       const AnimationBuilder& abuilder,
260                       sk_sp<sksg::ExternalColorFilter> node)
261         : DiscardableAdapterBase<SkSLColorFilterAdapter, sksg::ExternalColorFilter>(std::move(node))
262         , SkSLEffectBase(jprops, abuilder)
263     {
264         this->bindUniforms(jprops, abuilder, this);
265     }
266 
267 private:
onSync()268     void onSync() override {
269         if (!fEffect) {
270             return;
271         }
272         auto cf = fEffect->makeColorFilter(buildUniformData());
273         this->node()->setColorFilter(std::move(cf));
274     }
275 };
276 
277 #endif  // SK_ENABLE_SKOTTIE_SKSLEFFECT
278 
attachSkSLShader(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const279 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLShader(const skjson::ArrayValue& jprops,
280                                                         sk_sp<sksg::RenderNode> layer) const {
281 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
282     auto shaderNode = sk_make_sp<SkSLShaderNode>(std::move(layer), fLayerSize);
283     return fBuilder->attachDiscardableAdapter<SkSLShaderAdapter>(jprops, *fBuilder,
284                                                                  std::move(shaderNode));
285 #else
286     return layer;
287 #endif
288 }
289 
attachSkSLColorFilter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const290 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLColorFilter(const skjson::ArrayValue& jprops,
291                                                              sk_sp<sksg::RenderNode> layer) const {
292 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
293     auto cfNode = sksg::ExternalColorFilter::Make(std::move(layer));
294     return fBuilder->attachDiscardableAdapter<SkSLColorFilterAdapter>(jprops, *fBuilder,
295                                                                       std::move(cfNode));
296 #else
297     return layer;
298 #endif
299 }
300 
301 } // namespace skottie::internal
302