File indexing completed on 2024-06-09 04:21:55

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_fill_interval_map.h"
0008 #include "kis_fill_interval_map_p.h"
0009 
0010 #include "kis_fill_sanity_checks.h"
0011 
0012 
0013 KisFillIntervalMap::KisFillIntervalMap()
0014     : m_d(new Private)
0015 {
0016 }
0017 
0018 KisFillIntervalMap::~KisFillIntervalMap()
0019 {
0020 }
0021 
0022 void KisFillIntervalMap::insertInterval(const KisFillInterval &interval)
0023 {
0024     Private::GlobalMap::iterator rowMap = m_d->map.find(interval.row);
0025     if (rowMap == m_d->map.end()) {
0026         rowMap = m_d->map.insert(interval.row, Private::LineIntervalMap());
0027     }
0028 
0029     rowMap->insert(interval.start, interval);
0030 }
0031 
0032 void KisFillIntervalMap::cropInterval(KisFillInterval *interval)
0033 {
0034     Private::IteratorRange range;
0035     range = m_d->findFirstIntersectingInterval(*interval);
0036 
0037     Private::LineIntervalMap::iterator it = range.beginIt;
0038 
0039     while (interval->isValid() && it != range.endIt) {
0040         bool needsIncrement = true;
0041 
0042         if (it->start <= interval->start && it->end >= interval->start) {
0043             int savedIntervalStart = interval->start;
0044             interval->start = it->end + 1;
0045 
0046             /**
0047              * It might happen that we need to split a backward
0048              * interval into two pieces
0049              */
0050             if (it->end > interval->end) {
0051                 KisFillInterval newInterval(interval->end + 1, it->end, it->row);
0052                 range.rowMapIt->insert(newInterval.start, newInterval);
0053             }
0054 
0055             it->end = savedIntervalStart - 1;
0056 
0057             /**
0058              * It might also happen that the backward interval is
0059              * fully eaten by the forward interval. This is possible
0060              * only in case the BW-interval was generated by the
0061              * strictly adjacent FW-interval, that is (it->start ==
0062              * interval->start)
0063              */
0064             if (!it->isValid()) {
0065                 it = range.rowMapIt->erase(it);
0066                 needsIncrement = false;
0067             }
0068         } else if (it->start <= interval->end && it->end >= interval->end) {
0069             int savedIntervalEnd = interval->end;
0070             interval->end = it->start - 1;
0071             it->start = savedIntervalEnd + 1;
0072 
0073             /**
0074              * The BW-interval is eaten by the FW-interval. See a
0075              * comment above
0076              */
0077             if (!it->isValid()) {
0078                 it = range.rowMapIt->erase(it);
0079                 needsIncrement = false;
0080             }
0081         } else if (it->start > interval->end) {
0082             break;
0083         }
0084 
0085 #ifdef ENABLE_FILL_SANITY_CHECKS
0086         else if (it->start > interval->start && it->end < interval->end) {
0087             SANITY_ASSERT_MSG(0, "FATAL: The backward interval cannot fully reside inside the forward interval");
0088             interval->invalidate();
0089             it->invalidate();
0090             it = range.rowMapIt->erase(it);
0091             needsIncrement = false;
0092         }
0093 
0094         // The code above should have removed the invalidated backward interval,
0095         // just verify that
0096         KIS_SAFE_ASSERT_RECOVER((it == range.endIt || it->isValid()) &&
0097                                 "FATAL: The backward interval cannot become "
0098                                 "invalid during the crop action") {
0099             it = range.rowMapIt->erase(it);
0100             needsIncrement = false;
0101         }
0102 #endif /* ENABLE_FILL_SANITY_CHECKS */
0103 
0104         if (needsIncrement) {
0105             it++;
0106         }
0107     }
0108 }
0109 
0110 KisFillIntervalMap::Private::IteratorRange
0111 KisFillIntervalMap::Private::findFirstIntersectingInterval(const KisFillInterval &interval)
0112 {
0113     Private::GlobalMap::iterator rowMap = map.find(interval.row);
0114     if (rowMap == map.end()) {
0115         return IteratorRange();
0116     }
0117 
0118     LineIntervalMap::iterator it = rowMap->begin();
0119     LineIntervalMap::iterator end = rowMap->end();
0120 
0121     while(it != end) {
0122         if (it->end < interval.start) {
0123             ++it;
0124         } else if (it->start > interval.end) {
0125             it = end;
0126             break;
0127         } else {
0128             break;
0129         }
0130     }
0131 
0132     return IteratorRange(it, end, rowMap);
0133 }
0134 
0135 QStack<KisFillInterval> KisFillIntervalMap::fetchAllIntervals(int rowCorrection) const
0136 {
0137     QStack<KisFillInterval> intervals;
0138 
0139     Private::GlobalMap::const_iterator rowMapIt = m_d->map.constBegin();
0140     Private::GlobalMap::const_iterator rowMapEndIt = m_d->map.constEnd();
0141 
0142     while (rowMapIt != rowMapEndIt) {
0143         Private::LineIntervalMap::const_iterator it = rowMapIt->constBegin();
0144         Private::LineIntervalMap::const_iterator end = rowMapIt->constEnd();
0145 
0146         while(it != end) {
0147             KisFillInterval interval = *it;
0148             interval.row += rowCorrection;
0149             intervals.append(interval);
0150             ++it;
0151         }
0152 
0153         ++rowMapIt;
0154     }
0155 
0156     return intervals;
0157 }
0158 
0159 void KisFillIntervalMap::clear()
0160 {
0161     m_d->map.clear();
0162 }