#ifndef UI_GFX_ANIMATION_KEYFRAME_KEYFRAMED_ANIMATION_CURVE_INL_H_
#define UI_GFX_ANIMATION_KEYFRAME_KEYFRAMED_ANIMATION_CURVE_INL_H_
namespace {
template <class KeyframeType>
void InsertKeyframe(std::unique_ptr<KeyframeType> keyframe,
std::vector<std::unique_ptr<KeyframeType>>* keyframes) {
if (!keyframes->empty() && keyframe->Time() < keyframes->back()->Time()) {
for (size_t i = 0; i < keyframes->size(); ++i) {
if (keyframe->Time() < keyframes->at(i)->Time()) {
keyframes->insert(keyframes->begin() + i, std::move(keyframe));
return;
}
}
}
keyframes->push_back(std::move(keyframe));
}
struct TimeValues {
base::TimeDelta start_time;
base::TimeDelta duration;
double progress;
};
template <typename KeyframeType>
TimeValues GetTimeValues(const KeyframeType& start_frame,
const KeyframeType& end_frame,
double scaled_duration,
base::TimeDelta time) {
TimeValues values;
values.start_time = start_frame.Time() * scaled_duration;
values.duration = (end_frame.Time() * scaled_duration) - values.start_time;
const base::TimeDelta elapsed = time - values.start_time;
values.progress = (elapsed.is_inf() || values.duration.is_zero())
? 1.0
: (elapsed / values.duration);
return values;
}
template <typename KeyframeType>
base::TimeDelta TransformedAnimationTime(
const std::vector<std::unique_ptr<KeyframeType>>& keyframes,
const std::unique_ptr<gfx::TimingFunction>& timing_function,
double scaled_duration,
base::TimeDelta time,
gfx::TimingFunction::LimitDirection limit_direction) {
if (timing_function) {
const auto values = GetTimeValues(*keyframes.front(), *keyframes.back(),
scaled_duration, time);
double adjusted_progress =
timing_function->GetValue(values.progress, limit_direction);
time = (values.duration * adjusted_progress) + values.start_time;
}
return time;
}
template <typename KeyframeType>
size_t GetActiveKeyframe(
const std::vector<std::unique_ptr<KeyframeType>>& keyframes,
double scaled_duration,
base::TimeDelta time) {
DCHECK_GE(keyframes.size(), 2ul);
if (time == keyframes.front()->Time() * scaled_duration) {
return 0;
}
if (time == keyframes.back()->Time() * scaled_duration) {
return keyframes.size() - 2;
}
size_t i = 0;
while ((i < keyframes.size() - 2) &&
(time >= (keyframes[i + 1]->Time() * scaled_duration))) {
++i;
}
return i;
}
template <typename KeyframeType>
double TransformedKeyframeProgress(
const std::vector<std::unique_ptr<KeyframeType>>& keyframes,
double scaled_duration,
base::TimeDelta time,
gfx::TimingFunction::LimitDirection limit_direction,
size_t i) {
base::TimeDelta interval_start = keyframes[i]->Time() * scaled_duration;
base::TimeDelta interval_end = keyframes[i + 1]->Time() * scaled_duration;
base::TimeDelta duration = interval_end - interval_start;
double progress;
if (duration.is_zero()) {
progress = (time == keyframes.front()->Time() * scaled_duration) ? 0 : 1;
} else {
progress = (time - interval_start) / duration;
}
return keyframes[i]->timing_function()
? keyframes[i]->timing_function()->GetValue(progress,
limit_direction)
: progress;
}
int GetTimingFunctionSteps(const gfx::TimingFunction* timing_function) {
DCHECK(timing_function &&
timing_function->GetType() == gfx::TimingFunction::Type::STEPS);
const gfx::StepsTimingFunction* steps_timing_function =
reinterpret_cast<const gfx::StepsTimingFunction*>(timing_function);
DCHECK(steps_timing_function);
return steps_timing_function->steps();
}
template <class KeyframeType>
base::TimeDelta ComputeTickInterval(
const std::unique_ptr<gfx::TimingFunction>& timing_function,
double scaled_duration,
const std::vector<std::unique_ptr<KeyframeType>>& keyframes) {
DCHECK_LT(0u, keyframes.size());
gfx::TimingFunction::Type timing_function_type =
timing_function ? timing_function->GetType()
: gfx::TimingFunction::Type::LINEAR;
switch (timing_function_type) {
case gfx::TimingFunction::Type::LINEAR: {
base::TimeDelta min_interval = base::TimeDelta::Max();
for (size_t ii = 0; ii < keyframes.size() - 1; ++ii) {
KeyframeType* keyframe = keyframes[ii].get();
if (!keyframe->timing_function() ||
keyframe->timing_function()->GetType() !=
gfx::TimingFunction::Type::STEPS) {
return base::TimeDelta();
}
KeyframeType* next_keyframe = keyframes[ii + 1].get();
int steps = GetTimingFunctionSteps(keyframe->timing_function());
DCHECK_LT(0, steps);
base::TimeDelta interval = (next_keyframe->Time() - keyframe->Time()) *
scaled_duration / steps;
if (interval < min_interval)
min_interval = interval;
}
return min_interval;
}
case gfx::TimingFunction::Type::STEPS: {
return (keyframes.back()->Time() - keyframes.front()->Time()) *
scaled_duration / GetTimingFunctionSteps(timing_function.get());
}
case gfx::TimingFunction::Type::CUBIC_BEZIER:
break;
}
return base::TimeDelta();
}
struct KeyframesAndProgress {
size_t from;
size_t to;
double progress;
};
template <typename KeyframeType>
KeyframesAndProgress GetKeyframesAndProgress(
const std::vector<std::unique_ptr<KeyframeType>>& keyframes,
const std::unique_ptr<gfx::TimingFunction>& timing_function,
double scaled_duration,
base::TimeDelta time,
gfx::TimingFunction::LimitDirection limit_direction) {
if (keyframes.size() == 1) {
return {0, 0, 1};
}
base::TimeDelta start_time = keyframes.front()->Time() * scaled_duration;
base::TimeDelta end_time = keyframes.back()->Time() * scaled_duration;
time = std::clamp(time, start_time, end_time);
base::TimeDelta transformed_time = TransformedAnimationTime(
keyframes, timing_function, scaled_duration, time, limit_direction);
size_t keyframe_index =
GetActiveKeyframe(keyframes, scaled_duration, transformed_time);
double progress =
TransformedKeyframeProgress(keyframes, scaled_duration, transformed_time,
limit_direction, keyframe_index);
return {keyframe_index, keyframe_index + 1, progress};
}
}
#endif