summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Erik Nilsen <bjorn.nilsen@nokia.com>2009-04-29 14:42:07 (GMT)
committerBjoern Erik Nilsen <bjorn.nilsen@nokia.com>2009-04-29 15:43:57 (GMT)
commit5a9623cb54c598aa4226883076fb413ef88215e5 (patch)
tree05ef92a0b9409d3eb93567d21c3c07baa88987ec
parent48d9f395a2d2d0e9e4ff61b69c8c7e725aa3e3fd (diff)
downloadQt-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.cpp9
-rw-r--r--tests/auto/qwidget/tst_qwidget.cpp102
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?");