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:
Marc Mutz 2016-02-27 12:40:36 +01:00
parent 12705f70c9
commit 77164e4cc0
3 changed files with 163 additions and 3 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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));