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