File indexing completed on 2024-05-12 15:57:03
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "KisRegion.h" 0007 0008 #include <QRegion> 0009 #include "kis_debug.h" 0010 0011 namespace detail { 0012 0013 struct HorizontalMergePolicy 0014 { 0015 static int col(const QRect &rc) { 0016 return rc.x(); 0017 } 0018 static int nextCol(const QRect &rc) { 0019 return rc.x() + rc.width(); 0020 } 0021 static int rowHeight(const QRect &rc) { 0022 return rc.height(); 0023 } 0024 static bool rowIsLess(const QRect &lhs, const QRect &rhs) { 0025 return lhs.y() < rhs.y(); 0026 } 0027 static bool elementIsLess(const QRect &lhs, const QRect &rhs) { 0028 return lhs.y() < rhs.y() || (lhs.y() == rhs.y() && lhs.x() < rhs.x()); 0029 } 0030 }; 0031 0032 struct VerticalMergePolicy 0033 { 0034 static int col(const QRect &rc) { 0035 return rc.y(); 0036 } 0037 static int nextCol(const QRect &rc) { 0038 return rc.y() + rc.height(); 0039 } 0040 static int rowHeight(const QRect &rc) { 0041 return rc.width(); 0042 } 0043 static bool rowIsLess(const QRect &lhs, const QRect &rhs) { 0044 return lhs.x() < rhs.x(); 0045 } 0046 static bool elementIsLess(const QRect &lhs, const QRect &rhs) { 0047 return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y()); 0048 } 0049 }; 0050 0051 template <typename MergePolicy> 0052 QVector<QRect>::iterator mergeRects(QVector<QRect>::iterator beginIt, 0053 QVector<QRect>::iterator endIt, 0054 MergePolicy policy) 0055 { 0056 if (beginIt == endIt) return endIt; 0057 0058 std::sort(beginIt, endIt, MergePolicy::elementIsLess); 0059 0060 auto resultIt = beginIt; 0061 auto it = std::next(beginIt); 0062 0063 while (it != endIt) { 0064 auto rowEnd = std::upper_bound(it, endIt, *it, MergePolicy::rowIsLess); 0065 for (auto rowIt = it; rowIt != rowEnd; ++rowIt) { 0066 if (policy.rowHeight(*resultIt) == policy.rowHeight(*rowIt) && 0067 policy.nextCol(*resultIt) == policy.col(*rowIt)) { 0068 *resultIt |= *rowIt; 0069 } else { 0070 resultIt++; 0071 *resultIt = *rowIt; 0072 } 0073 } 0074 0075 it = rowEnd; 0076 } 0077 0078 return std::next(resultIt); 0079 } 0080 0081 struct VerticalSplitPolicy 0082 { 0083 static int rowStart(const QRect &rc) { 0084 return rc.y(); 0085 } 0086 static int rowEnd(const QRect &rc) { 0087 return rc.bottom(); 0088 } 0089 static int rowHeight(const QRect &rc) { 0090 return rc.height(); 0091 } 0092 static void setRowEnd(QRect &rc, int rowEnd) { 0093 return rc.setBottom(rowEnd); 0094 } 0095 static bool rowIsLess(const QRect &lhs, const QRect &rhs) { 0096 return lhs.y() < rhs.y(); 0097 } 0098 static QRect splitRectHi(const QRect &rc, int rowEnd) { 0099 return QRect(rc.x(), rc.y(), 0100 rc.width(), rowEnd - rc.y() + 1); 0101 } 0102 static QRect splitRectLo(const QRect &rc, int rowEnd) { 0103 return QRect(rc.x(), rowEnd + 1, 0104 rc.width(), rc.height() - (rowEnd - rc.y() + 1)); 0105 } 0106 }; 0107 0108 struct HorizontalSplitPolicy 0109 { 0110 static int rowStart(const QRect &rc) { 0111 return rc.x(); 0112 } 0113 static int rowEnd(const QRect &rc) { 0114 return rc.right(); 0115 } 0116 static int rowHeight(const QRect &rc) { 0117 return rc.width(); 0118 } 0119 static void setRowEnd(QRect &rc, int rowEnd) { 0120 return rc.setRight(rowEnd); 0121 } 0122 static bool rowIsLess(const QRect &lhs, const QRect &rhs) { 0123 return lhs.x() < rhs.x(); 0124 } 0125 static QRect splitRectHi(const QRect &rc, int rowEnd) { 0126 return QRect(rc.x(), rc.y(), 0127 rowEnd - rc.x() + 1, rc.height()); 0128 } 0129 static QRect splitRectLo(const QRect &rc, int rowEnd) { 0130 return QRect(rowEnd + 1, rc.y(), 0131 rc.width() - (rowEnd - rc.x() + 1), rc.height()); 0132 } 0133 }; 0134 0135 0136 struct VoidNoOp { 0137 void operator()() const { }; 0138 template<typename P1, typename... Params> 0139 void operator()(P1 p1, Params... parameters) { 0140 Q_UNUSED(p1); 0141 operator()(parameters...); 0142 } 0143 }; 0144 0145 struct MergeRectsOp 0146 { 0147 MergeRectsOp(QVector<QRect> &source, QVector<QRect> &destination) 0148 : m_source(source), 0149 m_destination(destination) 0150 { 0151 } 0152 0153 void operator()() { 0154 m_destination.append(std::accumulate(m_source.begin(), m_source.end(), 0155 QRect(), std::bit_or<QRect>())); 0156 m_source.clear(); 0157 } 0158 0159 private: 0160 QVector<QRect> &m_source; 0161 QVector<QRect> &m_destination; 0162 }; 0163 0164 template <typename Policy, typename RowMergeOp, typename OutIt> 0165 void splitRects(QVector<QRect>::iterator beginIt, QVector<QRect>::iterator endIt, 0166 OutIt resultIt, 0167 QVector<QRect> tempBuf[2], 0168 int gridSize, 0169 RowMergeOp rowMergeOp) 0170 { 0171 if (beginIt == endIt) return; 0172 0173 QVector<QRect> &nextRowExtra = tempBuf[0]; 0174 QVector<QRect> &nextRowExtraTmp = tempBuf[1]; 0175 0176 std::sort(beginIt, endIt, Policy::rowIsLess); 0177 int rowStart = Policy::rowStart(*beginIt); 0178 int rowEnd = rowStart + gridSize - 1; 0179 0180 auto it = beginIt; 0181 while (1) { 0182 bool switchToNextRow = false; 0183 0184 if (it == endIt) { 0185 if (nextRowExtra.isEmpty()) { 0186 rowMergeOp(); 0187 break; 0188 } else { 0189 switchToNextRow = true; 0190 } 0191 } else if (Policy::rowStart(*it) > rowEnd) { 0192 switchToNextRow = true; 0193 } 0194 0195 if (switchToNextRow) { 0196 rowMergeOp(); 0197 0198 if (!nextRowExtra.isEmpty()) { 0199 rowStart = Policy::rowStart(nextRowExtra.first()); 0200 rowEnd = rowStart + gridSize - 1; 0201 0202 for (auto nextIt = nextRowExtra.begin(); nextIt != nextRowExtra.end(); ++nextIt) { 0203 if (Policy::rowEnd(*nextIt) > rowEnd) { 0204 nextRowExtraTmp.append(Policy::splitRectLo(*nextIt, rowEnd)); 0205 *resultIt++ = Policy::splitRectHi(*nextIt, rowEnd); 0206 } else { 0207 *resultIt++ = *nextIt; 0208 } 0209 } 0210 nextRowExtra.clear(); 0211 std::swap(nextRowExtra, nextRowExtraTmp); 0212 0213 continue; 0214 } else { 0215 rowStart = Policy::rowStart(*it); 0216 rowEnd = rowStart + gridSize - 1; 0217 } 0218 } 0219 0220 if (Policy::rowEnd(*it) > rowEnd) { 0221 nextRowExtra.append(Policy::splitRectLo(*it, rowEnd)); 0222 *resultIt++ = Policy::splitRectHi(*it, rowEnd); 0223 } else { 0224 *resultIt++ = *it; 0225 } 0226 0227 ++it; 0228 } 0229 } 0230 0231 } 0232 0233 QVector<QRect>::iterator KisRegion::mergeSparseRects(QVector<QRect>::iterator beginIt, QVector<QRect>::iterator endIt) 0234 { 0235 endIt = detail::mergeRects(beginIt, endIt, detail::HorizontalMergePolicy()); 0236 endIt = detail::mergeRects(beginIt, endIt, detail::VerticalMergePolicy()); 0237 return endIt; 0238 } 0239 0240 void KisRegion::approximateOverlappingRects(QVector<QRect> &rects, int gridSize) 0241 { 0242 using namespace detail; 0243 0244 if (rects.isEmpty()) return; 0245 0246 QVector<QRect> rowsBuf; 0247 QVector<QRect> intermediate; 0248 QVector<QRect> tempBuf[2]; 0249 0250 splitRects<VerticalSplitPolicy>(rects.begin(), rects.end(), 0251 std::back_inserter(rowsBuf), 0252 tempBuf, gridSize, VoidNoOp()); 0253 0254 rects.clear(); 0255 KIS_SAFE_ASSERT_RECOVER_NOOP(tempBuf[0].isEmpty()); 0256 KIS_SAFE_ASSERT_RECOVER_NOOP(tempBuf[1].isEmpty()); 0257 0258 auto rowBegin = rowsBuf.begin(); 0259 while (rowBegin != rowsBuf.end()) { 0260 auto rowEnd = std::upper_bound(rowBegin, rowsBuf.end(), 0261 QRect(rowBegin->x(), 0262 rowBegin->y() + gridSize - 1, 0263 1,1), 0264 VerticalSplitPolicy::rowIsLess); 0265 0266 splitRects<HorizontalSplitPolicy>(rowBegin, rowEnd, 0267 std::back_inserter(intermediate), 0268 tempBuf, gridSize, 0269 MergeRectsOp(intermediate, rects)); 0270 rowBegin = rowEnd; 0271 0272 KIS_SAFE_ASSERT_RECOVER_NOOP(intermediate.isEmpty()); 0273 KIS_SAFE_ASSERT_RECOVER_NOOP(tempBuf[0].isEmpty()); 0274 KIS_SAFE_ASSERT_RECOVER_NOOP(tempBuf[1].isEmpty()); 0275 } 0276 } 0277 0278 void KisRegion::makeGridLikeRectsUnique(QVector<QRect> &rects) 0279 { 0280 std::sort(rects.begin(), rects.end(), detail::HorizontalMergePolicy::elementIsLess); 0281 auto it = std::unique(rects.begin(), rects.end()); 0282 rects.erase(it, rects.end()); 0283 } 0284 0285 KisRegion::KisRegion(const QRect &rect) 0286 { 0287 m_rects << rect; 0288 } 0289 0290 KisRegion::KisRegion(std::initializer_list<QRect> rects) 0291 : m_rects(rects) 0292 { 0293 } 0294 0295 KisRegion::KisRegion(const QVector<QRect> &rects) 0296 : m_rects(rects) 0297 { 0298 mergeAllRects(); 0299 } 0300 0301 KisRegion::KisRegion(QVector<QRect> &&rects) 0302 : m_rects(rects) 0303 { 0304 mergeAllRects(); 0305 } 0306 0307 KisRegion &KisRegion::operator=(const KisRegion &rhs) 0308 { 0309 m_rects = rhs.m_rects; 0310 return *this; 0311 } 0312 0313 KisRegion &KisRegion::operator&=(const QRect &rect) 0314 { 0315 for (auto it = m_rects.begin(); it != m_rects.end(); /* noop */) { 0316 *it &= rect; 0317 if (it->isEmpty()) { 0318 it = m_rects.erase(it); 0319 } else { 0320 ++it; 0321 } 0322 } 0323 mergeAllRects(); 0324 return *this; 0325 } 0326 0327 QRect KisRegion::boundingRect() const 0328 { 0329 return std::accumulate(m_rects.constBegin(), m_rects.constEnd(), QRect(), std::bit_or<QRect>()); 0330 } 0331 0332 QVector<QRect> KisRegion::rects() const 0333 { 0334 return m_rects; 0335 } 0336 0337 int KisRegion::rectCount() const 0338 { 0339 return m_rects.size(); 0340 } 0341 0342 bool KisRegion::isEmpty() const 0343 { 0344 return boundingRect().isEmpty(); 0345 } 0346 0347 QRegion KisRegion::toQRegion() const 0348 { 0349 // TODO: ustilize QRegion::setRects to make creation of QRegion much 0350 // faster. The only reason why we cannot use it "as is", our m_rects 0351 // do not satisfy the second setRects()'s precondition: "All rectangles with 0352 // a given top coordinate must have the same height". We can implement an 0353 // simple algorithm for cropping m_rects, and it will be much faster than 0354 // constructing QRegion iterationally. 0355 0356 return std::accumulate(m_rects.constBegin(), m_rects.constEnd(), QRegion(), std::bit_or<QRegion>()); 0357 } 0358 0359 void KisRegion::translate(int dx, int dy) 0360 { 0361 std::transform(m_rects.begin(), m_rects.end(), 0362 m_rects.begin(), 0363 [dx, dy] (const QRect &rc) { return rc.translated(dx, dy); }); 0364 } 0365 0366 KisRegion KisRegion::translated(int dx, int dy) const 0367 { 0368 KisRegion region(*this); 0369 region.translate(dx, dy); 0370 return region; 0371 } 0372 0373 KisRegion KisRegion::fromQRegion(const QRegion ®ion) 0374 { 0375 KisRegion result; 0376 result.m_rects.clear(); 0377 QRegion::const_iterator begin = region.begin(); 0378 while (begin != region.end()) { 0379 result.m_rects << *begin; 0380 begin++; 0381 } 0382 return result; 0383 } 0384 0385 KisRegion KisRegion::fromOverlappingRects(const QVector<QRect> &rects, int gridSize) 0386 { 0387 QVector<QRect> tmp = rects; 0388 approximateOverlappingRects(tmp, gridSize); 0389 return KisRegion(tmp); 0390 } 0391 0392 void KisRegion::mergeAllRects() 0393 { 0394 auto endIt = mergeSparseRects(m_rects.begin(), m_rects.end()); 0395 m_rects.erase(endIt, m_rects.end()); 0396 } 0397 0398 bool operator==(const KisRegion &lhs, const KisRegion &rhs) 0399 { 0400 return lhs.m_rects == rhs.m_rects; 0401 }