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 "SkottiePriv.h"
9
10 #include "SkJSON.h"
11 #include "SkottieJson.h"
12 #include "SkottieValue.h"
13 #include "SkMakeUnique.h"
14 #include "SkSGRenderNode.h"
15 #include "SkSGScene.h"
16 #include "SkTLazy.h"
17
18 namespace skottie {
19 namespace internal {
20
attachPrecompLayer(const skjson::ObjectValue & jlayer,const LayerInfo &,AnimatorScope * ascope) const21 sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer,
22 const LayerInfo&,
23 AnimatorScope* ascope) const {
24 const skjson::ObjectValue* time_remap = jlayer["tm"];
25 // Empirically, a time mapper supersedes start/stretch.
26 const auto start_time = time_remap ? 0.0f : ParseDefault<float>(jlayer["st"], 0.0f),
27 stretch_time = time_remap ? 1.0f : ParseDefault<float>(jlayer["sr"], 1.0f);
28 const auto requires_time_mapping = !SkScalarNearlyEqual(start_time , 0) ||
29 !SkScalarNearlyEqual(stretch_time, 1) ||
30 time_remap;
31
32 AnimatorScope local_animators;
33 auto precomp_layer = this->attachAssetRef(jlayer,
34 requires_time_mapping ? &local_animators : ascope,
35 [this] (const skjson::ObjectValue& jcomp,
36 AnimatorScope* ascope) {
37 return this->attachComposition(jcomp, ascope);
38 });
39
40 // Applies a bias/scale/remap t-adjustment to child animators.
41 class CompTimeMapper final : public sksg::GroupAnimator {
42 public:
43 CompTimeMapper(sksg::AnimatorList&& layer_animators, float time_bias, float time_scale)
44 : INHERITED(std::move(layer_animators))
45 , fTimeBias(time_bias)
46 , fTimeScale(time_scale) {}
47
48 void onTick(float t) override {
49 // When time remapping is active, |t| is driven externally.
50 if (fRemappedTime.isValid()) {
51 t = *fRemappedTime.get();
52 }
53
54 this->INHERITED::onTick((t + fTimeBias) * fTimeScale);
55 }
56
57 void remapTime(float t) { fRemappedTime.set(t); }
58
59 private:
60 const float fTimeBias,
61 fTimeScale;
62 SkTLazy<float> fRemappedTime;
63
64 using INHERITED = sksg::GroupAnimator;
65 };
66
67 if (requires_time_mapping) {
68 const auto t_bias = -start_time,
69 t_scale = sk_ieee_float_divide(1, stretch_time);
70 auto time_mapper =
71 skstd::make_unique<CompTimeMapper>(std::move(local_animators), t_bias,
72 sk_float_isfinite(t_scale) ? t_scale : 0);
73 if (time_remap) {
74 // The lambda below captures a raw pointer to the mapper object. That should be safe,
75 // because both the lambda and the mapper are scoped/owned by ctx->fAnimators.
76 auto* raw_mapper = time_mapper.get();
77 auto frame_rate = fFrameRate;
78 this->bindProperty<ScalarValue>(*time_remap, ascope,
79 [raw_mapper, frame_rate](const ScalarValue& t) {
80 raw_mapper->remapTime(t * frame_rate);
81 });
82 }
83 ascope->push_back(std::move(time_mapper));
84 }
85
86 return precomp_layer;
87 }
88
89 } // namespace internal
90 } // namespace skottie
91