• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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