| 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 | |
| 18 | namespace skjson { |
| 19 | class ArrayValue; |
| 20 | class ObjectValue; |
| 21 | class Value; |
| 22 | } // namespace skjson |
| 23 | |
| 24 | namespace skottie::internal { |
| 25 | |
| 26 | class AnimationBuilder; |
| 27 | |
| 28 | struct 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 | |
| 56 | class KeyframeAnimator : public Animator { |
| 57 | public: |
| 58 | ~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 | |
| 67 | protected: |
| 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 | |
| 82 | private: |
| 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 | |
| 107 | class KeyframeAnimatorBuilder : public SkNoncopyable { |
| 108 | public: |
| 109 | virtual ~KeyframeAnimatorBuilder(); |
| 110 | |
| 111 | virtual sk_sp<KeyframeAnimator> make(const AnimationBuilder&, const skjson::ArrayValue&) = 0; |
| 112 | |
| 113 | virtual bool parseValue(const AnimationBuilder&, const skjson::Value&) const = 0; |
| 114 | |
| 115 | protected: |
| 116 | virtual bool parseKFValue(const AnimationBuilder&, |
| 117 | const skjson::ObjectValue&, |
| 118 | const skjson::Value&, |
| 119 | Keyframe::Value*) = 0; |
| 120 | |
| 121 | bool parseKeyframes(const AnimationBuilder&, const skjson::ArrayValue&); |
| 122 | |
| 123 | std::vector<Keyframe> fKFs; // Keyframe records, one per AE/Lottie keyframe. |
| 124 | std::vector<SkCubicMap> fCMs; // Optional cubic mappers (Bezier interpolation). |
| 125 | |
| 126 | private: |
| 127 | uint32_t parseMapping(const skjson::ObjectValue&); |
| 128 | |
| 129 | // Track previous cubic map parameters (for deduping). |
| 130 | SkPoint prev_c0 = { 0, 0 }, |
| 131 | prev_c1 = { 0, 0 }; |
| 132 | }; |
| 133 | |
| 134 | template <typename T> |
| 135 | T Lerp(const T& a, const T& b, float t) { return a + (b - a) * t; } |
| 136 | |
| 137 | } // namespace skottie::internal |
| 138 | |
| 139 | #endif // SkottieKeyframeAnimator_DEFINED |
| 140 | |