diff --git a/app/common/rational.cpp b/app/common/rational.cpp index d7d374230..2efad3f1f 100644 --- a/app/common/rational.cpp +++ b/app/common/rational.cpp @@ -1,5 +1,22 @@ -//Copyright 2015 Adam Quintero -//This program is distributed under the terms of the GNU General Public License. +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ #include "rational.h" @@ -16,7 +33,7 @@ rational rational::fromDouble(const double &flt, bool* ok) } // Use FFmpeg function for the time being - AVRational r = av_d2q(flt, 65535); + AVRational r = av_d2q(flt, INT_MAX); if (r.den == 0) { // If den == 0, we were unable to convert to a rational @@ -39,9 +56,9 @@ rational rational::fromString(const QString &str, bool* ok) switch (elements.size()) { case 1: - return rational(elements.first().toLongLong(ok)); + return rational(elements.first().toInt(ok)); case 2: - return rational(elements.at(0).toLongLong(ok), elements.at(1).toLongLong(ok)); + return rational(elements.at(0).toInt(ok), elements.at(1).toInt(ok)); default: // Returns NaN with ok set to false if (ok) { @@ -51,63 +68,12 @@ rational rational::fromString(const QString &str, bool* ok) } } -//Function: ensures denom >= 0 - -void rational::fix_signs() -{ - // Normalize so that denominator is always positive and only numerator is positive - if (denom_ < 0) { - denom_ = -denom_; - numer_ = -numer_; - } else if (denom_ == intType(0)) { - // Normalize to 0/0 (aka NaN) if denominator is zero - numer_ = intType(0); - } else if (numer_ == intType(0)) { - // Normalize to 0/1 if numerator is zero - denom_ = intType(1); - } -} - -//Function: ensures lowest form - -void rational::reduce() -{ - if (!isNull() && denom_ != 1) { - // Euclidean often fails if numbers are negative, we abs it and re-neg it later if necessary - bool neg = numer_ < 0; - - numer_ = qAbs(numer_); - - intType d = gcd(numer_, denom_); - - if (d > 1) { - numer_ /= d; - denom_ /= d; - } - - if (neg) { - numer_ = -numer_; - } - } -} - -//Function: finds greatest common denominator - -intType rational::gcd(const intType &x, const intType &y) -{ - if (y == 0) { - return x; - } else { - return gcd(y, x % y); - } -} - //Function: convert to double double rational::toDouble() const { - if (denom_ != 0) { - return static_cast(numer_) / static_cast(denom_); + if (r_.den != 0) { + return av_q2d(r_); } else { return qSNaN(); } @@ -115,12 +81,7 @@ double rational::toDouble() const AVRational rational::toAVRational() const { - AVRational r; - - r.num = static_cast(numer_); - r.den = static_cast(denom_); - - return r; + return r_; } #ifdef USE_OTIO @@ -143,44 +104,41 @@ rational rational::flipped() const void rational::flip() { if (!isNull()) { - std::swap(denom_, numer_); + std::swap(r_.den, r_.num); + FixSigns(); } } -bool rational::isNull() const -{ - return numerator() == 0; -} - -bool rational::isNaN() const -{ - return denominator() == 0; -} - -const intType &rational::numerator() const -{ - return numer_; -} - -const intType &rational::denominator() const -{ - return denom_; -} - QString rational::toString() const { - return QStringLiteral("%1/%2").arg(QString::number(numer_), QString::number(denom_)); + return QStringLiteral("%1/%2").arg(QString::number(r_.num), QString::number(r_.den)); +} + +void rational::FixSigns() +{ + if (r_.den < 0) { + // Normalize so that denominator is always positive + r_.den = -r_.den; + r_.num = -r_.num; + } else if (r_.den == 0) { + // Normalize to 0/0 (aka NaN) if denominator is zero + r_.num = 0; + } else if (r_.num == 0) { + // Normalize to 0/1 if numerator is zero + r_.den = 1; + } +} + +void rational::Reduce() +{ + av_reduce(&r_.num, &r_.den, r_.num, r_.den, INT_MAX); } //Assignment Operators const rational& rational::operator=(const rational &rhs) { - if (this != &rhs) { - numer_ = rhs.numer_; - denom_ = rhs.denom_; - } - + r_ = rhs.r_; return *this; } @@ -190,19 +148,10 @@ const rational& rational::operator+=(const rational &rhs) if (!isNaN()) { if (rhs.isNaN()) { - // Set to NaN - denom_ = 0; - fix_signs(); - } else if (!rhs.isNull()) { - if (isNull()) { - numer_ = rhs.numer_; - denom_ = rhs.denom_; - } else { - numer_ = (numer_ * rhs.denom_) + (rhs.numer_ * denom_); - denom_ = denom_ * rhs.denom_; - fix_signs(); - reduce(); - } + *this = NaN; + } else { + r_ = av_add_q(r_, rhs.r_); + FixSigns(); } } @@ -215,39 +164,10 @@ const rational& rational::operator-=(const rational &rhs) if (!isNaN()) { if (rhs.isNaN()) { - // Set to NaN - denom_ = 0; - fix_signs(); - } else if (!rhs.isNull()) { - if (isNull()) { - numer_ = -rhs.numer_; - denom_ = rhs.denom_; - } else { - numer_ = (numer_ * rhs.denom_) - (rhs.numer_ * denom_); - denom_ = denom_ * rhs.denom_; - fix_signs(); - reduce(); - } - } - } - - return *this; -} - -const rational& rational::operator/=(const rational &rhs) -{ - Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX); - - if (!isNaN()) { - if (rhs.isNaN()) { - // Set to NaN - denom_ = 0; - fix_signs(); + *this = NaN; } else { - numer_ = numer_ * rhs.denom_; - denom_ = denom_ * rhs.numer_; - fix_signs(); - reduce(); + r_ = av_sub_q(r_, rhs.r_); + FixSigns(); } } @@ -260,13 +180,26 @@ const rational& rational::operator*=(const rational &rhs) if (!isNaN()) { if (rhs.isNaN()) { - denom_ = 0; - fix_signs(); + *this = NaN; } else { - numer_ = numer_ * rhs.numer_; - denom_ = denom_ * rhs.denom_; - fix_signs(); - reduce(); + r_ = av_mul_q(r_, rhs.r_); + FixSigns(); + } + } + + return *this; +} + +const rational& rational::operator/=(const rational &rhs) +{ + Q_ASSERT(*this != RATIONAL_MIN && *this != RATIONAL_MAX && rhs != RATIONAL_MIN && rhs != RATIONAL_MAX); + + if (!isNaN()) { + if (rhs.isNaN()) { + *this = NaN; + } else { + r_ = av_div_q(r_, rhs.r_); + FixSigns(); } } @@ -307,87 +240,29 @@ rational rational::operator*(const rational &rhs) const bool rational::operator<(const rational &rhs) const { - if (isNaN() || rhs.isNaN()) { - return false; - } - - if (isNull() && rhs.isNull()) { - return false; - } - - if (rhs == RATIONAL_MAX - || *this == RATIONAL_MIN) { - // We will always either be LESS THAN (true) or EQUAL (false) - return (*this != rhs); - } - - if (*this == RATIONAL_MAX - || rhs == RATIONAL_MIN) { - // We will always be GREATER THAN (false) or EQUAL (false) - return false; - } - - if (!isNull() && rhs.isNull()) { - return (numer_ * denom_ < intType(0)); - } - - if (isNull() && !rhs.isNull()) { - return !(rhs.numer_ * rhs.denom_ < intType(0)); - } - - return ((numer_ * rhs.denom_) < (denom_ * rhs.numer_)); + return av_cmp_q(r_, rhs.r_) == -1; } bool rational::operator<=(const rational &rhs) const { - if (isNaN() || rhs.isNaN()) { - return false; - } - - if (isNull() && rhs.isNull()) { - return true; - } - - if (rhs == RATIONAL_MAX - || *this == RATIONAL_MIN) { - // We will always either be LESS THAN (true) or EQUAL (true) - return true; - } - - if (*this == RATIONAL_MAX - || rhs == RATIONAL_MIN) { - // We will always be GREATER THAN (false) or EQUAL (true) - return rhs == *this; - } - - if (!isNull() && rhs.isNull()) { - return (numer_ * denom_ < intType(0)); - } - - if (isNull() && !rhs.isNull()) { - return !(rhs.numer_ * rhs.denom_ < intType(0)); - } - - return ((numer_ * rhs.denom_) <= (denom_ * rhs.numer_)); + int cmp = av_cmp_q(r_, rhs.r_); + return cmp == 0 || cmp == -1; } bool rational::operator>(const rational &rhs) const { - return rhs < *this; + return av_cmp_q(r_, rhs.r_) == 1; } bool rational::operator>=(const rational &rhs) const { - return rhs <= *this; + int cmp = av_cmp_q(r_, rhs.r_); + return cmp == 0 || cmp == 1; } bool rational::operator==(const rational &rhs) const { - if (isNaN() || rhs.isNaN()) { - return false; - } - - return (numer_ == rhs.numer_ && denom_ == rhs.denom_); + return av_cmp_q(r_, rhs.r_) == 0; } bool rational::operator!=(const rational &rhs) const @@ -395,55 +270,6 @@ bool rational::operator!=(const rational &rhs) const return !(*this == rhs); } -const rational& rational::operator+() const -{ - return *this; -} - -rational rational::operator-() const -{ - return rational(numer_, -denom_); -} - -bool rational::operator!() const -{ - return !numer_; -} - -//IO - -std::ostream& operator<<(std::ostream &out, const rational &value) -{ - out << value.numer_; - - if (value.denom_ != 1) { - out << '/' << value.denom_; - return out; - } - - return out; -} - -std::istream& operator>>(std::istream &in, rational &value) -{ - in >> value.numer_; - value.denom_ = 1; - - char ch; - in.get(ch); - - if(!in.eof()) { - if(ch == '/') { - in >> value.denom_; - value.fix_signs(); - value.reduce(); - } else { - in.putback(ch); - } - } - return in; -} - uint qHash(const rational &r, uint seed) { return ::qHash(r.toDouble(), seed); diff --git a/app/common/rational.h b/app/common/rational.h index 26f625fc6..73c839c81 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -1,60 +1,66 @@ -//Copyright 2015 Adam Quintero -//This program is distributed under the terms of the GNU General Public License. +/*** -// Adapted by MattKC for the Olive Video Editor (2019-2022) + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ #ifndef RATIONAL_H #define RATIONAL_H -#include + +extern "C" { +#include +} + +#include +#include #ifdef USE_OTIO #include #endif -#include -#include - -extern "C" { -#include -} - #include "common/define.h" namespace olive { -typedef int64_t intType; -/* - * Zero Handling - * 0/0 = 0 - * 0/non-zero = 0 - * non-zero/0 = 0 -*/ class rational { public: - //constructors - rational(const intType &numerator = 0) : - numer_(numerator), - denom_(1) + rational(const int &numerator = 0) { + r_.num = numerator; + r_.den = 1; } - rational(const intType &numerator, const intType &denominator) : - numer_(numerator), - denom_(denominator) + rational(const int &numerator, const int &denominator) { - fix_signs(); - reduce(); + r_.num = numerator; + r_.den = denominator; + + FixSigns(); + Reduce(); } rational(const rational &rhs) = default; - rational(const AVRational& r) : - numer_(r.num), - denom_(r.den) + rational(const AVRational& r) { - fix_signs(); - reduce(); + r_ = r; + + FixSigns(); } static rational fromDouble(const double& flt, bool *ok = nullptr); @@ -84,9 +90,9 @@ public: bool operator!=(const rational &rhs) const; //Unary operators - const rational& operator+() const; - rational operator-() const; - bool operator!() const; + const rational& operator+() const { return *this; } + rational operator-() const { return rational(r_.num, -r_.den); } + bool operator!() const { return !r_.num; } //Function: convert to double double toDouble() const; @@ -100,7 +106,7 @@ public: return fromDouble(t.to_seconds()); } - // Convert Olive ratioanls to opentime rationals with the given framerate (defaults to 24) + // Convert Olive rationals to opentime rationals with the given framerate (defaults to 24) opentime::RationalTime toRationalTime(double framerate = 24) const; #endif @@ -111,35 +117,26 @@ public: // Returns whether the rational is valid but equal to zero or not // // A NaN is always a null, but a null is not always a NaN - bool isNull() const; + bool isNull() const { return r_.num == 0; } - // Returns whether this rational is not a valid number - bool isNaN() const; + // Returns whether this rational is not a valid number (denominator == 0) + bool isNaN() const { return r_.den == 0; } - //IO - friend std::ostream& operator<<(std::ostream &out, const rational &value); - friend std::istream& operator>>(std::istream &in, rational &value); - - const intType& numerator() const; - const intType& denominator() const; + const int& numerator() const { return r_.num; } + const int& denominator() const { return r_.den; } QString toString() const; private: - //numerator and denominator - intType numer_; - intType denom_; + void FixSigns(); + void Reduce(); + + AVRational r_; - //Function: ensures denom >= 0 - void fix_signs(); - //Function: ensures lowest form - void reduce(); - //Function: finds greatest common denominator - static intType gcd(const intType &x, const intType &y); }; -#define RATIONAL_MIN rational(INT64_MIN, 1) -#define RATIONAL_MAX rational(INT64_MAX, 1) +#define RATIONAL_MIN rational(INT_MIN) +#define RATIONAL_MAX rational(INT_MAX) uint qHash(const rational& r, uint seed = 0);