diff --git a/libs/image/kis_time_range.cpp b/libs/image/kis_time_range.cpp index d8afb9455b..82411435e0 100644 --- a/libs/image/kis_time_range.cpp +++ b/libs/image/kis_time_range.cpp @@ -1,376 +1,331 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_time_range.h" #include #include "kis_keyframe_channel.h" #include "kis_node.h" #include "kis_layer_utils.h" #include "kis_dom_utils.h" -struct KisTimeRangeStaticRegistrar { - KisTimeRangeStaticRegistrar() { - qRegisterMetaType("KisTimeRange"); +struct KisTimeTypesStaticRegistrar { + KisTimeTypesStaticRegistrar() { qRegisterMetaType("KisTimeSpan"); qRegisterMetaType("KisFrameSet"); } }; -static KisTimeRangeStaticRegistrar __registrar; - -QDebug operator<<(QDebug dbg, const KisTimeRange &r) -{ - dbg.nospace() << "KisTimeRange(" << r.start() << ", " << r.end() << ")"; - - return dbg.space(); -} +static KisTimeTypesStaticRegistrar __registrar; QDebug operator<<(QDebug dbg, const KisTimeSpan &r) { dbg.nospace() << "KisTimeSpan(" << r.start() << ", " << r.end() << ")"; return dbg.space(); } QDebug operator<<(QDebug dbg, const KisFrameSet &r) { const QVector &spans = r.finiteSpans(); dbg.nospace() << "KisFrameSet("; for (int i = 0; i < spans.size(); i++) { if (i > 0) dbg.nospace() << ", "; dbg.nospace() << spans[i].start() << ".." << spans[i].end(); } if (r.isInfinite()) dbg.nospace() << ", " << r.firstFrameOfInfinity() << "..."; dbg.nospace() << ")"; return dbg.space(); } KisFrameSet& KisFrameSet::operator|=(const KisFrameSet &rhs) { if (rhs.isEmpty()) return *this; if (isEmpty()) { *this = rhs; return *this; } QVector spans; int lIndex = 0, rIndex = 0; KisTimeSpan currentSpan; int firstOfInfinite = !isInfinite() ? rhs.m_firstFrameOfInfinity : (!rhs.isInfinite()) ? m_firstFrameOfInfinity : qMin(m_firstFrameOfInfinity, rhs.m_firstFrameOfInfinity); while (lIndex < m_spans.size() || rIndex < rhs.m_spans.size()) { const bool leftRemaining = (lIndex < m_spans.size()); const bool rightRemaining = (rIndex < rhs.m_spans.size()); const bool leftFirst = leftRemaining && (!rightRemaining || m_spans[lIndex].start() < rhs.m_spans[rIndex].start()); KisTimeSpan first; if (leftFirst) { first = m_spans[lIndex++]; } else { first = rhs.m_spans[rIndex++]; } if (isInfinite() && firstOfInfinite <= first.end()) { currentSpan = KisTimeSpan(); firstOfInfinite = qMin(first.start(), firstOfInfinite); break; } else if (first.start() <= currentSpan.end() || currentSpan.isEmpty()) { currentSpan = currentSpan | first; } else { spans.append(currentSpan); currentSpan = first; } } if (!currentSpan.isEmpty()) { spans.append(currentSpan); } m_spans = spans; m_firstFrameOfInfinity = firstOfInfinite; return *this; } void addIntersectionAgainstInfinity(const QVector &src, int firstIndex , QVector &dst, int firstFrameOfInfinity) { for (int index = firstIndex; index < src.size(); index++) { const KisTimeSpan span = src[index].truncateRight(firstFrameOfInfinity); if (!span.isEmpty()) dst.append(span); } } KisFrameSet& KisFrameSet::operator&=(const KisFrameSet &rhs) { if (isEmpty() || rhs.isEmpty()) { *this = KisFrameSet(); return *this; } QVector spans; int lIndex = 0, rIndex = 0; while (lIndex < m_spans.size() && rIndex < rhs.m_spans.size()) { KisTimeSpan span; const KisTimeSpan rSpan = rhs.m_spans[rIndex]; const KisTimeSpan lSpan = m_spans[lIndex]; span = lSpan & rSpan; if (!span.isEmpty()) { spans.append(span); } if (lSpan.start() < rSpan.start()) { lIndex++; } else { rIndex++; } } if (isInfinite()) addIntersectionAgainstInfinity(rhs.m_spans, rIndex, spans, m_firstFrameOfInfinity); if (rhs.isInfinite()) addIntersectionAgainstInfinity(m_spans, lIndex, spans, rhs.m_firstFrameOfInfinity); int firstOfInfinite = (isInfinite() && rhs.isInfinite()) ? qMax(m_firstFrameOfInfinity, rhs.m_firstFrameOfInfinity) : -1; m_spans = spans; m_firstFrameOfInfinity = firstOfInfinite; return *this; } KisFrameSet& KisFrameSet::operator-=(const KisFrameSet &rhs) { if (rhs.isEmpty()) return *this; if (isEmpty()) { *this = KisFrameSet(); return *this; } QVector spans; int firstOfInfinite = (isInfinite() && !rhs.isInfinite()) ? qMax(m_firstFrameOfInfinity, rhs.m_spans.last().end() + 1) : -1; KisTimeSpan currentSpan = m_spans.first(); int lIndex = 0, rIndex = 0; while (lIndex < m_spans.size() && rIndex < rhs.m_spans.size()) { const KisTimeSpan rSpan = rhs.m_spans[rIndex]; if (currentSpan.isEmpty() || currentSpan.end() < rSpan.start()) { if (!currentSpan.isEmpty()) { spans.append(currentSpan); } lIndex++; currentSpan = (lIndex < m_spans.size()) ? m_spans[lIndex] : KisTimeSpan(); } else { const KisTimeSpan tail = currentSpan.truncateRight(rSpan.end() + 1); const KisTimeSpan head = currentSpan.truncateLeft(rSpan.start() - 1); if (!head.isEmpty()) { spans.append(head); } currentSpan = tail; rIndex++; } } while (!currentSpan.isEmpty()) { if (rhs.isInfinite() && currentSpan.end() >= rhs.firstFrameOfInfinity()) { currentSpan = currentSpan.truncateLeft(rhs.firstFrameOfInfinity() - 1); if (!currentSpan.isEmpty()) spans.append(currentSpan); break; } spans.append(currentSpan); lIndex++; currentSpan = (lIndex < m_spans.size()) ? m_spans[lIndex] : KisTimeSpan(); } m_spans = spans; m_firstFrameOfInfinity = firstOfInfinite; return *this; } KisFrameSet calculateIdenticalFramesRecursive(const KisNode *node, int time) { KisFrameSet frames = KisFrameSet::infiniteFrom(0); KisLayerUtils::recursiveApplyNodes(node, [&frames, time] (const KisNode *node) { if (node->visible()) { frames &= calculateNodeIdenticalFrames(node, time); } }); return frames; } KisFrameSet calculateAffectedFramesRecursive(const KisNode *node, int time) { KisFrameSet frames; KisLayerUtils::recursiveApplyNodes(node, [&frames, time] (const KisNode *node) { if (node->visible()) { frames |= calculateNodeAffectedFrames(node, time); } }); return frames; } int KisFrameSet::firstExcludedSince(int time) const { if (isEmpty()) return time; if (0 <= m_firstFrameOfInfinity && m_firstFrameOfInfinity <= time) return -1; if (time < start()) return time; if (time > m_spans.last().end()) return time; Q_FOREACH(const KisTimeSpan &span, m_spans) { if (span.start() > time) return time; if (span.end() >= time) return span.end() + 1; } KIS_SAFE_ASSERT_RECOVER_NOOP(false); return -1; } KisFrameSet calculateNodeIdenticalFrames(const KisNode *node, int time) { KisFrameSet range = KisFrameSet::infiniteFrom(0); const QMap channels = node->keyframeChannels(); Q_FOREACH (const KisKeyframeChannel *channel, channels) { // Intersection range &= channel->identicalFrames(time); } return range; } KisFrameSet calculateNodeAffectedFrames(const KisNode *node, int time) { KisFrameSet range; if (!node->visible()) return range; const QMap channels = node->keyframeChannels(); // TODO: channels should report to the image which channel exactly has changed // to avoid the dirty range to be stretched into infinity! if (channels.isEmpty() || !channels.contains(KisKeyframeChannel::Content.id())) { range = KisFrameSet::infiniteFrom(0); return range; } Q_FOREACH (const KisKeyframeChannel *channel, channels) { // Union range |= channel->affectedFrames(time); } return range; } namespace KisDomUtils { -void saveValue(QDomElement *parent, const QString &tag, const KisTimeRange &range) -{ - QDomDocument doc = parent->ownerDocument(); - QDomElement e = doc.createElement(tag); - parent->appendChild(e); - - e.setAttribute("type", "timerange"); - - if (range.isValid()) { - e.setAttribute("from", toString(range.start())); - - if (!range.isInfinite()) { - e.setAttribute("to", toString(range.end())); - } - } -} - -bool loadValue(const QDomElement &parent, const QString &tag, KisTimeRange *range) -{ - QDomElement e; - if (!findOnlyElement(parent, tag, &e)) return false; - - if (!Private::checkType(e, "timerange")) return false; - - int start = toInt(e.attribute("from", "-1")); - int end = toInt(e.attribute("to", "-1")); - - if (start == -1) { - range = new KisTimeRange(); - } else if (end == -1) { - *range = KisTimeRange::infinite(start); - } else { - *range = KisTimeRange::fromTime(start, end); - } - return true; -} - void saveValue(QDomElement *parent, const QString &tag, const KisTimeSpan &range) { QDomDocument doc = parent->ownerDocument(); QDomElement e = doc.createElement(tag); parent->appendChild(e); e.setAttribute("type", "timerange"); if (!range.isEmpty()) { e.setAttribute("from", toString(range.start())); e.setAttribute("to", toString(range.end())); } } bool loadValue(const QDomElement &parent, const QString &tag, KisTimeSpan *range) { QDomElement e; if (!findOnlyElement(parent, tag, &e)) return false; if (!Private::checkType(e, "timerange")) return false; int start = toInt(e.attribute("from", "-1")); int end = toInt(e.attribute("to", "-1")); if (start < 0 || end < 0) { *range = KisTimeSpan(); } else { *range = KisTimeSpan(start, end); } return true; } } diff --git a/libs/image/kis_time_range.h b/libs/image/kis_time_range.h index 60b8954691..d84e07dbe1 100644 --- a/libs/image/kis_time_range.h +++ b/libs/image/kis_time_range.h @@ -1,353 +1,238 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TIME_RANGE_H #define __KIS_TIME_RANGE_H #include "kritaimage_export.h" #include #include #include #include #include "kis_types.h" class QDomElement; /** * Represents a finite, continuous span of time between two frames. * Start and end frames are both included in the span. */ class KRITAIMAGE_EXPORT KisTimeSpan : public boost::equality_comparable { public: inline KisTimeSpan() : m_start(-1) , m_end(-2) {} inline KisTimeSpan(int start, int end) : m_start(start), m_end(end) {} inline bool isEmpty() const { return m_end < m_start; } inline int start() const { return m_start; } inline int end() const { return m_end; } inline bool isValid() const { return isEmpty(); } inline int duration() const { return !isEmpty() ? (m_end - m_start + 1) : 0; } inline bool contains(int time) const { return !isEmpty() ? (m_start <= time && time <= m_end) : false; } inline bool contains(const KisTimeSpan rhs) const { return rhs.isEmpty() || (m_start <= rhs.start() && rhs.end() <= m_end); } inline bool overlaps(const KisTimeSpan &other) const { return m_start <= other.m_end && other.m_start <= m_end; } inline KisTimeSpan truncateLeft(int newEnd) const { if (newEnd < m_start) return KisTimeSpan(); if (m_end <= newEnd) return *this; return KisTimeSpan(m_start, newEnd); } inline KisTimeSpan truncateRight(int newStart) const { if (m_end < newStart) return KisTimeSpan(); if (newStart <= m_start) return *this; return KisTimeSpan(newStart, m_end); } bool operator==(const KisTimeSpan &rhs) const { return rhs.m_start == m_start && rhs.m_end == m_end; } KisTimeSpan operator|(const KisTimeSpan &rhs) const { if (isEmpty()) return rhs; if (rhs.isEmpty()) return *this; int start = qMin(m_start, rhs.m_start); int end = qMax(m_end, rhs.m_end); return KisTimeSpan(start, end); } KisTimeSpan operator&(const KisTimeSpan &rhs) const { if (isEmpty() || rhs.isEmpty()) return KisTimeSpan(); int start = qMax(m_start, rhs.m_start); int end = qMin(m_end, rhs.m_end); if (end < start) return KisTimeSpan(); return KisTimeSpan(start, end); } private: int m_start, m_end; }; /** * Represents an arbitrary set of frames, possibly stretching to infinity. * */ class KRITAIMAGE_EXPORT KisFrameSet : public boost::equality_comparable, public boost::andable, public boost::orable, public boost::subtractable { public: static KisFrameSet between(int start, int end) { return KisFrameSet(KisTimeSpan(start, end)); } static KisFrameSet infiniteFrom(int start) { return KisFrameSet({}, start); } KisFrameSet() = default; inline explicit KisFrameSet(QVector spans, int firstFrameOfInfinity = -1) : m_spans(spans) , m_firstFrameOfInfinity(firstFrameOfInfinity) { // Normalize std::sort(m_spans.begin(), m_spans.end(), [](const KisTimeSpan &a, const KisTimeSpan &b) { return a.start() < b.start(); } ); mergeOverlappingSpans(); } explicit KisFrameSet(const KisTimeSpan span) : m_spans({span}) {} inline bool isInfinite() const { return m_firstFrameOfInfinity >= 0; } bool isEmpty() const { return m_spans.isEmpty() && !isInfinite(); } int start() const { if (m_spans.isEmpty()) return m_firstFrameOfInfinity; return m_spans.first().start(); } /** * List of the finite, continuous spans this set consists of. * Note: this list does not contain the tail of infinite sets. See firstFrameOfInfinity(). */ inline const QVector finiteSpans() const { return m_spans; } /** * If the set is infinite, the frame from which the infinite tail begins. Returns -1 if the set is finite. */ inline int firstFrameOfInfinity() const { return m_firstFrameOfInfinity; } inline bool contains(int frame) const { if (0 <= m_firstFrameOfInfinity && m_firstFrameOfInfinity <= frame) return true; Q_FOREACH(const KisTimeSpan &span, m_spans) { if (span.contains(frame)) return true; } return false; } int firstExcludedSince(int time) const; bool operator==(const KisFrameSet &rhs) const { return rhs.m_firstFrameOfInfinity == m_firstFrameOfInfinity && rhs.m_spans == m_spans; } KisFrameSet& operator|=(const KisFrameSet &rhs); KisFrameSet& operator&=(const KisFrameSet &rhs); KisFrameSet& operator-=(const KisFrameSet &rhs); private: inline void mergeOverlappingSpans() { int dst = 0; for (int src = 1; src < m_spans.length(); src++) { if (isInfinite() && m_firstFrameOfInfinity <= m_spans[src].end()) { m_firstFrameOfInfinity = qMin(m_spans[src].start(), m_firstFrameOfInfinity); break; } if (m_spans[src].overlaps(m_spans[dst])) { m_spans[dst] = m_spans[dst] | m_spans[src]; } else { dst++; if (dst != src) { m_spans[dst] = m_spans[src]; } } } if (dst < m_spans.length() - 1) { m_spans.resize(dst - 1); } } QVector m_spans; int m_firstFrameOfInfinity = -1; }; -class KRITAIMAGE_EXPORT KisTimeRange : public boost::equality_comparable -{ -public: - inline KisTimeRange() - : m_start(0), - m_end(-1) - { - } - - inline KisTimeRange(int start, int duration) - : m_start(start), - m_end(start + duration - 1) - { - } - - inline KisTimeRange(int start, int end, bool) - : m_start(start), - m_end(end) - { - } - - bool operator==(const KisTimeRange &rhs) const { - return rhs.m_start == m_start && rhs.m_end == m_end; - } - - KisTimeRange& operator|=(const KisTimeRange &rhs) { - if (!isValid()) { - m_start = rhs.start(); - } else if (rhs.isValid()) { - m_start = std::min(m_start, rhs.start()); - } - - if (rhs.isInfinite() || isInfinite()) { - m_end = std::numeric_limits::min(); - } else if (!isValid()) { - m_end = rhs.m_end; - } else { - m_end = std::max(m_end, rhs.m_end); - } - - return *this; - } - - KisTimeRange& operator&=(const KisTimeRange &rhs) { - if (!isValid()) { - return *this; - } else if (!rhs.isValid()) { - m_start = rhs.start(); - m_end = rhs.m_end; - return *this; - } else { - m_start = std::max(m_start, rhs.start()); - } - - if (isInfinite()) { - m_end = rhs.m_end; - } else if (!rhs.isInfinite()) { - m_end = std::min(m_end, rhs.m_end); - } - - return *this; - } - - inline int start() const { - return m_start; - } - - inline int end() const { - return m_end; - } - - inline int duration() const { - return m_end >= m_start ? m_end - m_start + 1 : 0; - } - - inline bool isInfinite() const { - return m_end == std::numeric_limits::min(); - } - - inline bool isValid() const { - return (m_end >= m_start) || (m_end == std::numeric_limits::min() && m_start >= 0); - } - - inline bool contains(int time) const { - if (m_end == std::numeric_limits::min()) { - return m_start <= time; - } - - return m_start <= time && time <= m_end; - } - - // Temporary adapter - inline KisFrameSet asFrameSet() const { - if (isInfinite()) return KisFrameSet::infiniteFrom(m_start); - return KisFrameSet::between(m_start, m_end); - } - - static inline KisTimeRange fromTime(int start, int end) { - return KisTimeRange(start, end, true); - } - - static inline KisTimeRange infinite(int start) { - return KisTimeRange(start, std::numeric_limits::min(), true); - } - -private: - int m_start; - int m_end; -}; - KRITAIMAGE_EXPORT KisFrameSet calculateIdenticalFramesRecursive(const KisNode *node, int time); KRITAIMAGE_EXPORT KisFrameSet calculateAffectedFramesRecursive(const KisNode *node, int time); KRITAIMAGE_EXPORT KisFrameSet calculateNodeIdenticalFrames(const KisNode *node, int time); KRITAIMAGE_EXPORT KisFrameSet calculateNodeAffectedFrames(const KisNode *node, int time); namespace KisDomUtils { - void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const KisTimeRange &range); - bool KRITAIMAGE_EXPORT loadValue(const QDomElement &parent, const QString &tag, KisTimeRange *range); - void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const KisTimeSpan &range); bool KRITAIMAGE_EXPORT loadValue(const QDomElement &parent, const QString &tag, KisTimeSpan *range); } -Q_DECLARE_METATYPE(KisTimeRange); Q_DECLARE_METATYPE(KisTimeSpan); Q_DECLARE_METATYPE(KisFrameSet); -KRITAIMAGE_EXPORT QDebug operator<<(QDebug dbg, const KisTimeRange &r); KRITAIMAGE_EXPORT QDebug operator<<(QDebug dbg, const KisTimeSpan &r); KRITAIMAGE_EXPORT QDebug operator<<(QDebug dbg, const KisFrameSet &r); #endif /* __KIS_TIME_RANGE_H */