| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "timeline_function.h" |
| |
| #include <zircon/assert.h> |
| |
| #include <limits> |
| #include <utility> |
| |
| namespace media { |
| |
| // static |
| // Translates a given reference value through a provided timeline function, producing a |
| // corresponding subject value. Returns kOverflow if result can't fit in an int64_t. |
| int64_t TimelineFunction::Apply(int64_t subject_time, int64_t reference_time, TimelineRate rate, |
| int64_t reference_input) { |
| // Round down when scaling. This ensures that we preserve the scaled distance between positive |
| // and negative points on the timeline. For example, suppose we call this twice: |
| // |
| // 1. reference_input - reference_time = 20, ratio = 1/8, scaled_value = 2.5 |
| // 2. reference_input - reference_time = -20, ratio = 1/8, scaled_value = -2.5 |
| // |
| // If we truncate, the scaled values are 2 and -2, which have a difference of 4, while the true |
| // scaled difference should be 40*1/8 = 5. However, if we round down, the scaled values are |
| // 2 and -3, which have a difference of 5. |
| int64_t scaled_value = |
| rate.Scale(reference_input - reference_time, TimelineRate::RoundingMode::Floor); |
| if (scaled_value == TimelineRate::kOverflow) { |
| return TimelineRate::kOverflow; |
| } |
| |
| int64_t result_value = scaled_value + subject_time; |
| auto result_value_128 = static_cast<__int128_t>(scaled_value) + subject_time; |
| if (result_value_128 != result_value) { |
| return TimelineRate::kOverflow; |
| } |
| |
| return result_value; |
| } |
| |
| // static |
| // Combine two given timeline functions, forming a new one. ASSERT upon overflow. |
| TimelineFunction TimelineFunction::Compose(const TimelineFunction& bc, const TimelineFunction& ab, |
| bool exact) { |
| // This composition approach may compromise range and accuracy (in some cases) for simplicity. |
| // TODO(mpuryear): re-implement for improved range/accuracy (without much more cost)? |
| auto scaled_subject_time = bc.Apply(ab.subject_time()); |
| ZX_ASSERT(scaled_subject_time != TimelineRate::kOverflow); |
| |
| return TimelineFunction(scaled_subject_time, ab.reference_time(), |
| TimelineRate::Product(ab.rate(), bc.rate(), exact)); |
| } |
| |
| } // namespace media |