/*** 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 "timerange.h" #include #include namespace olive { TimeRange::TimeRange(const rational &in, const rational &out) : in_(in), out_(out) { normalize(); } const rational &TimeRange::in() const { return in_; } const rational &TimeRange::out() const { return out_; } const rational &TimeRange::length() const { return length_; } void TimeRange::set_in(const rational &in) { in_ = in; normalize(); } void TimeRange::set_out(const rational &out) { out_ = out; normalize(); } void TimeRange::set_range(const rational &in, const rational &out) { in_ = in; out_ = out; normalize(); } bool TimeRange::operator==(const TimeRange &r) const { return in() == r.in() && out() == r.out(); } bool TimeRange::operator!=(const TimeRange &r) const { return in() != r.in() || out() != r.out(); } bool TimeRange::OverlapsWith(const TimeRange &a, bool in_inclusive, bool out_inclusive) const { bool doesnt_overlap_in = (in_inclusive) ? (a.out() < in()) : (a.out() <= in()); bool doesnt_overlap_out = (out_inclusive) ? (a.in() > out()) : (a.in() >= out()); return !doesnt_overlap_in && !doesnt_overlap_out; } TimeRange TimeRange::Combined(const TimeRange &a) const { return Combine(a, *this); } bool TimeRange::Contains(const TimeRange &compare, bool in_inclusive, bool out_inclusive) const { bool contains_in = (in_inclusive) ? (compare.in() >= in()) : (compare.in() > in()); bool contains_out = (out_inclusive) ? (compare.out() <= out()) : (compare.out() < out()); return contains_in && contains_out; } bool TimeRange::Contains(const rational &r) const { return r >= in_ && r < out_; } TimeRange TimeRange::Combine(const TimeRange &a, const TimeRange &b) { return TimeRange(qMin(a.in(), b.in()), qMax(a.out(), b.out())); } TimeRange TimeRange::Intersected(const TimeRange &a) const { return Intersect(a, *this); } TimeRange TimeRange::Intersect(const TimeRange &a, const TimeRange &b) { return TimeRange(qMax(a.in(), b.in()), qMin(a.out(), b.out())); } TimeRange TimeRange::operator+(const rational &rhs) const { TimeRange answer(*this); answer += rhs; return answer; } TimeRange TimeRange::operator-(const rational &rhs) const { TimeRange answer(*this); answer -= rhs; return answer; } const TimeRange &TimeRange::operator+=(const rational &rhs) { set_range(in_ + rhs, out_ + rhs); return *this; } const TimeRange &TimeRange::operator-=(const rational &rhs) { set_range(in_ - rhs, out_ - rhs); return *this; } std::list TimeRange::Split(const int &chunk_size) const { std::list split_ranges; int start_time = qFloor(this->in().toDouble() / static_cast(chunk_size)) * chunk_size; int end_time = qCeil(this->out().toDouble() / static_cast(chunk_size)) * chunk_size; for (int i=start_time; iin(), rational(i)), qMin(this->out(), rational(i + chunk_size)))); } return split_ranges; } void TimeRange::normalize() { // If `out` is earlier than `in`, swap them if (out_ < in_) { std::swap(out_, in_); } // Calculate length length_ = out_ - in_; } void TimeRangeList::insert(TimeRange range_to_add) { // See if list contains this range if (contains(range_to_add)) { return; } // Does not contain range, so we'll almost certainly be adding it in some way for (int i=0;i= range.out()) { // No intersect continue; } else { // Crop the time range to the range and add it to the list TimeRange cropped(qMax(range.in(), compare.in()), qMin(range.out(), compare.out())); intersect_list.insert(cropped); } } return intersect_list; } uint qHash(const TimeRange &r, uint seed) { return qHash(r.in(), seed) ^ qHash(r.out(), seed); } TimeRangeListFrameIterator::TimeRangeListFrameIterator() : TimeRangeListFrameIterator(TimeRangeList(), rational::NaN) { } TimeRangeListFrameIterator::TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase) : list_(list), timebase_(timebase), index_(-1), size_(-1) { UpdateIndexIfNecessary(); } bool TimeRangeListFrameIterator::GetNext(rational *out) { if (!HasNext()) { return false; } // Output current value *out = current_; // Determine next value by adding timebase current_ += timebase_; // If this time is outside the current range, jump to the next one UpdateIndexIfNecessary(); return true; } bool TimeRangeListFrameIterator::HasNext() const { return index_ < list_.size(); } int TimeRangeListFrameIterator::size() { if (size_ == -1) { // Size isn't calculated automatically for optimization, so we'll calculate it now size_ = 0; foreach (const TimeRange &range, list_) { rational start = Timecode::snap_time_to_timebase(range.in(), timebase_, Timecode::kCeil); rational end = Timecode::snap_time_to_timebase(range.out(), timebase_, Timecode::kFloor); if (end == range.out()) { end -= timebase_; } int64_t start_ts = Timecode::time_to_timestamp(start, timebase_); int64_t end_ts = Timecode::time_to_timestamp(end, timebase_); size_ += 1 + (end_ts - start_ts); } } return size_; } void TimeRangeListFrameIterator::UpdateIndexIfNecessary() { while (index_ < list_.size() && (index_ == -1 || current_ >= list_.at(index_).out())) { index_++; if (index_ < list_.size()) { current_ = Timecode::snap_time_to_timebase(list_.at(index_).in(), timebase_, Timecode::kCeil); } } } } QDebug operator<<(QDebug debug, const olive::TimeRange &r) { debug.nospace() << r.in().toDouble() << " - " << r.out().toDouble(); return debug.space(); } QDebug operator<<(QDebug debug, const olive::TimeRangeList &r) { debug << r.internal_array(); return debug.space(); }