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
19namespace skottie {
20namespace internal {
21
22namespace {
23
24// "Animates" time based on the layer's "tm" property.
25class TimeRemapper final : public AnimatablePropertyContainer {
26public:
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
34private:
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.
45class CompTimeMapper final : public Animator {
46public:
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
73private:
74 const AnimatorScope fAnimators;
75 const sk_sp<TimeRemapper> fRemapper;
76 const float fTimeBias,
77 fTimeScale;
78};
79
80} // namespace
81
82sk_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