diff options
author | Marc Mutz <[email protected]> | 2024-05-02 11:19:35 +0200 |
---|---|---|
committer | Marc Mutz <[email protected]> | 2024-05-24 04:38:21 +0000 |
commit | 00769990310ea2d5e89e56fcc1078c174a24d82d (patch) | |
tree | 4daa9c1d287d0259c7c326eff5b942849bd021fb | |
parent | f22e9795d9a32fc4e9f4d6f2a70c2f831028342b (diff) |
QRegion: re-add rects() and port setRects() to QSpan
In Qt 5, we had QVector<QRect> QRegion::rects(), but it was
deprecated, because just iterating over the QRegion as a container of
QRects was more efficient (QRegion has a SSO for the case of one
rectangle). With QSpan, we can now bring it back with the same
efficiency as iteration, supporting Qt 5 code that never made the move
away from rects() and new code that wishes to make the conversion into
rectangles more explicit. Re-add the Qt 5 tests, which show that the
function is nearly a drop-in replacement for the Qt 5 rects() (QSpan,
at the time of this commit, doesn't have relational operators, yet).
Also add a QSpan overload of setRects(). The old (ptr, n) function
(now obsoleted, but not deprecated) allowed nullptr + n != 0, which
QSpan doesn't accept, so print a warning in that case. Also, QSpan can
hold more than INT_MAX elements on 64-bit platforms, but QRegion's API
was never ported from int to qsizetype, so we need to catch oversized
spans used as inputs, too.
[ChangeLog][QtGui][QRegion] Added QSpan overload of setRects();
re-added Qt5's rects(), but returning QSpan instead of QVector now.
Fixes: QTBUG-124712
Change-Id: I24570c886cbf77abd8d1f4a3f42ae53c892cd9ff
Reviewed-by: Volker Hilsheimer <[email protected]>
Reviewed-by: Edward Welbourne <[email protected]>
-rw-r--r-- | src/gui/painting/qregion.cpp | 64 | ||||
-rw-r--r-- | src/gui/painting/qregion.h | 4 | ||||
-rw-r--r-- | tests/auto/gui/painting/qregion/tst_qregion.cpp | 24 |
3 files changed, 82 insertions, 10 deletions
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 8b712ee46d9..f9089d7bba2 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -876,9 +876,15 @@ QRegion QRegion::intersect(const QRect &r) const /*! \fn void QRegion::setRects(const QRect *rects, int number) + \overload + \obsolete Use the QSpan overload instead. +*/ + +/*! + \fn void QRegion::setRects(QSpan<const QRect> rects) + \since 6.8 - Sets the region using the array of rectangles specified by \a rects and - \a number. + Sets the region using the array of rectangles specified by \a rects. The rectangles \e must be optimally Y-X sorted and follow these restrictions: \list @@ -892,6 +898,11 @@ QRegion QRegion::intersect(const QRect &r) const \omit Only some platforms have these restrictions (Qt for Embedded Linux, X11 and \macos). \endomit + + \note For historical reasons, \c{rects.size()} must be less than \c{INT_MAX} + (see rectCount()). + + \sa rects() */ namespace { @@ -4214,18 +4225,39 @@ QRegion::const_iterator QRegion::end() const noexcept return d->qt_rgn ? d->qt_rgn->end() : nullptr; } -void QRegion::setRects(const QRect *rects, int num) +static Q_DECL_COLD_FUNCTION +void set_rects_warn(const char *what) +{ + qWarning("QRegion::setRects(): %s", what); +} + +void QRegion::setRects(const QRect *r, int n) { + if (!r && n) { // old setRects() allowed this, but QSpan doesn't + set_rects_warn("passing num != 0 when rects == nullptr is deprecated."); + n = 0; + } + setRects(QSpan<const QRect>(r, n)); +} + +void QRegion::setRects(QSpan<const QRect> rects) +{ + const auto num = int(rects.size()); + if (num != rects.size()) { + set_rects_warn("span size exceeds INT_MAX, ignoring"); + return; + } + *this = QRegion(); - if (!rects || num == 0 || (num == 1 && rects->isEmpty())) + if (!rects.data() || num == 0 || (num == 1 && rects.front().isEmpty())) return; detach(); d->qt_rgn->numRects = num; if (num == 1) { - d->qt_rgn->extents = *rects; - d->qt_rgn->innerRect = *rects; + d->qt_rgn->extents = rects.front(); + d->qt_rgn->innerRect = rects.front(); } else { d->qt_rgn->rects.resize(num); @@ -4246,12 +4278,30 @@ void QRegion::setRects(const QRect *rects, int num) } } +/*! + \since 6.8 + + Returns a span of non-overlapping rectangles that make up the region. The + span remains valid until the next call of a mutating (non-const) method on + this region. + + The union of all the rectangles is equal to the original region. + + \note This functions existed in Qt 5, too, but returned QVector<QRect> + instead. + + \sa setRects() +*/ +QSpan<const QRect> QRegion::rects() const noexcept +{ + return {begin(), end()}; +}; + int QRegion::rectCount() const noexcept { return (d->qt_rgn ? d->qt_rgn->numRects : 0); } - bool QRegion::operator==(const QRegion &r) const { if (!d->qt_rgn) diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index b0051b60676..4b852815f32 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -8,11 +8,11 @@ #include <QtCore/qatomic.h> #include <QtCore/qrect.h> #include <QtGui/qwindowdefs.h> -#include <QtCore/qcontainerfwd.h> #ifndef QT_NO_DATASTREAM #include <QtCore/qdatastream.h> #endif +#include <QtCore/qspan.h> QT_BEGIN_NAMESPACE @@ -75,6 +75,8 @@ public: QRect boundingRect() const noexcept; void setRects(const QRect *rect, int num); + void setRects(QSpan<const QRect> r); + QSpan<const QRect> rects() const noexcept; int rectCount() const noexcept; QRegion operator|(const QRegion &r) const; diff --git a/tests/auto/gui/painting/qregion/tst_qregion.cpp b/tests/auto/gui/painting/qregion/tst_qregion.cpp index 3d60e62fc1a..934725844a6 100644 --- a/tests/auto/gui/painting/qregion/tst_qregion.cpp +++ b/tests/auto/gui/painting/qregion/tst_qregion.cpp @@ -138,12 +138,15 @@ void tst_QRegion::rects() 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().size(), 1); + QCOMPARE(region.rects()[0], rect); } { QRect r(QPoint(10, 10), QPoint(40, 40)); @@ -190,6 +193,7 @@ void tst_QRegion::setRects() QCOMPARE(region, QRegion()); QCOMPARE(region.begin(), region.end()); QVERIFY(!region.boundingRect().isValid()); + QVERIFY(region.rects().isEmpty()); } { QRegion region; @@ -197,12 +201,15 @@ void tst_QRegion::setRects() region.setRects(&rect, 1); QCOMPARE(region.begin(), region.end()); QVERIFY(!region.boundingRect().isValid()); + QVERIFY(region.rects().isEmpty()); } { QRegion region; QRect rect(10, -20, 30, 40); region.setRects(&rect, 1); QCOMPARE(region.end(), region.begin() + 1); + QCOMPARE(region.rects().size(), 1); + QCOMPARE(region.rects()[0], rect); QCOMPARE(*region.begin(), rect); } } @@ -316,10 +323,12 @@ void tst_QRegion::emptyPolygonRegion() QRegion r(pa); QTEST(r.isEmpty(), "isEmpty"); QTEST(int(std::distance(r.begin(), r.end())), "numRects"); - QList<QRect> rects; - std::copy(r.begin(), r.end(), std::back_inserter(rects)); + QList<QRect> rects{r.begin(), r.end()}; QTEST(int(rects.size()), "numRects"); QTEST(rects, "rects"); + const auto span = r.rects(); + rects.assign(span.begin(), span.end()); + QTEST(rects, "rects"); } @@ -862,6 +871,7 @@ void tst_QRegion::isEmpty() QCOMPARE(region, QRegion()); QCOMPARE(region.rectCount(), 0); QCOMPARE(region.boundingRect(), QRect()); + QVERIFY(region.rects().isEmpty()); } void tst_QRegion::regionFromPath() @@ -877,6 +887,10 @@ void tst_QRegion::regionFromPath() 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()[0], QRect(0, 0, 10, 10)); + QCOMPARE(rgn.rects()[1], QRect(0, 100, 100, 1000)); + QCOMPARE(rgn.boundingRect(), QRect(0, 0, 100, 1100)); } @@ -893,6 +907,12 @@ void tst_QRegion::regionFromPath() 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()[0], QRect(0, 0, 100, 10)); + QCOMPARE(rgn.rects()[1], QRect(0, 10, 10, 80)); + QCOMPARE(rgn.rects()[2], QRect(90, 10, 10, 80)); + QCOMPARE(rgn.rects()[3], QRect(0, 90, 100, 10)); + QCOMPARE(rgn.boundingRect(), QRect(0, 0, 100, 100)); } } |