Krita Source Code Documentation
Loading...
Searching...
No Matches
KisRegion.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6#include "KisRegion.h"
7
8#include <QRegion>
9#include "kis_debug.h"
10
11namespace detail {
12
14{
15 static int col(const QRect &rc) {
16 return rc.x();
17 }
18 static int nextCol(const QRect &rc) {
19 return rc.x() + rc.width();
20 }
21 static int rowHeight(const QRect &rc) {
22 return rc.height();
23 }
24 static bool rowIsLess(const QRect &lhs, const QRect &rhs) {
25 return lhs.y() < rhs.y();
26 }
27 static bool elementIsLess(const QRect &lhs, const QRect &rhs) {
28 return lhs.y() < rhs.y() || (lhs.y() == rhs.y() && lhs.x() < rhs.x());
29 }
30};
31
33{
34 static int col(const QRect &rc) {
35 return rc.y();
36 }
37 static int nextCol(const QRect &rc) {
38 return rc.y() + rc.height();
39 }
40 static int rowHeight(const QRect &rc) {
41 return rc.width();
42 }
43 static bool rowIsLess(const QRect &lhs, const QRect &rhs) {
44 return lhs.x() < rhs.x();
45 }
46 static bool elementIsLess(const QRect &lhs, const QRect &rhs) {
47 return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y());
48 }
49};
50
51template <typename MergePolicy>
54 MergePolicy policy)
55{
56 if (beginIt == endIt) return endIt;
57
58 std::sort(beginIt, endIt, MergePolicy::elementIsLess);
59
60 auto resultIt = beginIt;
61 auto it = std::next(beginIt);
62
63 while (it != endIt) {
64 auto rowEnd = std::upper_bound(it, endIt, *it, MergePolicy::rowIsLess);
65 for (auto rowIt = it; rowIt != rowEnd; ++rowIt) {
66 if (policy.rowHeight(*resultIt) == policy.rowHeight(*rowIt) &&
67 policy.nextCol(*resultIt) == policy.col(*rowIt)) {
68 *resultIt |= *rowIt;
69 } else {
70 resultIt++;
71 *resultIt = *rowIt;
72 }
73 }
74
75 it = rowEnd;
76 }
77
78 return std::next(resultIt);
79}
80
82{
83 static int rowStart(const QRect &rc) {
84 return rc.y();
85 }
86 static int rowEnd(const QRect &rc) {
87 return rc.bottom();
88 }
89 static int rowHeight(const QRect &rc) {
90 return rc.height();
91 }
92 static void setRowEnd(QRect &rc, int rowEnd) {
93 return rc.setBottom(rowEnd);
94 }
95 static bool rowIsLess(const QRect &lhs, const QRect &rhs) {
96 return lhs.y() < rhs.y();
97 }
98 static QRect splitRectHi(const QRect &rc, int rowEnd) {
99 return QRect(rc.x(), rc.y(),
100 rc.width(), rowEnd - rc.y() + 1);
101 }
102 static QRect splitRectLo(const QRect &rc, int rowEnd) {
103 return QRect(rc.x(), rowEnd + 1,
104 rc.width(), rc.height() - (rowEnd - rc.y() + 1));
105 }
106};
107
109{
110 static int rowStart(const QRect &rc) {
111 return rc.x();
112 }
113 static int rowEnd(const QRect &rc) {
114 return rc.right();
115 }
116 static int rowHeight(const QRect &rc) {
117 return rc.width();
118 }
119 static void setRowEnd(QRect &rc, int rowEnd) {
120 return rc.setRight(rowEnd);
121 }
122 static bool rowIsLess(const QRect &lhs, const QRect &rhs) {
123 return lhs.x() < rhs.x();
124 }
125 static QRect splitRectHi(const QRect &rc, int rowEnd) {
126 return QRect(rc.x(), rc.y(),
127 rowEnd - rc.x() + 1, rc.height());
128 }
129 static QRect splitRectLo(const QRect &rc, int rowEnd) {
130 return QRect(rowEnd + 1, rc.y(),
131 rc.width() - (rowEnd - rc.x() + 1), rc.height());
132 }
133};
134
135
136struct VoidNoOp {
137 void operator()() const { };
138 template<typename P1, typename... Params>
139 void operator()(P1 p1, Params... parameters) {
140 Q_UNUSED(p1);
141 operator()(parameters...);
142 }
143};
144
146{
148 : m_source(source),
149 m_destination(destination)
150 {
151 }
152
153 void operator()() {
154 m_destination.append(std::accumulate(m_source.begin(), m_source.end(),
155 QRect(), std::bit_or<QRect>()));
156 m_source.clear();
157 }
158
159private:
162};
163
164template <typename Policy, typename RowMergeOp, typename OutIt>
166 OutIt resultIt,
167 QVector<QRect> tempBuf[2],
168 int gridSize,
169 RowMergeOp rowMergeOp)
170{
171 if (beginIt == endIt) return;
172
173 QVector<QRect> &nextRowExtra = tempBuf[0];
174 QVector<QRect> &nextRowExtraTmp = tempBuf[1];
175
176 std::sort(beginIt, endIt, Policy::rowIsLess);
177 int rowStart = Policy::rowStart(*beginIt);
178 int rowEnd = rowStart + gridSize - 1;
179
180 auto it = beginIt;
181 while (1) {
182 bool switchToNextRow = false;
183
184 if (it == endIt) {
185 if (nextRowExtra.isEmpty()) {
186 rowMergeOp();
187 break;
188 } else {
189 switchToNextRow = true;
190 }
191 } else if (Policy::rowStart(*it) > rowEnd) {
192 switchToNextRow = true;
193 }
194
195 if (switchToNextRow) {
196 rowMergeOp();
197
198 if (!nextRowExtra.isEmpty()) {
199 rowStart = Policy::rowStart(nextRowExtra.first());
200 rowEnd = rowStart + gridSize - 1;
201
202 for (auto nextIt = nextRowExtra.begin(); nextIt != nextRowExtra.end(); ++nextIt) {
203 if (Policy::rowEnd(*nextIt) > rowEnd) {
204 nextRowExtraTmp.append(Policy::splitRectLo(*nextIt, rowEnd));
205 *resultIt++ = Policy::splitRectHi(*nextIt, rowEnd);
206 } else {
207 *resultIt++ = *nextIt;
208 }
209 }
210 nextRowExtra.clear();
211 std::swap(nextRowExtra, nextRowExtraTmp);
212
213 continue;
214 } else {
215 rowStart = Policy::rowStart(*it);
216 rowEnd = rowStart + gridSize - 1;
217 }
218 }
219
220 if (Policy::rowEnd(*it) > rowEnd) {
221 nextRowExtra.append(Policy::splitRectLo(*it, rowEnd));
222 *resultIt++ = Policy::splitRectHi(*it, rowEnd);
223 } else {
224 *resultIt++ = *it;
225 }
226
227 ++it;
228 }
229}
230
231}
232
239
241{
242 using namespace detail;
243
244 if (rects.isEmpty()) return;
245
246 QVector<QRect> rowsBuf;
247 QVector<QRect> intermediate;
248 QVector<QRect> tempBuf[2];
249
250 splitRects<VerticalSplitPolicy>(rects.begin(), rects.end(),
251 std::back_inserter(rowsBuf),
252 tempBuf, gridSize, VoidNoOp());
253
254 rects.clear();
257
258 auto rowBegin = rowsBuf.begin();
259 while (rowBegin != rowsBuf.end()) {
260 auto rowEnd = std::upper_bound(rowBegin, rowsBuf.end(),
261 QRect(rowBegin->x(),
262 rowBegin->y() + gridSize - 1,
263 1,1),
264 VerticalSplitPolicy::rowIsLess);
265
266 splitRects<HorizontalSplitPolicy>(rowBegin, rowEnd,
267 std::back_inserter(intermediate),
268 tempBuf, gridSize,
269 MergeRectsOp(intermediate, rects));
270 rowBegin = rowEnd;
271
272 KIS_SAFE_ASSERT_RECOVER_NOOP(intermediate.isEmpty());
275 }
276}
277
279{
281 auto it = std::unique(rects.begin(), rects.end());
282 rects.erase(it, rects.end());
283}
284
286{
287 m_rects << rect;
288}
289
290KisRegion::KisRegion(std::initializer_list<QRect> rects)
291 : m_rects(rects)
292{
293}
294
296 : m_rects(rects)
297{
299}
300
302 : m_rects(rects)
303{
305}
306
308{
309 m_rects = rhs.m_rects;
310 return *this;
311}
312
314{
315 for (auto it = m_rects.begin(); it != m_rects.end(); /* noop */) {
316 *it &= rect;
317 if (it->isEmpty()) {
318 it = m_rects.erase(it);
319 } else {
320 ++it;
321 }
322 }
324 return *this;
325}
326
328{
329 return std::accumulate(m_rects.constBegin(), m_rects.constEnd(), QRect(), std::bit_or<QRect>());
330}
331
333{
334 return m_rects;
335}
336
338{
339 return m_rects.size();
340}
341
343{
344 return boundingRect().isEmpty();
345}
346
347QRegion KisRegion::toQRegion() const
348{
349 // TODO: utilize QRegion::setRects to make creation of QRegion much
350 // faster. The only reason why we cannot use it "as is", is that our m_rects
351 // do not satisfy the second setRects()'s precondition: "All rectangles with
352 // a given top coordinate must have the same height". We can implement a
353 // simple algorithm for cropping m_rects, and it will be much faster than
354 // constructing QRegion iteratively.
355
356 return std::accumulate(m_rects.constBegin(), m_rects.constEnd(), QRegion(), std::bit_or<QRegion>());
357}
358
359void KisRegion::translate(int dx, int dy)
360{
361 std::transform(m_rects.begin(), m_rects.end(),
362 m_rects.begin(),
363 [dx, dy] (const QRect &rc) { return rc.translated(dx, dy); });
364}
365
366KisRegion KisRegion::translated(int dx, int dy) const
367{
368 KisRegion region(*this);
369 region.translate(dx, dy);
370 return region;
371}
372
373KisRegion KisRegion::fromQRegion(const QRegion &region)
374{
375 KisRegion result;
376 result.m_rects.clear();
377 QRegion::const_iterator begin = region.begin();
378 while (begin != region.end()) {
379 result.m_rects << *begin;
380 begin++;
381 }
382 return result;
383}
384
386{
389 return KisRegion(tmp);
390}
391
393{
394 auto endIt = mergeSparseRects(m_rects.begin(), m_rects.end());
395 m_rects.erase(endIt, m_rects.end());
396}
397
398bool operator==(const KisRegion &lhs, const KisRegion &rhs)
399{
400 return lhs.m_rects == rhs.m_rects;
401}
QPointF p1
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
bool operator==(const KisRegion &lhs, const KisRegion &rhs)
static KisRegion fromQRegion(const QRegion &region)
KisRegion translated(int x, int y) const
static KisRegion fromOverlappingRects(const QVector< QRect > &rects, int gridSize)
QVector< QRect > m_rects
Definition KisRegion.h:97
int rectCount() const
QRect boundingRect() const
void mergeAllRects()
KisRegion & operator=(const KisRegion &rhs)
static QVector< QRect >::iterator mergeSparseRects(QVector< QRect >::iterator beginIt, QVector< QRect >::iterator endIt)
merge a set of rectangles into a smaller set of bigger rectangles
KisRegion()=default
void translate(int dx, int dy)
QVector< QRect > rects() const
static void makeGridLikeRectsUnique(QVector< QRect > &rects)
KisRegion & operator&=(const QRect &rect)
bool isEmpty() const
static void approximateOverlappingRects(QVector< QRect > &rects, int gridSize)
QRegion toQRegion() const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
QVector< QRect >::iterator mergeRects(QVector< QRect >::iterator beginIt, QVector< QRect >::iterator endIt, MergePolicy policy)
Definition KisRegion.cpp:52
void splitRects(QVector< QRect >::iterator beginIt, QVector< QRect >::iterator endIt, OutIt resultIt, QVector< QRect > tempBuf[2], int gridSize, RowMergeOp rowMergeOp)
static bool elementIsLess(const QRect &lhs, const QRect &rhs)
Definition KisRegion.cpp:27
static int col(const QRect &rc)
Definition KisRegion.cpp:15
static int nextCol(const QRect &rc)
Definition KisRegion.cpp:18
static bool rowIsLess(const QRect &lhs, const QRect &rhs)
Definition KisRegion.cpp:24
static int rowHeight(const QRect &rc)
Definition KisRegion.cpp:21
static QRect splitRectHi(const QRect &rc, int rowEnd)
static bool rowIsLess(const QRect &lhs, const QRect &rhs)
static void setRowEnd(QRect &rc, int rowEnd)
static QRect splitRectLo(const QRect &rc, int rowEnd)
static int rowEnd(const QRect &rc)
static int rowStart(const QRect &rc)
static int rowHeight(const QRect &rc)
QVector< QRect > & m_destination
MergeRectsOp(QVector< QRect > &source, QVector< QRect > &destination)
QVector< QRect > & m_source
static int nextCol(const QRect &rc)
Definition KisRegion.cpp:37
static int col(const QRect &rc)
Definition KisRegion.cpp:34
static int rowHeight(const QRect &rc)
Definition KisRegion.cpp:40
static bool rowIsLess(const QRect &lhs, const QRect &rhs)
Definition KisRegion.cpp:43
static bool elementIsLess(const QRect &lhs, const QRect &rhs)
Definition KisRegion.cpp:46
static bool rowIsLess(const QRect &lhs, const QRect &rhs)
Definition KisRegion.cpp:95
static void setRowEnd(QRect &rc, int rowEnd)
Definition KisRegion.cpp:92
static int rowHeight(const QRect &rc)
Definition KisRegion.cpp:89
static QRect splitRectHi(const QRect &rc, int rowEnd)
Definition KisRegion.cpp:98
static QRect splitRectLo(const QRect &rc, int rowEnd)
static int rowEnd(const QRect &rc)
Definition KisRegion.cpp:86
static int rowStart(const QRect &rc)
Definition KisRegion.cpp:83
void operator()(P1 p1, Params... parameters)
void operator()() const