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#ifndef SkottieKeyframeAnimator_DEFINED
9#define SkottieKeyframeAnimator_DEFINED
10
11#include "include/core/SkCubicMap.h"
12#include "include/core/SkPoint.h"
13#include "include/private/SkNoncopyable.h"
14#include "modules/skottie/src/animator/Animator.h"
15
16#include <vector>
17
18namespace skjson {
19class ArrayValue;
20class ObjectValue;
21class Value;
22} // namespace skjson
23
24namespace skottie::internal {
25
26class AnimationBuilder;
27
28struct Keyframe {
29 // We can store scalar values inline; other types are stored externally,
30 // and we track them by index.
31 struct Value {
32 union {
33 uint32_t idx;
34 float flt;
35 };
36
37 bool operator==(const Value& other) const {
38 return idx == other.idx
39 || flt == other.flt; // +/-0
40 }
41 bool operator!=(const Value& other) const { return !((*this) == other); }
42 };
43
44 float t;
45 Value v;
46 uint32_t mapping; // Encodes the value interpolation in [KFRec_n .. KFRec_n+1):
47 // 0 -> constant
48 // 1 -> linear
49 // n -> cubic: cubic_mappers[n-2]
50
51 static constexpr uint32_t kConstantMapping = 0;
52 static constexpr uint32_t kLinearMapping = 1;
53 static constexpr uint32_t kCubicIndexOffset = 2;
54};
55
56class KeyframeAnimator : public Animator {
57public:
58 virtual ~KeyframeAnimator() override;
59
60 bool isConstant() const {
61 SkASSERT(!fKFs.empty());
62
63 // parseKeyFrames() ensures we only keep a single frame for constant properties.
64 return fKFs.size() == 1;
65 }
66
67protected:
68 KeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms)
69 : fKFs(std::move(kfs))
70 , fCMs(std::move(cms)) {}
71
72 struct LERPInfo {
73 float weight; // vrec0/vrec1 weight [0..1]
74 Keyframe::Value vrec0, vrec1;
75
76 bool isConstant() const { return vrec0 == vrec1; }
77 };
78
79 // Main entry point: |t| -> LERPInfo
80 LERPInfo getLERPInfo(float t) const;
81
82private:
83 // Two sequential KFRecs determine how the value varies within [kf0 .. kf1)
84 struct KFSegment {
85 const Keyframe* kf0;
86 const Keyframe* kf1;
87
88 bool contains(float t) const {
89 SkASSERT(!!kf0 == !!kf1);
90 SkASSERT(!kf0 || kf1 == kf0 + 1);
91
92 return kf0 && kf0->t <= t && t < kf1->t;
93 }
94 };
95
96 // Find the KFSegment containing |t|.
97 KFSegment find_segment(float t) const;
98
99 // Given a |t| and a containing KFSegment, compute the local interpolation weight.
100 float compute_weight(const KFSegment& seg, float t) const;
101
102 const std::vector<Keyframe> fKFs; // Keyframe records, one per AE/Lottie keyframe.
103 const std::vector<SkCubicMap> fCMs; // Optional cubic mappers (Bezier interpolation).
104 mutable KFSegment fCurrentSegment = { nullptr, nullptr }; // Cached segment.
105};
106
107class KeyframeAnimatorBuilder : public SkNoncopyable {
108public:
109 virtual ~KeyframeAnimatorBuilder();
110
111 virtual sk_sp<KeyframeAnimator> make(const AnimationBuilder&,
112 const skjson::ArrayValue&,
113 void* target_value) = 0;
114
115 virtual bool parseValue(const AnimationBuilder&, const skjson::Value&, void*) const = 0;
116
117protected:
118 virtual bool parseKFValue(const AnimationBuilder&,
119 const skjson::ObjectValue&,
120 const skjson::Value&,
121 Keyframe::Value*) = 0;
122
123 bool parseKeyframes(const AnimationBuilder&, const skjson::ArrayValue&);
124
125 std::vector<Keyframe> fKFs; // Keyframe records, one per AE/Lottie keyframe.
126 std::vector<SkCubicMap> fCMs; // Optional cubic mappers (Bezier interpolation).
127
128private:
129 uint32_t parseMapping(const skjson::ObjectValue&);
130
131 // Track previous cubic map parameters (for deduping).
132 SkPoint prev_c0 = { 0, 0 },
133 prev_c1 = { 0, 0 };
134};
135
136template <typename T>
137T Lerp(const T& a, const T& b, float t) { return a + (b - a) * t; }
138
139} // namespace skottie::internal
140
141#endif // SkottieKeyframeAnimator_DEFINED
142