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/src/Composition.h" |
11 | #include "modules/skottie/src/SkottieJson.h" |
12 | #include "modules/skottie/src/SkottieValue.h" |
13 | #include "modules/skottie/src/animator/Animator.h" |
14 | #include "modules/sksg/include/SkSGRenderNode.h" |
15 | #include "modules/sksg/include/SkSGScene.h" |
16 | #include "src/core/SkTLazy.h" |
17 | #include "src/utils/SkJSON.h" |
18 | |
19 | namespace skottie { |
20 | namespace internal { |
21 | |
22 | namespace { |
23 | |
24 | // "Animates" time based on the layer's "tm" property. |
25 | class TimeRemapper final : public AnimatablePropertyContainer { |
26 | public: |
27 | TimeRemapper(const skjson::ObjectValue& jtm, const AnimationBuilder* abuilder, float scale) |
28 | : fScale(scale) { |
29 | this->bind(*abuilder, jtm, fT); |
30 | } |
31 | |
32 | float t() const { return fT * fScale; } |
33 | |
34 | private: |
35 | void onSync() override { |
36 | // nothing to sync - we just track t |
37 | } |
38 | |
39 | const float fScale; |
40 | |
41 | ScalarValue fT = 0; |
42 | }; |
43 | |
44 | // Applies a bias/scale/remap t-adjustment to child animators. |
45 | class CompTimeMapper final : public Animator { |
46 | public: |
47 | CompTimeMapper(AnimatorScope&& layer_animators, |
48 | sk_sp<TimeRemapper> remapper, |
49 | float time_bias, float time_scale) |
50 | : fAnimators(std::move(layer_animators)) |
51 | , fRemapper(std::move(remapper)) |
52 | , fTimeBias(time_bias) |
53 | , fTimeScale(time_scale) {} |
54 | |
55 | StateChanged onSeek(float t) override { |
56 | if (fRemapper) { |
57 | // When time remapping is active, |t| is fully driven externally. |
58 | fRemapper->seek(t); |
59 | t = fRemapper->t(); |
60 | } else { |
61 | t = (t + fTimeBias) * fTimeScale; |
62 | } |
63 | |
64 | bool changed = false; |
65 | |
66 | for (const auto& anim : fAnimators) { |
67 | changed |= anim->seek(t); |
68 | } |
69 | |
70 | return changed; |
71 | } |
72 | |
73 | private: |
74 | const AnimatorScope fAnimators; |
75 | const sk_sp<TimeRemapper> fRemapper; |
76 | const float fTimeBias, |
77 | fTimeScale; |
78 | }; |
79 | |
80 | } // namespace |
81 | |
82 | sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer, |
83 | LayerInfo* layer_info) const { |
84 | sk_sp<TimeRemapper> time_remapper; |
85 | if (const skjson::ObjectValue* jtm = jlayer["tm" ]) { |
86 | time_remapper = sk_make_sp<TimeRemapper>(*jtm, this, fFrameRate); |
87 | } |
88 | |
89 | const auto start_time = ParseDefault<float>(jlayer["st" ], 0.0f), |
90 | stretch_time = ParseDefault<float>(jlayer["sr" ], 1.0f); |
91 | const auto requires_time_mapping = !SkScalarNearlyEqual(start_time , 0) || |
92 | !SkScalarNearlyEqual(stretch_time, 1) || |
93 | time_remapper; |
94 | |
95 | // Precomp layers are sized explicitly. |
96 | layer_info->fSize = SkSize::Make(ParseDefault<float>(jlayer["w" ], 0.0f), |
97 | ParseDefault<float>(jlayer["h" ], 0.0f)); |
98 | |
99 | SkTLazy<AutoScope> local_scope; |
100 | if (requires_time_mapping) { |
101 | local_scope.init(this); |
102 | } |
103 | |
104 | auto precomp_layer = this->attachAssetRef(jlayer, |
105 | [this, layer_info] (const skjson::ObjectValue& jcomp) { |
106 | return CompositionBuilder(*this, layer_info->fSize, jcomp).build(*this); |
107 | }); |
108 | |
109 | if (requires_time_mapping) { |
110 | const auto t_bias = -start_time, |
111 | t_scale = sk_ieee_float_divide(1, stretch_time); |
112 | auto time_mapper = sk_make_sp<CompTimeMapper>(local_scope->release(), |
113 | std::move(time_remapper), |
114 | t_bias, |
115 | sk_float_isfinite(t_scale) ? t_scale : 0); |
116 | |
117 | fCurrentAnimatorScope->push_back(std::move(time_mapper)); |
118 | } |
119 | |
120 | return precomp_layer; |
121 | } |
122 | |
123 | } // namespace internal |
124 | } // namespace skottie |
125 | |