QRegion: make iterable
Virtually all code in Qt that inspects a QRegion does so by calling rects(), which returns a QVector<QRect>. But rects() has a problem: A QRegion that contains just one rectangle internally is not represented by a QVector, and the mere act of calling rects() makes QRegion create one. So, expose the fact that QRegion is a container of QRects to users by providing iterators and begin()/end(), which can be nothrow, since for the one-rectangle case, instead of vectorize()ing the region, we just return pointers to (and one past) the 'extent' rectangle. As a consequence, the iterator type is just const QRect*, but I think that whatever containers QRegion may use under the hood in the future, it will be certainly one that is layout-compatible with a C array. No mutable iterators are provided, since QRegion maintains a running bounding-rect, so a mutable iterator would have to call into QRegion for every change, which doesn't make sense. [ChangeLog][QtGui][QRegion] Is now iterable as a container of QRects: added {c,}{r,}{begin,end}(). Change-Id: I2fa565fac0c1d26e2c0937604b23763cd4e23604 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
parent
12705f70c9
commit
77164e4cc0
@ -81,8 +81,8 @@ QT_BEGIN_NAMESPACE
|
||||
contains() a QPoint or QRect. The bounding rectangle can be found
|
||||
with boundingRect().
|
||||
|
||||
The function rects() gives a decomposition of the region into
|
||||
rectangles.
|
||||
Iteration over the region (with begin(), end()) gives a decomposition of
|
||||
the region into rectangles. The same sequence of rectangles is returned by rects().
|
||||
|
||||
Example of using complex regions:
|
||||
\snippet code/src_gui_painting_qregion.cpp 0
|
||||
@ -927,6 +927,100 @@ QRegion QRegion::intersect(const QRect &r) const
|
||||
The union of all the rectangles is equal to the original region.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QRegion::const_iterator
|
||||
\since 5.8
|
||||
|
||||
An iterator over the QRects that make up the region.
|
||||
|
||||
QRegion does not offer mutable iterators.
|
||||
|
||||
\sa begin(), end()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QRegion::const_reverse_iterator
|
||||
\since 5.8
|
||||
|
||||
A reverse iterator over the QRects that make up the region.
|
||||
|
||||
QRegion does not offer mutable iterators.
|
||||
|
||||
\sa rbegin(), rend()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::begin() const
|
||||
\since 5.8
|
||||
|
||||
Returns a const_iterator pointing to the beginning of the range of
|
||||
rectangles that make up this range, in the order in which rects()
|
||||
returns them.
|
||||
|
||||
\sa rbegin(), cbegin(), end()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::cbegin() const
|
||||
\since 5.8
|
||||
|
||||
Same as begin().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::end() const
|
||||
\since 5.8
|
||||
|
||||
Returns a const_iterator pointing to one past the end of the range of
|
||||
rectangles that make up this range, in the order in which rects()
|
||||
returns them.
|
||||
|
||||
\sa rend(), cend(), begin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::cend() const
|
||||
\since 5.8
|
||||
|
||||
Same as end().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::rbegin() const
|
||||
\since 5.8
|
||||
|
||||
Returns a const_reverse_iterator pointing to the beginning of the range of
|
||||
rectangles that make up this range, in the reverse order in which rects()
|
||||
returns them.
|
||||
|
||||
\sa begin(), crbegin(), rend()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::crbegin() const
|
||||
\since 5.8
|
||||
|
||||
Same as rbegin().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::rend() const
|
||||
\since 5.8
|
||||
|
||||
Returns a const_reverse_iterator pointing to one past the end of the range of
|
||||
rectangles that make up this range, in the reverse order in which rects()
|
||||
returns them.
|
||||
|
||||
\sa end(), crend(), rbegin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRegion::crend() const
|
||||
\since 5.8
|
||||
|
||||
Same as rend().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRegion::setRects(const QRect *rects, int number)
|
||||
|
||||
@ -1171,6 +1265,12 @@ struct QRegionPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
const QRect *begin() const Q_DECL_NOTHROW
|
||||
{ return numRects == 1 ? &extents : rects.data(); } // avoid vectorize()
|
||||
|
||||
const QRect *end() const Q_DECL_NOTHROW
|
||||
{ return begin() + numRects; }
|
||||
|
||||
inline void append(const QRect *r);
|
||||
void append(const QRegionPrivate *r);
|
||||
void prepend(const QRect *r);
|
||||
@ -4248,6 +4348,16 @@ QVector<QRect> QRegion::rects() const
|
||||
}
|
||||
}
|
||||
|
||||
QRegion::const_iterator QRegion::begin() const Q_DECL_NOTHROW
|
||||
{
|
||||
return d->qt_rgn ? d->qt_rgn->begin() : nullptr;
|
||||
}
|
||||
|
||||
QRegion::const_iterator QRegion::end() const Q_DECL_NOTHROW
|
||||
{
|
||||
return d->qt_rgn ? d->qt_rgn->end() : nullptr;
|
||||
}
|
||||
|
||||
void QRegion::setRects(const QRect *rects, int num)
|
||||
{
|
||||
*this = QRegion();
|
||||
|
@ -81,6 +81,18 @@ public:
|
||||
bool isEmpty() const;
|
||||
bool isNull() const;
|
||||
|
||||
typedef const QRect *const_iterator;
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
|
||||
const_iterator begin() const Q_DECL_NOTHROW;
|
||||
const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); }
|
||||
const_iterator end() const Q_DECL_NOTHROW;
|
||||
const_iterator cend() const Q_DECL_NOTHROW { return end(); }
|
||||
const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); }
|
||||
const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); }
|
||||
const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); }
|
||||
const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); }
|
||||
|
||||
bool contains(const QPoint &p) const;
|
||||
bool contains(const QRect &r) const;
|
||||
|
||||
|
@ -47,6 +47,7 @@ public:
|
||||
private slots:
|
||||
void moveSemantics();
|
||||
void boundingRect();
|
||||
void rangeFor();
|
||||
void rects();
|
||||
void swap();
|
||||
void setRects();
|
||||
@ -136,17 +137,34 @@ void tst_QRegion::boundingRect()
|
||||
|
||||
}
|
||||
|
||||
void tst_QRegion::rangeFor()
|
||||
{
|
||||
// compile-only test for range-for over QRegion, so really useless
|
||||
// content otherwise:
|
||||
QRect rect(10, -20, 30, 40);
|
||||
QRegion region(rect);
|
||||
int equal = 0;
|
||||
for (const QRect &r : region) // check this compiles
|
||||
equal += int(r == rect); // can't use QCOMPARE here b/c of the
|
||||
// MSVC 201272013 parse bug re:
|
||||
// do-while in range-for loops
|
||||
QCOMPARE(equal, 1);
|
||||
}
|
||||
|
||||
void tst_QRegion::rects()
|
||||
{
|
||||
{
|
||||
QRect rect;
|
||||
QRegion region(rect);
|
||||
QVERIFY(region.isEmpty());
|
||||
QCOMPARE(region.begin(), region.end());
|
||||
QVERIFY(region.rects().isEmpty());
|
||||
}
|
||||
{
|
||||
QRect rect(10, -20, 30, 40);
|
||||
QRegion region(rect);
|
||||
QCOMPARE(region.end(), region.begin() + 1);
|
||||
QCOMPARE(*region.begin(), rect);
|
||||
QCOMPARE(region.rects().count(), 1);
|
||||
QCOMPARE(region.rects()[0], rect);
|
||||
}
|
||||
@ -192,6 +210,7 @@ void tst_QRegion::setRects()
|
||||
region.setRects(&rect, 0);
|
||||
QVERIFY(region.isEmpty());
|
||||
QCOMPARE(region, QRegion());
|
||||
QCOMPARE(region.begin(), region.end());
|
||||
QVERIFY(!region.boundingRect().isValid());
|
||||
QVERIFY(region.rects().isEmpty());
|
||||
}
|
||||
@ -199,6 +218,7 @@ void tst_QRegion::setRects()
|
||||
QRegion region;
|
||||
QRect rect;
|
||||
region.setRects(&rect, 1);
|
||||
QCOMPARE(region.begin(), region.end());
|
||||
QVERIFY(!region.boundingRect().isValid());
|
||||
QVERIFY(region.rects().isEmpty());
|
||||
}
|
||||
@ -206,8 +226,10 @@ void tst_QRegion::setRects()
|
||||
QRegion region;
|
||||
QRect rect(10, -20, 30, 40);
|
||||
region.setRects(&rect, 1);
|
||||
QCOMPARE(region.end(), region.begin() + 1);
|
||||
QCOMPARE(region.rects().count(), 1);
|
||||
QCOMPARE(region.rects()[0], rect);
|
||||
QCOMPARE(*region.begin(), rect);
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,8 +342,12 @@ void tst_QRegion::emptyPolygonRegion()
|
||||
|
||||
QRegion r(pa);
|
||||
QTEST(r.isEmpty(), "isEmpty");
|
||||
QTEST(int(std::distance(r.begin(), r.end())), "numRects");
|
||||
QVector<QRect> rects;
|
||||
std::copy(r.begin(), r.end(), std::back_inserter(rects));
|
||||
QTEST(r.rects().count(), "numRects");
|
||||
QTEST(r.rects(), "rects");
|
||||
QCOMPARE(r.rects(), rects);
|
||||
}
|
||||
|
||||
|
||||
@ -860,6 +886,7 @@ void tst_QRegion::isEmpty()
|
||||
QFETCH(QRegion, region);
|
||||
|
||||
QVERIFY(region.isEmpty());
|
||||
QCOMPARE(region.begin(), region.end());
|
||||
QCOMPARE(region, QRegion());
|
||||
QCOMPARE(region.rectCount(), 0);
|
||||
QCOMPARE(region.boundingRect(), QRect());
|
||||
@ -892,6 +919,11 @@ void tst_QRegion::regionFromPath()
|
||||
path.addRect(0, 100, 100, 1000);
|
||||
|
||||
QRegion rgn(path.toFillPolygon().toPolygon());
|
||||
|
||||
QCOMPARE(rgn.end(), rgn.begin() + 2);
|
||||
QCOMPARE(rgn.begin()[0], QRect(0, 0, 10, 10));
|
||||
QCOMPARE(rgn.begin()[1], QRect(0, 100, 100, 1000));
|
||||
|
||||
QCOMPARE(rgn.rects().size(), 2);
|
||||
QCOMPARE(rgn.rects().at(0), QRect(0, 0, 10, 10));
|
||||
QCOMPARE(rgn.rects().at(1), QRect(0, 100, 100, 1000));
|
||||
@ -905,8 +937,14 @@ void tst_QRegion::regionFromPath()
|
||||
path.addRect(10, 10, 80, 80);
|
||||
|
||||
QRegion rgn(path.toFillPolygon().toPolygon());
|
||||
QCOMPARE(rgn.rects().size(), 4);
|
||||
|
||||
QCOMPARE(rgn.end(), rgn.begin() + 4);
|
||||
QCOMPARE(rgn.begin()[0], QRect(0, 0, 100, 10));
|
||||
QCOMPARE(rgn.begin()[1], QRect(0, 10, 10, 80));
|
||||
QCOMPARE(rgn.begin()[2], QRect(90, 10, 10, 80));
|
||||
QCOMPARE(rgn.begin()[3], QRect(0, 90, 100, 10));
|
||||
|
||||
QCOMPARE(rgn.rects().size(), 4);
|
||||
QCOMPARE(rgn.rects().at(0), QRect(0, 0, 100, 10));
|
||||
QCOMPARE(rgn.rects().at(1), QRect(0, 10, 10, 80));
|
||||
QCOMPARE(rgn.rects().at(2), QRect(90, 10, 10, 80));
|
||||
|
Loading…
Reference in New Issue
Block a user