Commit 54bf8f75 authored by Fredrik Söderquist's avatar Fredrik Söderquist Committed by Commit Bot

Use base::TimeDelta as internal representation for SMILTime

This changes the internal representation of SMILTime to use
base::TimeDelta. This aligns better with handling of time in general,
and should hopefully avoid issues that arise from having a floating
point representation.

The special values "indefinite" and "unresolved" are stored as
TimeDelta::Max() and TimeDelta::Max() - 1us respectively to satisfy the
requirements for ordering.

Bug: 1003338
Change-Id: I26ef5d14b45f646dcfcfe672736aa7caf343a375
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1806753
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#697148}
parent 266f07cf
...@@ -32,8 +32,8 @@ namespace blink { ...@@ -32,8 +32,8 @@ namespace blink {
SMILTime SMILTime::Repeat(SMILRepeatCount repeat_count) const { SMILTime SMILTime::Repeat(SMILRepeatCount repeat_count) const {
DCHECK(repeat_count.IsValid()); DCHECK(repeat_count.IsValid());
if (repeat_count.IsIndefinite() || repeat_count.IsUnspecified()) if (repeat_count.IsIndefinite() || repeat_count.IsUnspecified())
return SMILTime::Indefinite(); return Indefinite();
return SMILTime(time_ * repeat_count.NumericValue()); return time_ * repeat_count.NumericValue();
} }
std::ostream& operator<<(std::ostream& os, SMILTime time) { std::ostream& operator<<(std::ostream& os, SMILTime time) {
......
...@@ -39,85 +39,104 @@ namespace blink { ...@@ -39,85 +39,104 @@ namespace blink {
class SMILRepeatCount; class SMILRepeatCount;
struct SMILInterval; struct SMILInterval;
// SMILTime is used as both a time and time delta in the SMIL animation
// code. It is a small wrapper around TimeDelta that adds two sentinel values
// for SMIL concepts: "indefinite" and "unresolved".
//
// For ordering, the special values have the properties that:
//
// <finite SMILTime> < SMILTime::Indefinite() < SMILTime::Unresolved()
//
// SMILTime::Earliest() and SMILTime::Latest() are smallest and largest finite
// time values respectively and sort accordingly.
//
// Addition and subtraction follow the semantics defined for computations of
// active duration (https://www.w3.org/TR/SMIL3/smil-timing.html#q78).
class SMILTime { class SMILTime {
DISALLOW_NEW(); DISALLOW_NEW();
public: public:
constexpr SMILTime() : time_(0) {} constexpr SMILTime() = default;
static constexpr SMILTime Unresolved() { static constexpr SMILTime Unresolved() { return base::TimeDelta::Max(); }
return std::numeric_limits<double>::quiet_NaN();
}
static constexpr SMILTime Indefinite() { static constexpr SMILTime Indefinite() {
return std::numeric_limits<double>::infinity(); return base::TimeDelta::Max() - base::TimeDelta::FromMicroseconds(1);
} }
static constexpr SMILTime Earliest() { static constexpr SMILTime Latest() {
return -std::numeric_limits<double>::infinity(); return base::TimeDelta::Max() - base::TimeDelta::FromMicroseconds(2);
} }
static constexpr SMILTime Earliest() { return base::TimeDelta::Min(); }
static constexpr SMILTime FromSecondsD(double seconds) { static constexpr SMILTime FromSecondsD(double seconds) {
return SMILTime(seconds); return base::TimeDelta::FromSecondsD(seconds);
} }
static constexpr SMILTime FromMicroseconds(int64_t us) { static constexpr SMILTime FromMicroseconds(int64_t us) {
return SMILTime(static_cast<double>(us) / return base::TimeDelta::FromMicroseconds(us);
base::Time::kMicrosecondsPerSecond);
} }
// Used for computing progress. Don't use for anything else. // Used for computing progress. Don't use for anything else.
double InternalValueAsDouble() const { return time_; } double InternalValueAsDouble() const { return time_.InMicrosecondsF(); }
double InSecondsF() const { return time_; } double InSecondsF() const { return time_.InSecondsF(); }
int64_t InMicroseconds() const { int64_t InMicroseconds() const { return time_.InMicroseconds(); }
return time_ * base::Time::kMicrosecondsPerSecond;
}
bool IsFinite() const { return std::isfinite(time_); } bool IsFinite() const { return *this < Indefinite(); }
bool IsIndefinite() const { return std::isinf(time_); } bool IsIndefinite() const { return *this == Indefinite(); }
bool IsUnresolved() const { return std::isnan(time_); } bool IsUnresolved() const { return *this == Unresolved(); }
SMILTime Repeat(SMILRepeatCount repeat_count) const; SMILTime Repeat(SMILRepeatCount repeat_count) const;
SMILTime operator+(SMILTime other) const { return time_ + other.time_; } SMILTime operator+(SMILTime other) const {
SMILTime operator-(SMILTime other) const { return time_ - other.time_; } if (!IsFinite())
SMILTime operator-() const { return SMILTime(-time_); } return time_;
if (!other.IsFinite())
return other;
SMILTime result(time_ + other.time_);
return result.IsFinite() ? result : Latest();
}
SMILTime operator-(SMILTime other) const {
if (!IsFinite())
return time_;
if (!other.IsFinite())
return other;
SMILTime result(time_ - other.time_);
return result.IsFinite() ? result : Latest();
}
SMILTime operator-() const { return -time_; }
// Division and /modulo are used primarily for computing interval // Division and /modulo are used primarily for computing interval
// progress/repeats. // progress/repeats.
int64_t operator/(SMILTime other) const { int64_t operator/(SMILTime other) const {
return int64_t(time_ / other.time_); DCHECK(IsFinite());
DCHECK(other.IsFinite());
return time_ / other.time_;
} }
SMILTime operator%(SMILTime other) const { SMILTime operator%(SMILTime other) const {
DCHECK(IsFinite()); DCHECK(IsFinite());
DCHECK(other.IsFinite()); DCHECK(other.IsFinite());
return SMILTime(fmod(time_, other.time_)); return SMILTime(time_ % other.time_);
} }
bool operator==(SMILTime other) const { bool operator==(SMILTime other) const { return time_ == other.time_; }
return (IsUnresolved() && other.IsUnresolved()) || time_ == other.time_; explicit operator bool() const { return IsFinite() && !time_.is_zero(); }
} bool operator!=(SMILTime other) const { return time_ != other.time_; }
explicit operator bool() const { return IsFinite() && time_; }
bool operator!=(SMILTime other) const { return !this->operator==(other); } // Ordering of SMILTimes has to follow: finite < indefinite < unresolved. We
// set this up by assigning consecutive sentinel values for the two latter
// Ordering of SMILTimes has to follow: finite < indefinite (Inf) < unresolved // (see above).
// (NaN). The first comparison is handled by IEEE754 but NaN values must be bool operator>(SMILTime other) const { return time_ > other.time_; }
// checked explicitly to guarantee that unresolved is ordered correctly too. bool operator<(SMILTime other) const { return time_ < other.time_; }
bool operator>(SMILTime other) const { bool operator>=(SMILTime other) const { return time_ >= other.time_; }
return IsUnresolved() || time_ > other.time_; bool operator<=(SMILTime other) const { return time_ <= other.time_; }
}
bool operator<(SMILTime other) const { return other.operator>(*this); }
bool operator>=(SMILTime other) const {
return this->operator>(other) || this->operator==(other);
}
bool operator<=(SMILTime other) const {
return this->operator<(other) || this->operator==(other);
}
private: private:
friend bool operator!=(const SMILInterval& a, const SMILInterval& b); friend bool operator!=(const SMILInterval& a, const SMILInterval& b);
friend struct SMILTimeHash; friend struct SMILTimeHash;
constexpr SMILTime(double time) : time_(time) {} constexpr SMILTime(base::TimeDelta time) : time_(time) {}
double time_; base::TimeDelta time_;
}; };
std::ostream& operator<<(std::ostream& os, SMILTime time);
class SMILTimeWithOrigin { class SMILTimeWithOrigin {
DISALLOW_NEW(); DISALLOW_NEW();
...@@ -140,8 +159,6 @@ class SMILTimeWithOrigin { ...@@ -140,8 +159,6 @@ class SMILTimeWithOrigin {
Origin origin_; Origin origin_;
}; };
std::ostream& operator<<(std::ostream& os, SMILTime time);
inline bool operator<(const SMILTimeWithOrigin& a, inline bool operator<(const SMILTimeWithOrigin& a,
const SMILTimeWithOrigin& b) { const SMILTimeWithOrigin& b) {
return a.Time() < b.Time(); return a.Time() < b.Time();
...@@ -174,19 +191,15 @@ struct SMILInterval { ...@@ -174,19 +191,15 @@ struct SMILInterval {
}; };
inline bool operator!=(const SMILInterval& a, const SMILInterval& b) { inline bool operator!=(const SMILInterval& a, const SMILInterval& b) {
// Compare the "raw" values since the operator!= for SMILTime always return return a.begin != b.begin || a.end != b.end;
// true for non-finite times.
return a.begin.time_ != b.begin.time_ || a.end.time_ != b.end.time_;
} }
struct SMILTimeHash { struct SMILTimeHash {
STATIC_ONLY(SMILTimeHash); STATIC_ONLY(SMILTimeHash);
static unsigned GetHash(const SMILTime& key) { static unsigned GetHash(const SMILTime& key) {
return WTF::FloatHash<double>::GetHash(key.time_); return WTF::IntHash<int64_t>::GetHash(key.time_.InMicroseconds());
}
static bool Equal(const SMILTime& a, const SMILTime& b) {
return WTF::FloatHash<double>::Equal(a.time_, b.time_);
} }
static bool Equal(const SMILTime& a, const SMILTime& b) { return a == b; }
static const bool safe_to_compare_to_empty_or_deleted = true; static const bool safe_to_compare_to_empty_or_deleted = true;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment