diff --git a/libs/image/kis_time_range.cpp b/libs/image/kis_time_range.cpp index 363e970bc4..d89752684a 100644 --- a/libs/image/kis_time_range.cpp +++ b/libs/image/kis_time_range.cpp @@ -1,144 +1,354 @@ /* * 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" struct KisTimeRangeStaticRegistrar { KisTimeRangeStaticRegistrar() { - qRegisterMetaType("KisTimeRange"); + qRegisterMetaType("KisTimeRange"); + qRegisterMetaType("KisTimeSpan"); + qRegisterMetaType("KisFrameSet"); } }; static KisTimeRangeStaticRegistrar __registrar; QDebug operator<<(QDebug dbg, const KisTimeRange &r) { dbg.nospace() << "KisTimeRange(" << r.start() << ", " << r.end() << ")"; return dbg.space(); } +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; +} + void KisTimeRange::calculateTimeRangeRecursive(const KisNode *node, int time, KisTimeRange &range, bool exclusive) { if (!node->visible()) return; if (exclusive) { // Intersection range &= calculateNodeIdenticalFrames(node, time); } else { // Union range |= calculateNodeAffectedFrames(node, time); } KisNodeSP child = node->firstChild(); while (child) { calculateTimeRangeRecursive(child, time, range, exclusive); child = child->nextSibling(); } } KisTimeRange KisTimeRange::calculateNodeIdenticalFrames(const KisNode *node, int time) { KisTimeRange range = KisTimeRange::infinite(0); const QMap channels = node->keyframeChannels(); if (channels.isEmpty() || !channels.contains(KisKeyframeChannel::Content.id())) { return range; } Q_FOREACH (const KisKeyframeChannel *channel, channels) { // Intersection range &= channel->identicalFrames(time); } return range; } KisTimeRange KisTimeRange::calculateNodeAffectedFrames(const KisNode *node, int time) { KisTimeRange range; if (!node->visible()) return range; const QMap channels = node->keyframeChannels(); if (channels.isEmpty() || !channels.contains(KisKeyframeChannel::Content.id())) { range = KisTimeRange::infinite(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 a6af4eab50..a468c82364 100644 --- a/libs/image/kis_time_range.h +++ b/libs/image/kis_time_range.h @@ -1,152 +1,343 @@ /* * 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" #include +/** + * 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; + } + + 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; } 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); } static void calculateTimeRangeRecursive(const KisNode *node, int time, KisTimeRange &range, bool exclusive); static KisTimeRange calculateNodeIdenticalFrames(const KisNode *node, int time); static KisTimeRange calculateNodeAffectedFrames(const KisNode *node, int time); private: int m_start; int m_end; }; 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 */ diff --git a/libs/image/tests/CMakeLists.txt b/libs/image/tests/CMakeLists.txt index aff2df122c..fddccf7090 100644 --- a/libs/image/tests/CMakeLists.txt +++ b/libs/image/tests/CMakeLists.txt @@ -1,244 +1,245 @@ # cmake in some versions for some not yet known reasons fails to run automoc # on random targets (changing target names already has an effect) # As temporary workaround skipping build of tests on these versions for now # See https://mail.kde.org/pipermail/kde-buildsystem/2015-June/010819.html # extend range of affected cmake versions as needed if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1.3 AND NOT ${CMAKE_VERSION} VERSION_GREATER 3.2.3) message(WARNING "Skipping krita/image/tests, CMake in at least versions 3.1.3 - 3.2.3 seems to have a problem with automoc. \n(FRIENDLY REMINDER: PLEASE DON'T BREAK THE TESTS!)") set (HAVE_FAILING_CMAKE TRUE) else() set (HAVE_FAILING_CMAKE FALSE) endif() include_directories( ${CMAKE_SOURCE_DIR}/libs/image/metadata ${CMAKE_BINARY_DIR}/libs/image/ ${CMAKE_SOURCE_DIR}/libs/image/ ${CMAKE_SOURCE_DIR}/libs/image/brushengine ${CMAKE_SOURCE_DIR}/libs/image/tiles3 ${CMAKE_SOURCE_DIR}/libs/image/tiles3/swap ${CMAKE_SOURCE_DIR}/sdk/tests ) include_Directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(HAVE_VC) include_directories(${Vc_INCLUDE_DIR}) endif() include(ECMAddTests) include(KritaAddBrokenUnitTest) macro_add_unittest_definitions() set(KisRandomGeneratorDemoSources kis_random_generator_demo.cpp kimageframe.cpp) ki18n_wrap_ui(KisRandomGeneratorDemoSources kis_random_generator_demo.ui) add_executable(KisRandomGeneratorDemo ${KisRandomGeneratorDemoSources}) target_link_libraries(KisRandomGeneratorDemo kritaimage) ecm_mark_as_test(KisRandomGeneratorDemo) ecm_add_tests( kis_base_node_test.cpp kis_fast_math_test.cpp kis_node_test.cpp kis_node_facade_test.cpp kis_fixed_paint_device_test.cpp kis_layer_test.cpp kis_effect_mask_test.cpp kis_iterator_test.cpp kis_painter_test.cpp kis_selection_test.cpp kis_count_visitor_test.cpp kis_projection_test.cpp kis_properties_configuration_test.cpp kis_transaction_test.cpp kis_pixel_selection_test.cpp kis_group_layer_test.cpp kis_paint_layer_test.cpp kis_adjustment_layer_test.cpp kis_annotation_test.cpp kis_change_profile_visitor_test.cpp kis_clone_layer_test.cpp kis_colorspace_convert_visitor_test.cpp kis_convolution_painter_test.cpp kis_crop_processing_visitor_test.cpp kis_processing_applicator_test.cpp kis_datamanager_test.cpp kis_fill_painter_test.cpp kis_filter_configuration_test.cpp kis_filter_test.cpp kis_filter_processing_information_test.cpp kis_filter_registry_test.cpp kis_filter_strategy_test.cpp kis_gradient_painter_test.cpp kis_image_commands_test.cpp kis_image_test.cpp kis_image_signal_router_test.cpp kis_iterators_ng_test.cpp kis_iterator_benchmark.cpp kis_updater_context_test.cpp kis_simple_update_queue_test.cpp kis_stroke_test.cpp kis_simple_stroke_strategy_test.cpp kis_stroke_strategy_undo_command_based_test.cpp kis_strokes_queue_test.cpp kis_mask_test.cpp kis_math_toolbox_test.cpp kis_name_server_test.cpp kis_node_commands_test.cpp kis_node_graph_listener_test.cpp kis_node_visitor_test.cpp kis_paint_information_test.cpp kis_distance_information_test.cpp kis_paintop_test.cpp kis_pattern_test.cpp kis_selection_mask_test.cpp kis_shared_ptr_test.cpp kis_bsplines_test.cpp kis_warp_transform_worker_test.cpp kis_liquify_transform_worker_test.cpp kis_transparency_mask_test.cpp kis_types_test.cpp kis_vec_test.cpp kis_filter_config_widget_test.cpp kis_mask_generator_test.cpp kis_cubic_curve_test.cpp kis_fixed_point_maths_test.cpp kis_node_query_path_test.cpp kis_filter_weights_buffer_test.cpp kis_filter_weights_applicator_test.cpp kis_fill_interval_test.cpp kis_fill_interval_map_test.cpp kis_scanline_fill_test.cpp kis_psd_layer_style_test.cpp kis_layer_style_projection_plane_test.cpp kis_lod_capable_layer_offset_test.cpp kis_algebra_2d_test.cpp kis_marker_painter_test.cpp kis_lazy_brush_test.cpp kis_colorize_mask_test.cpp kis_mask_similarity_test.cpp KisMaskGeneratorBenchmark.cpp + kis_time_range_test.cpp NAME_PREFIX "krita-image-" LINK_LIBRARIES kritaimage Qt5::Test) ecm_add_test(kis_layer_style_filter_environment_test.cpp TEST_NAME kritaimage-layer_style_filter_environment_test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) ecm_add_test(kis_asl_parser_test.cpp TEST_NAME kritalibpsd-asl_parser_test LINK_LIBRARIES kritapsd kritapigment kritawidgetutils kritacommand Qt5::Xml Qt5::Test) ecm_add_test(KisPerStrokeRandomSourceTest.cpp TEST_NAME KisPerStrokeRandomSourceTest LINK_LIBRARIES kritaimage Qt5::Test) ecm_add_test(KisWatershedWorkerTest.cpp TEST_NAME KisWatershedWorkerTest LINK_LIBRARIES kritaimage Qt5::Test) # ecm_add_test(kis_dom_utils_test.cpp # TEST_NAME krita-image-DomUtils-Test # LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc dep # kis_transform_worker_test.cpp # TEST_NAME krita-image-KisTransformWorkerTest #LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc # kis_perspective_transform_worker_test.cpp # TEST_NAME krita-image-KisPerspectiveTransformWorkerTest #LINK_LIBRARIES kritaimage Qt5::Test) # kis_cs_conversion_test.cpp # TEST_NAME krita-image-KisCsConversionTest # LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc # kis_processings_test.cpp # TEST_NAME krita-image-KisProcessingsTest #LINK_LIBRARIES kritaimage Qt5::Test) # image/tests cannot use stuff that needs kisdocument # kis_projection_leaf_test.cpp # TEST_NAME kritaimage-projection_leaf_test # LINK_LIBRARIES kritaimage Qt5::Test) if (NOT HAVE_FAILING_CMAKE) krita_add_broken_unit_test(kis_paint_device_test.cpp TEST_NAME krita-image-KisPaintDeviceTest LINK_LIBRARIES kritaimage kritaodf Qt5::Test) else() message(WARNING "Skipping KisPaintDeviceTest!!!!!!!!!!!!!!") endif() if (NOT HAVE_FAILING_CMAKE) krita_add_broken_unit_test(kis_filter_mask_test.cpp TEST_NAME krita-image-KisFilterMaskTest LINK_LIBRARIES kritaimage Qt5::Test) else() message(WARNING "Skipping KisFilterMaskTest!!!!!!!!!!!!!!") endif() krita_add_broken_unit_test(kis_transform_mask_test.cpp TEST_NAME krita-image-KisTransformMaskTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_histogram_test.cpp TEST_NAME krita-image-KisHistogramTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_walkers_test.cpp TEST_NAME krita-image-KisWalkersTest LINK_LIBRARIES kritaimage Qt5::Test) #krita_add_broken_unit_test(kis_async_merger_test.cpp # TEST_NAME krita-image-KisAsyncMergerTest # LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_update_scheduler_test.cpp TEST_NAME krita-image-KisUpdateSchedulerTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_queues_progress_updater_test.cpp TEST_NAME krita-image-KisQueuesProgressUpdaterTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_cage_transform_worker_test.cpp TEST_NAME krita-image-KisCageTransformWorkerTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_meta_data_test.cpp TEST_NAME krita-image-KisMetaDataTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_random_generator_test.cpp TEST_NAME krita-image-KisRandomGeneratorTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_keyframing_test.cpp TEST_NAME krita-image-Keyframing-Test LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_image_animation_interface_test.cpp TEST_NAME krita-image-ImageAnimationInterface-Test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) krita_add_broken_unit_test(kis_onion_skin_compositor_test.cpp TEST_NAME krita-image-OnionSkinCompositor-Test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) krita_add_broken_unit_test(kis_layer_styles_test.cpp TEST_NAME krita-image-LayerStylesTest LINK_LIBRARIES kritaimage Qt5::Test) diff --git a/libs/image/tests/kis_time_range_test.cpp b/libs/image/tests/kis_time_range_test.cpp new file mode 100644 index 0000000000..e566269c44 --- /dev/null +++ b/libs/image/tests/kis_time_range_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018 Jouni Pentikäinen + * + * 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_test.h" + +#include +#include "kis_time_range.h" + +void KisTimeRangeTest::testTimeSpans() +{ + KisTimeSpan emptySpan; + QCOMPARE(emptySpan.isEmpty(), true); + QCOMPARE(emptySpan.duration(), 0); + QCOMPARE(emptySpan.contains(0), false); + + KisTimeSpan span1(7, 17); + KisTimeSpan span2(10, 20); + KisTimeSpan span3(20, 30); + + QCOMPARE(span1.isEmpty(), false); + QCOMPARE(span1.duration(), 11); + QCOMPARE(span1.contains(6), false); + QCOMPARE(span1.contains(7), true); + QCOMPARE(span1.contains(17), true); + QCOMPARE(span1.contains(18), false); + + QCOMPARE(emptySpan | span1, span1); + QCOMPARE(span1 | emptySpan, span1); + QCOMPARE(emptySpan & span1, KisTimeSpan()); + QCOMPARE(span1 & emptySpan, KisTimeSpan()); + + QCOMPARE(span1 | span2, KisTimeSpan(7, 20)); + QCOMPARE(span2 | span1, KisTimeSpan(7, 20)); + QCOMPARE(span1 & span2, KisTimeSpan(10, 17)); + QCOMPARE(span2 & span1, KisTimeSpan(10, 17)); + + QCOMPARE(span1 | span3, KisTimeSpan(7, 30)); + QCOMPARE(span3 | span1, KisTimeSpan(7, 30)); + QCOMPARE(span1 & span3, KisTimeSpan()); + QCOMPARE(span3 & span1, KisTimeSpan()); +} + +void KisTimeRangeTest::testFrameSets() +{ + KisFrameSet emptySet; + QCOMPARE(emptySet.contains(0), false); + QCOMPARE(emptySet.isEmpty(), true); + QCOMPARE(emptySet.isInfinite(), false); + QCOMPARE(emptySet, KisFrameSet()); + + KisFrameSet set1 = KisFrameSet::between(7, 17); + + QCOMPARE(set1.isEmpty(), false); + QCOMPARE(set1.isInfinite(), false); + QCOMPARE(set1.start(), 7); + QCOMPARE(set1.contains(6), false); + QCOMPARE(set1.contains(7), true); + QCOMPARE(set1.contains(17), true); + QCOMPARE(set1.contains(18), false); + + KisFrameSet set2 = KisFrameSet::between(10, 20); + + QCOMPARE(set1 == set2, false); + + KisFrameSet intersection12 = set2 & set1; + QCOMPARE(intersection12.isEmpty(), false); + QCOMPARE(intersection12.isInfinite(), false); + QCOMPARE(intersection12.start(), 10); + QCOMPARE(intersection12.contains(9), false); + QCOMPARE(intersection12.contains(10), true); + QCOMPARE(intersection12.contains(17), true); + QCOMPARE(intersection12.contains(18), false); + + KisFrameSet set3 = KisFrameSet::between(20, 30); + + QCOMPARE(set1 & set3, emptySet); + + KisFrameSet union13 = set3 | set1; + QCOMPARE(union13.isEmpty(), false); + QCOMPARE(union13.isInfinite(), false); + QCOMPARE(union13.start(), 7); + QCOMPARE(union13.contains(6), false); + QCOMPARE(union13.contains(7), true); + QCOMPARE(union13.contains(17), true); + QCOMPARE(union13.contains(18), false); + QCOMPARE(union13.contains(19), false); + QCOMPARE(union13.contains(20), true); + QCOMPARE(union13.contains(30), true); + QCOMPARE(union13.contains(31), false); + + QCOMPARE(set1 - set3, set1); + QCOMPARE(set3 - set1, set3); + + KisFrameSet set4 = KisFrameSet({KisTimeSpan(9,10), KisTimeSpan(13, 14)}, 16); + KisFrameSet expected14 = KisFrameSet({KisTimeSpan(7,8), KisTimeSpan(11,12), KisTimeSpan(15,15)}); + KisFrameSet difference14 = set1 - set4; + + QCOMPARE(difference14.isEmpty(), false); + QCOMPARE(difference14.isInfinite(), false); + QCOMPARE(difference14, expected14); + QCOMPARE(set4 - set1, KisFrameSet::infiniteFrom(18)); + + KisFrameSet set5 = KisFrameSet::infiniteFrom(9); + + QCOMPARE(set5 & set1, KisFrameSet::between(9, 17)); + QCOMPARE(set5 | set1, KisFrameSet::infiniteFrom(7)); + QCOMPARE(set5 & set2, set2); + QCOMPARE(set5 | set2, set5); +} + +QTEST_MAIN(KisTimeRangeTest) diff --git a/libs/image/tests/kis_time_range_test.h b/libs/image/tests/kis_time_range_test.h new file mode 100644 index 0000000000..2aed5856b3 --- /dev/null +++ b/libs/image/tests/kis_time_range_test.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Jouni Pentikäinen + * + * 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_TEST +#define KIS_TIME_RANGE_TEST + +#include + +class KisTimeRangeTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testTimeSpans(); + void testFrameSets(); +}; + +#endif