diff options
author | Bjoern Erik Nilsen <bjorn.nilsen@nokia.com> | 2009-04-29 14:42:07 (GMT) |
---|---|---|
committer | Bjoern Erik Nilsen <bjorn.nilsen@nokia.com> | 2009-04-29 15:43:57 (GMT) |
commit | 5a9623cb54c598aa4226883076fb413ef88215e5 (patch) | |
tree | 05ef92a0b9409d3eb93567d21c3c07baa88987ec | |
parent | 48d9f395a2d2d0e9e4ff61b69c8c7e725aa3e3fd (diff) | |
download | Qt-5a9623cb54c598aa4226883076fb413ef88215e5.zip Qt-5a9623cb54c598aa4226883076fb413ef88215e5.tar.gz Qt-5a9623cb54c598aa4226883076fb413ef88215e5.tar.bz2 |
Wrong clip in QWidget::render(QPainter *, ...) when using Qt::(Replace|No)Clip.
The problem was that we didn't take the painter's clip into account
when setting the system viewport ("hard clip"). We only used the system
clip, but we have to use system clip + painter clip, which is the
current engine clip. Unfortunately, we have to calculate it again
since there's no cross-platform way of retrieving it.
This was only a problem with Qt::(Replace|No)Clip, since we
in all other cases combine the old clip with the new one.
(Uber cool) auto test included.
Task-number: 250482
Reviewed-by: Samuel
-rw-r--r-- | src/gui/kernel/qwidget.cpp | 9 | ||||
-rw-r--r-- | tests/auto/qwidget/tst_qwidget.cpp | 102 |
2 files changed, 109 insertions, 2 deletions
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index f612601..fb9c8cb 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -4824,8 +4824,13 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, const QRegion oldSystemClip = enginePriv->systemClip; const QRegion oldSystemViewport = enginePriv->systemViewport; - // This ensures that transformed system clips are inside the current system clip. - enginePriv->setSystemViewport(oldSystemClip); + // This ensures that all painting triggered by render() is clipped to the current engine clip. + if (painter->hasClipping()) { + const QRegion painterClip = painter->deviceTransform().map(painter->clipRegion()); + enginePriv->setSystemViewport(oldSystemClip.isEmpty() ? painterClip : oldSystemClip & painterClip); + } else { + enginePriv->setSystemViewport(oldSystemClip); + } render(target, targetOffset, toBePainted, renderFlags); diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index 767553a..72ffcc3 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -285,6 +285,8 @@ private slots: void render_systemClip(); void render_systemClip2_data(); void render_systemClip2(); + void render_systemClip3_data(); + void render_systemClip3(); void setContentsMargins(); @@ -6995,6 +6997,106 @@ void tst_QWidget::render_systemClip2() } } +void tst_QWidget::render_systemClip3_data() +{ + QTest::addColumn<QSize>("size"); + QTest::addColumn<bool>("useSystemClip"); + + // Reference: http://en.wikipedia.org/wiki/Flag_of_Norway + QTest::newRow("Norwegian Civil Flag") << QSize(220, 160) << false; + QTest::newRow("Norwegian War Flag") << QSize(270, 160) << true; +} + +// This test ensures that the current engine clip (systemClip + painter clip) +// is preserved after QPainter::setClipRegion(..., Qt::ReplaceClip); +void tst_QWidget::render_systemClip3() +{ + QFETCH(QSize, size); + QFETCH(bool, useSystemClip); + + // Calculate the inner/outer cross of the flag. + QRegion outerCross(0, 0, size.width(), size.height()); + outerCross -= QRect(0, 0, 60, 60); + outerCross -= QRect(100, 0, size.width() - 100, 60); + outerCross -= QRect(0, 100, 60, 60); + outerCross -= QRect(100, 100, size.width() - 100, 60); + + QRegion innerCross(0, 0, size.width(), size.height()); + innerCross -= QRect(0, 0, 70, 70); + innerCross -= QRect(90, 0, size.width() - 90, 70); + innerCross -= QRect(0, 90, 70, 70); + innerCross -= QRect(90, 90, size.width() - 90, 70); + + const QRegion redArea(QRegion(0, 0, size.width(), size.height()) - outerCross); + const QRegion whiteArea(outerCross - innerCross); + const QRegion blueArea(innerCross); + QRegion systemClip; + + // Okay, here's the image that should look like a Norwegian civil/war flag in the end. + QImage flag(size, QImage::Format_ARGB32); + flag.fill(QColor(Qt::transparent).rgba()); + + if (useSystemClip) { + QPainterPath warClip(QPoint(size.width(), 0)); + warClip.lineTo(size.width() - 110, 60); + warClip.lineTo(size.width(), 80); + warClip.lineTo(size.width() - 110, 100); + warClip.lineTo(size.width(), 160); + warClip.closeSubpath(); + systemClip = QRegion(0, 0, size.width(), size.height()) - QRegion(warClip.toFillPolygon().toPolygon()); + flag.paintEngine()->setSystemClip(systemClip); + } + + QPainter painter(&flag); + painter.fillRect(QRect(QPoint(), size), Qt::red); // Fill image background with red. + painter.setClipRegion(outerCross); // Limit widget painting to inside the outer cross. + + // Here's the widget that's supposed to draw the inner/outer cross of the flag. + // The outer cross (white) should be drawn when the background is auto-filled, and + // the inner cross (blue) should be drawn in the paintEvent. + class MyWidget : public QWidget + { public: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + // Be evil and try to paint outside the outer cross. This should not be + // possible since the shared painter is clipped to the outer cross. + painter.setClipRect(0, 0, 60, 60, Qt::ReplaceClip); + painter.fillRect(rect(), Qt::green); + painter.setClipRegion(clip, Qt::ReplaceClip); + painter.fillRect(rect(), Qt::blue); + } + QRegion clip; + }; + + MyWidget widget; + widget.clip = innerCross; + widget.setFixedSize(size); + widget.setPalette(Qt::white); + widget.setAutoFillBackground(true); + widget.render(&painter); + +#ifdef RENDER_DEBUG + flag.save("flag.png"); +#endif + + // Let's make sure we got a Norwegian flag. + for (int i = 0; i < flag.height(); ++i) { + for (int j = 0; j < flag.width(); ++j) { + const QPoint pixel(j, i); + const QRgb pixelValue = flag.pixel(pixel); + if (useSystemClip && !systemClip.contains(pixel)) + QCOMPARE(pixelValue, QColor(Qt::transparent).rgba()); + else if (redArea.contains(pixel)) + QCOMPARE(pixelValue, QColor(Qt::red).rgba()); + else if (whiteArea.contains(pixel)) + QCOMPARE(pixelValue, QColor(Qt::white).rgba()); + else + QCOMPARE(pixelValue, QColor(Qt::blue).rgba()); + } + } +} + void tst_QWidget::setContentsMargins() { QLabel label("why does it always rain on me?"); |