1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "track_animation_state.h"
17 
18 #include <limits>
19 
20 META_BEGIN_NAMESPACE()
21 
22 namespace Internal {
23 
SetTrackDataParams(TrackDataParams && params)24 void TrackAnimationState::SetTrackDataParams(TrackDataParams&& params)
25 {
26     trackParams_ = BASE_NS::move(params);
27 }
28 
AddKeyframe(float timestamp,const IAny::ConstPtr & value)29 size_t TrackAnimationState::AddKeyframe(float timestamp, const IAny::ConstPtr& value)
30 {
31     if (!keyframeArray_) {
32         CORE_LOG_E("Invalid keyframe array target.");
33         return ITrackAnimation::INVALID_INDEX;
34     }
35 
36     timestamp = BASE_NS::Math::clamp01(timestamp);
37     size_t index = 0;
38 
39     // Find index for timestamp which maintains ascending timestamp order
40     auto timestamps = GetTimeStamps()->GetValue();
41 
42     for (auto t : timestamps) {
43         if (t > timestamp) {
44             break;
45         }
46         index++;
47     }
48 
49     if (keyframeArray_->InsertAnyAt(index, *value)) {
50         GetTimeStamps()->InsertValueAt(index, timestamp);
51         ValidateValues();
52     } else {
53         CORE_LOG_E("Failed to add keyframe to TrackAnimation");
54         index = ITrackAnimation::INVALID_INDEX;
55     }
56 
57     return index;
58 }
59 
RemoveKeyframe(size_t index)60 bool TrackAnimationState::RemoveKeyframe(size_t index)
61 {
62     if (!keyframeArray_ || index >= GetTimeStamps()->GetSize()) {
63         return false;
64     }
65     if (keyframeArray_->RemoveAt(index)) {
66         GetTimeStamps()->RemoveAt(index);
67         return true;
68     }
69     return false;
70 }
71 
UpdateValid()72 bool TrackAnimationState::UpdateValid()
73 {
74     if (!(keyframeArray_ && trackStart_ && trackEnd_ && currentValue_)) {
75         return false;
76     }
77     auto& timestamps = GetTimeStamps();
78     auto size = timestamps->GetSize();
79     if (timestamps && size > 1 && keyframeArray_->GetSize() == size) {
80         // Update our timestamp range
81         startProgress_ = timestamps->GetValueAt(0);
82         endProgress_ = timestamps->GetValueAt(size - 1);
83         return true;
84     }
85     return false;
86 }
87 
ResetCurrentTrack()88 void TrackAnimationState::ResetCurrentTrack()
89 {
90     currentRangeStartTs_ = std::numeric_limits<float>::max();
91     currentRangeEndTs_ = {};
92     currentIndex_ = ITrackAnimation::INVALID_INDEX;
93 }
94 
Reset()95 void TrackAnimationState::Reset()
96 {
97     trackStart_.reset();
98     trackEnd_.reset();
99     currentValue_.reset();
100 }
101 
ValidateValues()102 bool TrackAnimationState::ValidateValues()
103 {
104     if (!keyframeArray_) {
105         return false;
106     }
107     if (trackStart_ && trackStart_->GetTypeId() != keyframeArray_->GetTypeId(TypeIdRole::ITEM)) {
108         Reset();
109     }
110     if (!trackStart_) {
111         if (auto size = keyframeArray_->GetSize()) {
112             if (trackStart_ = keyframeArray_->Clone({ CloneValueType::DEFAULT_VALUE, TypeIdRole::ITEM }); trackStart_) {
113                 trackEnd_ = trackStart_->Clone(false);
114                 keyframeArray_->GetAnyAt(0, *trackStart_);
115                 keyframeArray_->GetAnyAt(size - 1, *trackEnd_);
116                 currentValue_ = trackStart_->Clone(true);
117             }
118         } else {
119             Reset();
120         }
121     }
122     return trackStart_ != nullptr;
123 }
124 
SetKeyframes(const IArrayAny::Ptr & keyframes)125 bool TrackAnimationState::SetKeyframes(const IArrayAny::Ptr& keyframes)
126 {
127     bool valid = false;
128     if (keyframes) {
129         if (keyframes != keyframeArray_) {
130             keyframeArray_ = keyframes;
131             valid = ValidateValues();
132         } else {
133             valid = true;
134         }
135     }
136     if (auto& timestamps = GetTimeStamps()) {
137         if (const auto size = timestamps->GetSize()) {
138             startProgress_ = timestamps->GetValueAt(0);
139             endProgress_ = timestamps->GetValueAt(size - 1);
140         }
141     }
142     if (!valid) {
143         Reset();
144     }
145     return valid;
146 }
147 
IsBetween(float value,float rangeStart,float rangeEnd,bool includeEnd)148 inline static constexpr bool IsBetween(float value, float rangeStart, float rangeEnd, bool includeEnd)
149 {
150     if (includeEnd) {
151         return value >= rangeStart && value <= rangeEnd;
152     }
153     return value >= rangeStart && value < rangeEnd;
154 }
155 
IsInCurrentRange(float progress) const156 bool TrackAnimationState::IsInCurrentRange(float progress) const noexcept
157 {
158     if (currentIndex_ == ITrackAnimation::INVALID_INDEX || !keyframeArray_) {
159         return false;
160     }
161 
162     // Include also end in the range if last keyframe
163     return IsBetween(
164         progress, currentRangeStartTs_, currentRangeEndTs_, currentIndex_ == keyframeArray_->GetSize() - 1);
165 }
166 
UpdateIndex(float progress)167 BASE_NS::pair<uint32_t, float> TrackAnimationState::UpdateIndex(float progress)
168 {
169     uint32_t index = static_cast<uint32_t>(currentIndex_);
170     if (IsInCurrentRange(progress)) {
171         return { index, GetCurrentTrackProgress(progress) };
172     }
173 
174     const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
175     auto& timestamps = GetTimeStamps();
176     if (!size || !timestamps || timestamps->GetSize() != size) {
177         index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX));
178     } else {
179         size_t lo = 0;
180         auto hi = size - 1;
181         auto startTs = timestamps->GetValueAt(lo);
182         auto endTs = timestamps->GetValueAt(hi);
183         if (progress < startTs || progress > endTs) {
184             index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX));
185         } else {
186             while (lo <= hi) {
187                 const auto mid = lo + (hi - lo) / 2;
188                 const auto endIndex = mid < size - 1 ? mid + 1 : mid;
189                 startTs = timestamps->GetValueAt(mid);
190                 endTs = timestamps->GetValueAt(endIndex);
191                 if (IsBetween(progress, startTs, endTs, mid >= endIndex)) {
192                     // Found correct keyframe
193                     index = JumpTo(mid);
194                     break;
195                 }
196                 if (progress < startTs) {
197                     hi = mid - 1;
198                 } else {
199                     lo = mid + 1;
200                 }
201             }
202         }
203     }
204     return { index, GetCurrentTrackProgress(progress) };
205 }
206 
JumpTo(size_t index)207 uint32_t TrackAnimationState::JumpTo(size_t index)
208 {
209     if (index == currentIndex_) {
210         return currentIndex_;
211     }
212 
213     if (index == ITrackAnimation::INVALID_INDEX) {
214         currentIndex_ = index;
215         ResetCurrentTrack();
216     } else {
217         const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
218         auto& timestamps = GetTimeStamps();
219         if (!size || !timestamps || timestamps->GetSize() != size) {
220             return ITrackAnimation::INVALID_INDEX;
221         }
222         index = BASE_NS::Math::min(index, size - 1);
223 
224         bool last = index == size - 1;
225         auto start = last ? index - 1 : index;
226         auto end = start + 1; // < size - 1 ? start + 1 : start;
227 
228         keyframeArray_->GetAnyAt(end, *trackEnd_);
229         keyframeArray_->GetAnyAt(start, *trackStart_);
230         currentRangeStartTs_ = timestamps->GetValueAt(start);
231         currentRangeEndTs_ = timestamps->GetValueAt(end);
232         currentIndex_ = last ? end : index;
233     }
234     return static_cast<uint32_t>(currentIndex_);
235 }
236 
GetCurrentTrackProgress(float progress) const237 float TrackAnimationState::GetCurrentTrackProgress(float progress) const noexcept
238 {
239     if (currentIndex_ == ITrackAnimation::INVALID_INDEX) {
240         return 0.f;
241     }
242     float currentTrackRange = currentRangeEndTs_ - currentRangeStartTs_;
243     auto rangeProgress = BASE_NS::Math::clamp(progress, startProgress_, endProgress_);
244     if (currentTrackRange < BASE_NS::Math::EPSILON) {
245         return 1.f;
246     }
247     return 1.f / currentTrackRange * (rangeProgress - currentRangeStartTs_);
248 }
249 
250 } // namespace Internal
251 
252 META_END_NAMESPACE()
253