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/SkottieJson.h"
9#include "modules/skottie/src/SkottieValue.h"
10#include "modules/skottie/src/animator/KeyframeAnimator.h"
11#include "modules/skottie/src/text/TextValue.h"
12
13namespace skottie::internal {
14
15namespace  {
16class TextKeyframeAnimator final : public KeyframeAnimator {
17public:
18    TextKeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms,
19                         std::vector<TextValue> vs, TextValue* target_value)
20        : INHERITED(std::move(kfs), std::move(cms))
21        , fValues(std::move(vs))
22        , fTarget(target_value) {}
23
24private:
25    StateChanged onSeek(float t) override {
26        const auto& lerp_info = this->getLERPInfo(t);
27
28        // Text value keyframes are treated as selectors, not as interpolated values.
29        if (*fTarget != fValues[SkToSizeT(lerp_info.vrec0.idx)]) {
30            *fTarget = fValues[SkToSizeT(lerp_info.vrec0.idx)];
31            return true;
32        }
33
34        return false;
35    }
36
37    const std::vector<TextValue> fValues;
38    TextValue*                   fTarget;
39
40    using INHERITED = KeyframeAnimator;
41};
42
43class TextExpressionAnimator final : public Animator {
44public:
45    TextExpressionAnimator(sk_sp<ExpressionEvaluator<SkString>> expression_evaluator,
46        TextValue* target_value)
47        : fExpressionEvaluator(std::move(expression_evaluator))
48        , fTarget(target_value) {}
49
50private:
51
52    StateChanged onSeek(float t) override {
53        SkString old_value = fTarget->fText;
54
55        fTarget->fText = fExpressionEvaluator->evaluate(t);
56
57        return fTarget->fText != old_value;
58    }
59
60    sk_sp<ExpressionEvaluator<SkString>> fExpressionEvaluator;
61    TextValue* fTarget;
62};
63
64class TextAnimatorBuilder final : public AnimatorBuilder {
65public:
66    explicit TextAnimatorBuilder(TextValue* target)
67        : INHERITED(Keyframe::Value::Type::kIndex)
68        , fTarget(target) {}
69
70    sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder& abuilder,
71                                    const skjson::ArrayValue& jkfs) override {
72        SkASSERT(jkfs.size() > 0);
73
74        fValues.reserve(jkfs.size());
75        if (!this->parseKeyframes(abuilder, jkfs)) {
76            return nullptr;
77        }
78        fValues.shrink_to_fit();
79
80        return sk_sp<TextKeyframeAnimator>(
81                    new TextKeyframeAnimator(std::move(fKFs),
82                                                std::move(fCMs),
83                                                std::move(fValues),
84                                                fTarget));
85    }
86
87    sk_sp<Animator> makeFromExpression(ExpressionManager& em, const char* expr) override {
88         sk_sp<ExpressionEvaluator<SkString>> expression_evaluator =
89                em.createStringExpressionEvaluator(expr);
90            return sk_make_sp<TextExpressionAnimator>(expression_evaluator, fTarget);
91    }
92
93    bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override {
94        return Parse(jv, abuilder, fTarget);
95    }
96
97private:
98    bool parseKFValue(const AnimationBuilder& abuilder,
99                        const skjson::ObjectValue&,
100                        const skjson::Value& jv,
101                        Keyframe::Value* v) override {
102        TextValue val;
103        if (!Parse(jv, abuilder, &val)) {
104            return false;
105        }
106
107        // TODO: full deduping?
108        if (fValues.empty() || val != fValues.back()) {
109            fValues.push_back(std::move(val));
110        }
111
112        v->idx = SkToU32(fValues.size() - 1);
113
114        return true;
115    }
116
117    std::vector<TextValue> fValues;
118    TextValue*             fTarget;
119
120    using INHERITED = AnimatorBuilder;
121};
122
123} // namespace
124
125template <>
126bool AnimatablePropertyContainer::bind<TextValue>(const AnimationBuilder& abuilder,
127                                                  const skjson::ObjectValue* jprop,
128                                                  TextValue* v) {
129    TextAnimatorBuilder builder(v);
130    return this->bindImpl(abuilder, jprop, builder);
131}
132
133} // namespace skottie::internal
134