• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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/SkottiePriv.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "modules/skottie/src/SkottieJson.h"
12 #include "modules/sksg/include/SkSGGroup.h"
13 #include "modules/sksg/include/SkSGTransform.h"
14 
15 #include <algorithm>
16 
17 namespace skottie {
18 namespace internal {
19 
attachNestedAnimation(const char * name) const20 sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name) const {
21     class SkottieSGAdapter final : public sksg::RenderNode {
22     public:
23         explicit SkottieSGAdapter(sk_sp<Animation> animation)
24             : fAnimation(std::move(animation)) {
25             SkASSERT(fAnimation);
26         }
27 
28     protected:
29         SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
30             return SkRect::MakeSize(fAnimation->size());
31         }
32 
33         const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
34 
35         void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
36             const auto local_scope =
37                 ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
38                                                               canvas->getTotalMatrix(),
39                                                               true);
40             fAnimation->render(canvas);
41         }
42 
43     private:
44         const sk_sp<Animation> fAnimation;
45     };
46 
47     class SkottieAnimatorAdapter final : public sksg::Animator {
48     public:
49         SkottieAnimatorAdapter(sk_sp<Animation> animation, float time_scale)
50             : fAnimation(std::move(animation))
51             , fTimeScale(time_scale) {
52             SkASSERT(fAnimation);
53         }
54 
55     protected:
56         void onTick(float t) {
57             // TODO: we prolly need more sophisticated timeline mapping for nested animations.
58             fAnimation->seek(t * fTimeScale);
59         }
60 
61     private:
62         const sk_sp<Animation> fAnimation;
63         const float            fTimeScale;
64     };
65 
66     const auto data = fResourceProvider->load("", name);
67     if (!data) {
68         this->log(Logger::Level::kError, nullptr, "Could not load: %s.", name);
69         return nullptr;
70     }
71 
72     auto animation = Animation::Builder()
73             .setResourceProvider(fResourceProvider)
74             .setFontManager(fLazyFontMgr.getMaybeNull())
75             .make(static_cast<const char*>(data->data()), data->size());
76     if (!animation) {
77         this->log(Logger::Level::kError, nullptr, "Could not parse nested animation: %s.", name);
78         return nullptr;
79     }
80 
81     fCurrentAnimatorScope->push_back(
82             sk_make_sp<SkottieAnimatorAdapter>(animation, animation->duration() / fDuration));
83 
84     return sk_make_sp<SkottieSGAdapter>(std::move(animation));
85 }
86 
attachAssetRef(const skjson::ObjectValue & jlayer,const std::function<sk_sp<sksg::RenderNode> (const skjson::ObjectValue &)> & func) const87 sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
88     const skjson::ObjectValue& jlayer,
89     const std::function<sk_sp<sksg::RenderNode>(const skjson::ObjectValue&)>& func) const {
90 
91     const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
92     if (refId.isEmpty()) {
93         this->log(Logger::Level::kError, nullptr, "Layer missing refId.");
94         return nullptr;
95     }
96 
97     if (refId.startsWith("$")) {
98         return this->attachNestedAnimation(refId.c_str() + 1);
99     }
100 
101     const auto* asset_info = fAssets.find(refId);
102     if (!asset_info) {
103         this->log(Logger::Level::kError, nullptr, "Asset not found: '%s'.", refId.c_str());
104         return nullptr;
105     }
106 
107     if (asset_info->fIsAttaching) {
108         this->log(Logger::Level::kError, nullptr,
109                   "Asset cycle detected for: '%s'", refId.c_str());
110         return nullptr;
111     }
112 
113     asset_info->fIsAttaching = true;
114     auto asset = func(*asset_info->fAsset);
115     asset_info->fIsAttaching = false;
116 
117     return asset;
118 }
119 
attachComposition(const skjson::ObjectValue & jcomp) const120 sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(
121         const skjson::ObjectValue& jcomp) const {
122     const skjson::ArrayValue* jlayers = jcomp["layers"];
123     if (!jlayers) return nullptr;
124 
125     std::vector<sk_sp<sksg::RenderNode>> layers;
126     AttachLayerContext                   layerCtx(*jlayers);
127 
128     // Optional motion blur params.
129     if (const skjson::ObjectValue* jmb = jcomp["mb"]) {
130         static constexpr size_t kMaxSamplesPerFrame = 64;
131         layerCtx.fMotionBlurSamples = std::min(ParseDefault<size_t>((*jmb)["spf"], 1ul),
132                                                kMaxSamplesPerFrame);
133         layerCtx.fMotionBlurAngle = SkTPin(ParseDefault((*jmb)["sa"], 0.0f),    0.0f, 720.0f);
134         layerCtx.fMotionBlurPhase = SkTPin(ParseDefault((*jmb)["sp"], 0.0f), -360.0f, 360.0f);
135     }
136 
137     layers.reserve(jlayers->size());
138     for (const auto& l : *jlayers) {
139         if (auto layer = this->attachLayer(l, &layerCtx)) {
140             layers.push_back(std::move(layer));
141         }
142     }
143 
144     if (layers.empty()) {
145         return nullptr;
146     }
147 
148     sk_sp<sksg::RenderNode> comp;
149     if (layers.size() == 1) {
150         comp = std::move(layers[0]);
151     } else {
152         // Layers are painted in bottom->top order.
153         std::reverse(layers.begin(), layers.end());
154         layers.shrink_to_fit();
155         comp = sksg::Group::Make(std::move(layers));
156     }
157 
158     // Optional camera.
159     if (layerCtx.fCameraTransform) {
160         comp = sksg::TransformEffect::Make(std::move(comp), std::move(layerCtx.fCameraTransform));
161     }
162 
163     return comp;
164 }
165 
166 } // namespace internal
167 } // namespace skottie
168