1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "modules/skottie/src/animator/KeyframeAnimator.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottieJson.h"
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#define DUMP_KF_RECORDS 0
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_cinamespace skottie::internal {
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ciKeyframeAnimator::~KeyframeAnimator() = default;
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ciKeyframeAnimator::LERPInfo KeyframeAnimator::getLERPInfo(float t) const {
19cb93a386Sopenharmony_ci    SkASSERT(!fKFs.empty());
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci    if (t <= fKFs.front().t) {
22cb93a386Sopenharmony_ci        // Constant/clamped segment.
23cb93a386Sopenharmony_ci        return { 0, fKFs.front().v, fKFs.front().v };
24cb93a386Sopenharmony_ci    }
25cb93a386Sopenharmony_ci    if (t >= fKFs.back().t) {
26cb93a386Sopenharmony_ci        // Constant/clamped segment.
27cb93a386Sopenharmony_ci        return { 0, fKFs.back().v, fKFs.back().v };
28cb93a386Sopenharmony_ci    }
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci    // Cache the current segment (most queries have good locality).
31cb93a386Sopenharmony_ci    if (!fCurrentSegment.contains(t)) {
32cb93a386Sopenharmony_ci        fCurrentSegment = this->find_segment(t);
33cb93a386Sopenharmony_ci    }
34cb93a386Sopenharmony_ci    SkASSERT(fCurrentSegment.contains(t));
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ci    if (fCurrentSegment.kf0->mapping == Keyframe::kConstantMapping) {
37cb93a386Sopenharmony_ci        // Constant/hold segment.
38cb93a386Sopenharmony_ci        return { 0, fCurrentSegment.kf0->v, fCurrentSegment.kf0->v };
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    return {
42cb93a386Sopenharmony_ci        this->compute_weight(fCurrentSegment, t),
43cb93a386Sopenharmony_ci        fCurrentSegment.kf0->v,
44cb93a386Sopenharmony_ci        fCurrentSegment.kf1->v,
45cb93a386Sopenharmony_ci    };
46cb93a386Sopenharmony_ci}
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ciKeyframeAnimator::KFSegment KeyframeAnimator::find_segment(float t) const {
49cb93a386Sopenharmony_ci    SkASSERT(fKFs.size() > 1);
50cb93a386Sopenharmony_ci    SkASSERT(t > fKFs.front().t);
51cb93a386Sopenharmony_ci    SkASSERT(t < fKFs.back().t);
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci    auto kf0 = &fKFs.front(),
54cb93a386Sopenharmony_ci         kf1 = &fKFs.back();
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci    // Binary-search, until we reduce to sequential keyframes.
57cb93a386Sopenharmony_ci    while (kf0 + 1 != kf1) {
58cb93a386Sopenharmony_ci        SkASSERT(kf0 < kf1);
59cb93a386Sopenharmony_ci        SkASSERT(kf0->t <= t && t < kf1->t);
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci        const auto mid_kf = kf0 + (kf1 - kf0) / 2;
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci        if (t >= mid_kf->t) {
64cb93a386Sopenharmony_ci            kf0 = mid_kf;
65cb93a386Sopenharmony_ci        } else {
66cb93a386Sopenharmony_ci            kf1 = mid_kf;
67cb93a386Sopenharmony_ci        }
68cb93a386Sopenharmony_ci    }
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    return {kf0, kf1};
71cb93a386Sopenharmony_ci}
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_cifloat KeyframeAnimator::compute_weight(const KFSegment &seg, float t) const {
74cb93a386Sopenharmony_ci    SkASSERT(seg.contains(t));
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci    // Linear weight.
77cb93a386Sopenharmony_ci    auto w = (t - seg.kf0->t) / (seg.kf1->t - seg.kf0->t);
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    // Optional cubic mapper.
80cb93a386Sopenharmony_ci    if (seg.kf0->mapping >= Keyframe::kCubicIndexOffset) {
81cb93a386Sopenharmony_ci        const auto mapper_index = SkToSizeT(seg.kf0->mapping - Keyframe::kCubicIndexOffset);
82cb93a386Sopenharmony_ci        w = fCMs[mapper_index].computeYFromX(w);
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci    return w;
86cb93a386Sopenharmony_ci}
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ciAnimatorBuilder::~AnimatorBuilder() = default;
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_cibool AnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder,
91cb93a386Sopenharmony_ci                                     const skjson::ArrayValue& jkfs) {
92cb93a386Sopenharmony_ci    // Keyframe format:
93cb93a386Sopenharmony_ci    //
94cb93a386Sopenharmony_ci    // [                        // array of
95cb93a386Sopenharmony_ci    //   {
96cb93a386Sopenharmony_ci    //     "t": <float>         // keyframe time
97cb93a386Sopenharmony_ci    //     "s": <T>             // keyframe value
98cb93a386Sopenharmony_ci    //     "h": <bool>          // optional constant/hold keyframe marker
99cb93a386Sopenharmony_ci    //     "i": [<float,float>] // optional "in" Bezier control point
100cb93a386Sopenharmony_ci    //     "o": [<float,float>] // optional "out" Bezier control point
101cb93a386Sopenharmony_ci    //   },
102cb93a386Sopenharmony_ci    //   ...
103cb93a386Sopenharmony_ci    // ]
104cb93a386Sopenharmony_ci    //
105cb93a386Sopenharmony_ci    // Legacy keyframe format:
106cb93a386Sopenharmony_ci    //
107cb93a386Sopenharmony_ci    // [                        // array of
108cb93a386Sopenharmony_ci    //   {
109cb93a386Sopenharmony_ci    //     "t": <float>         // keyframe time
110cb93a386Sopenharmony_ci    //     "s": <T>             // keyframe start value
111cb93a386Sopenharmony_ci    //     "e": <T>             // keyframe end value
112cb93a386Sopenharmony_ci    //     "h": <bool>          // optional constant/hold keyframe marker (constant mapping)
113cb93a386Sopenharmony_ci    //     "i": [<float,float>] // optional "in" Bezier control point (cubic mapping)
114cb93a386Sopenharmony_ci    //     "o": [<float,float>] // optional "out" Bezier control point (cubic mapping)
115cb93a386Sopenharmony_ci    //   },
116cb93a386Sopenharmony_ci    //   ...
117cb93a386Sopenharmony_ci    //   {
118cb93a386Sopenharmony_ci    //     "t": <float>         // last keyframe only specifies a t
119cb93a386Sopenharmony_ci    //                          // the value is prev. keyframe end value
120cb93a386Sopenharmony_ci    //   }
121cb93a386Sopenharmony_ci    // ]
122cb93a386Sopenharmony_ci    //
123cb93a386Sopenharmony_ci    // Note: the legacy format contains duplicates, as normal frames are contiguous:
124cb93a386Sopenharmony_ci    //       frame(n).e == frame(n+1).s
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci    const auto parse_value = [&](const skjson::ObjectValue& jkf, size_t i, Keyframe::Value* v) {
127cb93a386Sopenharmony_ci        auto parsed = this->parseKFValue(abuilder, jkf, jkf["s"], v);
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci        // A missing value is only OK for the last legacy KF
130cb93a386Sopenharmony_ci        // (where it is pulled from prev KF 'end' value).
131cb93a386Sopenharmony_ci        if (!parsed && i > 0 && i == jkfs.size() - 1) {
132cb93a386Sopenharmony_ci            const skjson::ObjectValue* prev_kf = jkfs[i - 1];
133cb93a386Sopenharmony_ci            SkASSERT(prev_kf);
134cb93a386Sopenharmony_ci            parsed = this->parseKFValue(abuilder, jkf, (*prev_kf)["e"], v);
135cb93a386Sopenharmony_ci        }
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci        return parsed;
138cb93a386Sopenharmony_ci    };
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci    bool constant_value = true;
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    fKFs.reserve(jkfs.size());
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci    for (size_t i = 0; i < jkfs.size(); ++i) {
145cb93a386Sopenharmony_ci        const skjson::ObjectValue* jkf = jkfs[i];
146cb93a386Sopenharmony_ci        if (!jkf) {
147cb93a386Sopenharmony_ci            return false;
148cb93a386Sopenharmony_ci        }
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci        float t;
151cb93a386Sopenharmony_ci        if (!Parse<float>((*jkf)["t"], &t)) {
152cb93a386Sopenharmony_ci            return false;
153cb93a386Sopenharmony_ci        }
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci        Keyframe::Value v;
156cb93a386Sopenharmony_ci        if (!parse_value(*jkf, i, &v)) {
157cb93a386Sopenharmony_ci            return false;
158cb93a386Sopenharmony_ci        }
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci        if (i > 0) {
161cb93a386Sopenharmony_ci            auto& prev_kf = fKFs.back();
162cb93a386Sopenharmony_ci
163cb93a386Sopenharmony_ci            // Ts must be strictly monotonic.
164cb93a386Sopenharmony_ci            if (t <= prev_kf.t) {
165cb93a386Sopenharmony_ci                return false;
166cb93a386Sopenharmony_ci            }
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci            // We can power-reduce the mapping of repeated values (implicitly constant).
169cb93a386Sopenharmony_ci            if (v.equals(prev_kf.v, keyframe_type)) {
170cb93a386Sopenharmony_ci                prev_kf.mapping = Keyframe::kConstantMapping;
171cb93a386Sopenharmony_ci            }
172cb93a386Sopenharmony_ci        }
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ci        fKFs.push_back({t, v, this->parseMapping(*jkf)});
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ci        constant_value = constant_value && (v.equals(fKFs.front().v, keyframe_type));
177cb93a386Sopenharmony_ci    }
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    SkASSERT(fKFs.size() == jkfs.size());
180cb93a386Sopenharmony_ci    fCMs.shrink_to_fit();
181cb93a386Sopenharmony_ci
182cb93a386Sopenharmony_ci    if (constant_value) {
183cb93a386Sopenharmony_ci        // When all keyframes hold the same value, we can discard all but one
184cb93a386Sopenharmony_ci        // (interpolation has no effect).
185cb93a386Sopenharmony_ci        fKFs.resize(1);
186cb93a386Sopenharmony_ci    }
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_ci#if(DUMP_KF_RECORDS)
189cb93a386Sopenharmony_ci    SkDEBUGF("Animator[%p], values: %lu, KF records: %zu\n",
190cb93a386Sopenharmony_ci             this, fKFs.back().v_idx + 1, fKFs.size());
191cb93a386Sopenharmony_ci    for (const auto& kf : fKFs) {
192cb93a386Sopenharmony_ci        SkDEBUGF("  { t: %1.3f, v_idx: %lu, mapping: %lu }\n", kf.t, kf.v_idx, kf.mapping);
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci#endif
195cb93a386Sopenharmony_ci    return true;
196cb93a386Sopenharmony_ci}
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ciuint32_t AnimatorBuilder::parseMapping(const skjson::ObjectValue& jkf) {
199cb93a386Sopenharmony_ci    if (ParseDefault(jkf["h"], false)) {
200cb93a386Sopenharmony_ci        return Keyframe::kConstantMapping;
201cb93a386Sopenharmony_ci    }
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci    SkPoint c0, c1;
204cb93a386Sopenharmony_ci    if (!Parse(jkf["o"], &c0) ||
205cb93a386Sopenharmony_ci        !Parse(jkf["i"], &c1) ||
206cb93a386Sopenharmony_ci        SkCubicMap::IsLinear(c0, c1)) {
207cb93a386Sopenharmony_ci        return Keyframe::kLinearMapping;
208cb93a386Sopenharmony_ci    }
209cb93a386Sopenharmony_ci
210cb93a386Sopenharmony_ci    // De-dupe sequential cubic mappers.
211cb93a386Sopenharmony_ci    if (c0 != prev_c0 || c1 != prev_c1 || fCMs.empty()) {
212cb93a386Sopenharmony_ci        fCMs.emplace_back(c0, c1);
213cb93a386Sopenharmony_ci        prev_c0 = c0;
214cb93a386Sopenharmony_ci        prev_c1 = c1;
215cb93a386Sopenharmony_ci    }
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci    SkASSERT(!fCMs.empty());
218cb93a386Sopenharmony_ci    return SkToU32(fCMs.size()) - 1 + Keyframe::kCubicIndexOffset;
219cb93a386Sopenharmony_ci}
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci} // namespace skottie::internal
222