1 | /* |
2 | * Copyright 2018 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 "modules/skottie/include/ExternalLayer.h" |
11 | #include "modules/skottie/src/Composition.h" |
12 | #include "modules/skottie/src/SkottieJson.h" |
13 | #include "modules/skottie/src/SkottieValue.h" |
14 | #include "modules/skottie/src/animator/Animator.h" |
15 | #include "modules/sksg/include/SkSGRenderNode.h" |
16 | #include "modules/sksg/include/SkSGScene.h" |
17 | #include "src/core/SkTLazy.h" |
18 | #include "src/utils/SkJSON.h" |
19 | |
20 | namespace skottie { |
21 | namespace internal { |
22 | |
23 | namespace { |
24 | |
25 | // "Animates" time based on the layer's "tm" property. |
26 | class TimeRemapper final : public AnimatablePropertyContainer { |
27 | public: |
28 | TimeRemapper(const skjson::ObjectValue& jtm, const AnimationBuilder* abuilder, float scale) |
29 | : fScale(scale) { |
30 | this->bind(*abuilder, jtm, fT); |
31 | } |
32 | |
33 | float t() const { return fT * fScale; } |
34 | |
35 | private: |
36 | void onSync() override { |
37 | // nothing to sync - we just track t |
38 | } |
39 | |
40 | const float fScale; |
41 | |
42 | ScalarValue fT = 0; |
43 | }; |
44 | |
45 | // Applies a bias/scale/remap t-adjustment to child animators. |
46 | class CompTimeMapper final : public Animator { |
47 | public: |
48 | CompTimeMapper(AnimatorScope&& layer_animators, |
49 | sk_sp<TimeRemapper> remapper, |
50 | float time_bias, float time_scale) |
51 | : fAnimators(std::move(layer_animators)) |
52 | , fRemapper(std::move(remapper)) |
53 | , fTimeBias(time_bias) |
54 | , fTimeScale(time_scale) {} |
55 | |
56 | StateChanged onSeek(float t) override { |
57 | if (fRemapper) { |
58 | // When time remapping is active, |t| is fully driven externally. |
59 | fRemapper->seek(t); |
60 | t = fRemapper->t(); |
61 | } else { |
62 | t = (t + fTimeBias) * fTimeScale; |
63 | } |
64 | |
65 | bool changed = false; |
66 | |
67 | for (const auto& anim : fAnimators) { |
68 | changed |= anim->seek(t); |
69 | } |
70 | |
71 | return changed; |
72 | } |
73 | |
74 | private: |
75 | const AnimatorScope fAnimators; |
76 | const sk_sp<TimeRemapper> fRemapper; |
77 | const float fTimeBias, |
78 | fTimeScale; |
79 | }; |
80 | |
81 | } // namespace |
82 | |
83 | sk_sp<sksg::RenderNode> AnimationBuilder::attachExternalPrecompLayer( |
84 | const skjson::ObjectValue& jlayer, |
85 | const LayerInfo& layer_info) const { |
86 | |
87 | if (!fPrecompInterceptor) { |
88 | return nullptr; |
89 | } |
90 | |
91 | const skjson::StringValue* id = jlayer["refId" ]; |
92 | const skjson::StringValue* nm = jlayer["nm" ]; |
93 | |
94 | if (!id || !nm) { |
95 | return nullptr; |
96 | } |
97 | |
98 | auto external_layer = fPrecompInterceptor->onLoadPrecomp(id->begin(), |
99 | nm->begin(), |
100 | layer_info.fSize); |
101 | if (!external_layer) { |
102 | return nullptr; |
103 | } |
104 | |
105 | // Attaches an ExternalLayer implementation to the animation scene graph. |
106 | class SGAdapter final : public sksg::RenderNode { |
107 | public: |
108 | SG_ATTRIBUTE(T, float, fCurrentT) |
109 | |
110 | SGAdapter(sk_sp<ExternalLayer> external, const SkSize& layer_size) |
111 | : fExternal(std::move(external)) |
112 | , fSize(layer_size) {} |
113 | |
114 | private: |
115 | SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override { |
116 | return SkRect::MakeSize(fSize); |
117 | } |
118 | |
119 | void onRender(SkCanvas* canvas, const RenderContext* ctx) const override { |
120 | // Commit all pending effects via a layer if needed, |
121 | // since we don't have knowledge of the external content. |
122 | const auto local_scope = |
123 | ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(), |
124 | canvas->getTotalMatrix(), |
125 | true); |
126 | fExternal->render(canvas, static_cast<double>(fCurrentT)); |
127 | } |
128 | |
129 | const RenderNode* onNodeAt(const SkPoint& pt) const override { |
130 | SkASSERT(this->bounds().contains(pt.fX, pt.fY)); |
131 | return this; |
132 | } |
133 | |
134 | const sk_sp<ExternalLayer> fExternal; |
135 | const SkSize fSize; |
136 | float fCurrentT = 0; |
137 | }; |
138 | |
139 | // Connects an SGAdapter to the animator tree and dispatches seek events. |
140 | class AnimatorAdapter final : public Animator { |
141 | public: |
142 | AnimatorAdapter(sk_sp<SGAdapter> sg_adapter, float fps) |
143 | : fSGAdapter(std::move(sg_adapter)) |
144 | , fFps(fps) {} |
145 | |
146 | private: |
147 | StateChanged onSeek(float t) override { |
148 | fSGAdapter->setT(t / fFps); |
149 | |
150 | return true; |
151 | } |
152 | |
153 | const sk_sp<SGAdapter> fSGAdapter; |
154 | const float fFps; |
155 | }; |
156 | |
157 | auto sg_adapter = sk_make_sp<SGAdapter>(std::move(external_layer), layer_info.fSize); |
158 | |
159 | fCurrentAnimatorScope->push_back(sk_make_sp<AnimatorAdapter>(sg_adapter, fFrameRate)); |
160 | |
161 | return std::move(sg_adapter); |
162 | } |
163 | |
164 | sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer, |
165 | LayerInfo* layer_info) const { |
166 | sk_sp<TimeRemapper> time_remapper; |
167 | if (const skjson::ObjectValue* jtm = jlayer["tm" ]) { |
168 | time_remapper = sk_make_sp<TimeRemapper>(*jtm, this, fFrameRate); |
169 | } |
170 | |
171 | const auto start_time = ParseDefault<float>(jlayer["st" ], 0.0f), |
172 | stretch_time = ParseDefault<float>(jlayer["sr" ], 1.0f); |
173 | const auto requires_time_mapping = !SkScalarNearlyEqual(start_time , 0) || |
174 | !SkScalarNearlyEqual(stretch_time, 1) || |
175 | time_remapper; |
176 | |
177 | // Precomp layers are sized explicitly. |
178 | layer_info->fSize = SkSize::Make(ParseDefault<float>(jlayer["w" ], 0.0f), |
179 | ParseDefault<float>(jlayer["h" ], 0.0f)); |
180 | |
181 | SkTLazy<AutoScope> local_scope; |
182 | if (requires_time_mapping) { |
183 | local_scope.init(this); |
184 | } |
185 | |
186 | auto precomp_layer = this->attachExternalPrecompLayer(jlayer, *layer_info); |
187 | |
188 | if (!precomp_layer) { |
189 | const ScopedAssetRef precomp_asset(this, jlayer); |
190 | if (precomp_asset) { |
191 | precomp_layer = |
192 | CompositionBuilder(*this, layer_info->fSize, *precomp_asset).build(*this); |
193 | } |
194 | } |
195 | |
196 | if (requires_time_mapping) { |
197 | const auto t_bias = -start_time, |
198 | t_scale = sk_ieee_float_divide(1, stretch_time); |
199 | auto time_mapper = sk_make_sp<CompTimeMapper>(local_scope->release(), |
200 | std::move(time_remapper), |
201 | t_bias, |
202 | sk_float_isfinite(t_scale) ? t_scale : 0); |
203 | |
204 | fCurrentAnimatorScope->push_back(std::move(time_mapper)); |
205 | } |
206 | |
207 | return precomp_layer; |
208 | } |
209 | |
210 | } // namespace internal |
211 | } // namespace skottie |
212 | |