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
14namespace skottie::internal {
15
16Animator::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
32void 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
46void AnimatablePropertyContainer::shrink_to_fit() {
47 fAnimators.shrink_to_fit();
48}
49
50bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
51 const skjson::ObjectValue* jprop,
52 KeyframeAnimatorBuilder& builder,
53 void* target_value) {
54 if (!jprop) {
55 return false;
56 }
57
58 const auto& jpropA = (*jprop)["a"];
59 const auto& jpropK = (*jprop)["k"];
60
61 if (!(*jprop)["x"].is<skjson::NullValue>()) {
62 abuilder.log(Logger::Level::kWarning, nullptr, "Unsupported expression.");
63 }
64
65 // Older Json versions don't have an "a" animation marker.
66 // For those, we attempt to parse both ways.
67 if (!ParseDefault<bool>(jpropA, false)) {
68 if (builder.parseValue(abuilder, jpropK, target_value)) {
69 // Static property.
70 return true;
71 }
72
73 if (!jpropA.is<skjson::NullValue>()) {
74 abuilder.log(Logger::Level::kError, jprop,
75 "Could not parse (explicit) static property.");
76 return false;
77 }
78 }
79
80 // Keyframed property.
81 sk_sp<KeyframeAnimator> animator;
82 const skjson::ArrayValue* jkfs = jpropK;
83 if (jkfs && jkfs->size() > 0) {
84 animator = builder.make(abuilder, *jkfs, target_value);
85 }
86
87 if (!animator) {
88 abuilder.log(Logger::Level::kError, jprop, "Could not parse keyframed property.");
89 return false;
90 }
91
92 if (animator->isConstant()) {
93 // If all keyframes are constant, there is no reason to treat this
94 // as an animated property - apply immediately and discard the animator.
95 animator->seek(0);
96 } else {
97 fAnimators.push_back(std::move(animator));
98 }
99
100 return true;
101}
102
103} // namespace skottie::internal
104