1 /*
2 * Copyright 2020 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/animator/Animator.h"
9
10 #include "modules/skottie/src/SkottieJson.h"
11 #include "modules/skottie/src/SkottiePriv.h"
12 #include "modules/skottie/src/animator/KeyframeAnimator.h"
13
14 namespace skottie::internal {
15
onSeek(float t)16 Animator::StateChanged AnimatablePropertyContainer::onSeek(float t) {
17 // The very first seek must trigger a sync, to ensure proper SG setup.
18 bool changed = !fHasSynced;
19
20 for (const auto& animator : fAnimators) {
21 changed |= animator->seek(t);
22 }
23
24 if (changed) {
25 this->onSync();
26 fHasSynced = true;
27 }
28
29 return changed;
30 }
31
attachDiscardableAdapter(sk_sp<AnimatablePropertyContainer> child)32 void AnimatablePropertyContainer::attachDiscardableAdapter(
33 sk_sp<AnimatablePropertyContainer> child) {
34 if (!child) {
35 return;
36 }
37
38 if (child->isStatic()) {
39 child->seek(0);
40 return;
41 }
42
43 fAnimators.push_back(child);
44 }
45
shrink_to_fit()46 void AnimatablePropertyContainer::shrink_to_fit() {
47 fAnimators.shrink_to_fit();
48 }
49
bindImpl(const AnimationBuilder & abuilder,const skjson::ObjectValue * jprop,AnimatorBuilder & builder)50 bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
51 const skjson::ObjectValue* jprop,
52 AnimatorBuilder& builder) {
53 if (!jprop) {
54 return false;
55 }
56
57 const auto& jpropA = (*jprop)["a"];
58 const auto& jpropK = (*jprop)["k"];
59
60 // Handle expressions on the property.
61 if (const skjson::StringValue* expr = (*jprop)["x"]) {
62 if (!abuilder.expression_manager()) {
63 abuilder.log(Logger::Level::kWarning, jprop,
64 "Expression encountered but ExpressionManager not provided.");
65 } else {
66 builder.parseValue(abuilder, jpropK);
67 sk_sp<Animator> expression_animator = builder.makeFromExpression(
68 *abuilder.expression_manager(),
69 expr->begin());
70 if (expression_animator) {
71 fAnimators.push_back(std::move(expression_animator));
72 return true;
73 }
74 }
75 }
76
77 // Older Json versions don't have an "a" animation marker.
78 // For those, we attempt to parse both ways.
79 if (!ParseDefault<bool>(jpropA, false)) {
80 if (builder.parseValue(abuilder, jpropK)) {
81 // Static property.
82 return true;
83 }
84
85 if (!jpropA.is<skjson::NullValue>()) {
86 abuilder.log(Logger::Level::kError, jprop,
87 "Could not parse (explicit) static property.");
88 return false;
89 }
90 }
91
92 // Keyframed property.
93 sk_sp<KeyframeAnimator> animator;
94 const skjson::ArrayValue* jkfs = jpropK;
95 if (jkfs && jkfs->size() > 0) {
96 animator = builder.makeFromKeyframes(abuilder, *jkfs);
97 }
98
99 if (!animator) {
100 abuilder.log(Logger::Level::kError, jprop, "Could not parse keyframed property.");
101 return false;
102 }
103
104 if (animator->isConstant()) {
105 // If all keyframes are constant, there is no reason to treat this
106 // as an animated property - apply immediately and discard the animator.
107 animator->seek(0);
108 } else {
109 fAnimators.push_back(std::move(animator));
110 }
111
112 return true;
113 }
114
115 } // namespace skottie::internal
116