summaryrefslogtreecommitdiffstats
path: root/tests/auto/opengl/qgl/tst_qgl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/opengl/qgl/tst_qgl.cpp')
-rw-r--r--tests/auto/opengl/qgl/tst_qgl.cpp2525
1 files changed, 2525 insertions, 0 deletions
diff --git a/tests/auto/opengl/qgl/tst_qgl.cpp b/tests/auto/opengl/qgl/tst_qgl.cpp
new file mode 100644
index 0000000000..d860caaa37
--- /dev/null
+++ b/tests/auto/opengl/qgl/tst_qgl.cpp
@@ -0,0 +1,2525 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qgl.h>
+#include <qglpixelbuffer.h>
+#include <qglframebufferobject.h>
+#include <qglcolormap.h>
+#include <qpaintengine.h>
+#include <qpainterpath.h>
+#include <qopenglfunctions.h>
+#include <qopenglframebufferobject.h>
+#include <qopenglpaintdevice.h>
+
+#include <QGraphicsView>
+#include <QGraphicsProxyWidget>
+#include <QVBoxLayout>
+
+#ifdef QT_BUILD_INTERNAL
+#include <qpa/qplatformpixmap.h>
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtGui/private/qimage_p.h>
+#include <QtGui/private/qimagepixmapcleanuphooks_p.h>
+#include <QtGui/private/qopenglextensions_p.h>
+#endif
+
+class tst_QGL : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QGL();
+ virtual ~tst_QGL();
+
+ static void initMain();
+
+private slots:
+ void initTestCase();
+ void getSetCheck();
+#ifdef QT_BUILD_INTERNAL
+ void qglContextDefaultBindTexture();
+ void openGLVersionCheck();
+ void shareRegister();
+ void textureCleanup();
+#endif
+ void partialGLWidgetUpdates_data();
+ void partialGLWidgetUpdates();
+ void glWidgetWithAlpha();
+ void glWidgetRendering();
+ void glFBOSimpleRendering();
+ void glFBORendering();
+ void currentFboSync();
+ void multipleFBOInterleavedRendering();
+ void glFBOUseInGLWidget();
+ void glPBufferRendering();
+ void glWidgetReparent();
+ void glWidgetRenderPixmap();
+ void colormap();
+ void fboFormat();
+ void testDontCrashOnDanglingResources();
+ void replaceClipping();
+ void clipTest();
+ void destroyFBOAfterContext();
+ void threadImages();
+ void nullRectCrash();
+ void graphicsViewClipping();
+ void extensions();
+};
+
+tst_QGL::tst_QGL()
+{
+}
+
+tst_QGL::~tst_QGL()
+{
+}
+
+void tst_QGL::initMain()
+{
+ QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
+}
+
+void tst_QGL::initTestCase()
+{
+ QGLWidget glWidget;
+ if (!glWidget.isValid())
+ QSKIP("QGL is not supported on the test system");
+}
+
+class MyGLContext : public QGLContext
+{
+public:
+ MyGLContext(const QGLFormat& format) : QGLContext(format) {}
+ bool windowCreated() const { return QGLContext::windowCreated(); }
+ void setWindowCreated(bool on) { QGLContext::setWindowCreated(on); }
+ bool initialized() const { return QGLContext::initialized(); }
+ void setInitialized(bool on) { QGLContext::setInitialized(on); }
+};
+
+class MyGLWidget : public QGLWidget
+{
+public:
+ MyGLWidget() : QGLWidget() {}
+ bool autoBufferSwap() const { return QGLWidget::autoBufferSwap(); }
+ void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); }
+};
+
+static int appDefaultDepth()
+{
+ static int depth = 0;
+ if (depth == 0) {
+ QPixmap pm(1, 1);
+ depth = pm.depth();
+ }
+ return depth;
+}
+
+// Using INT_MIN and INT_MAX will cause failures on systems
+// where "int" is 64-bit, so use the explicit values instead.
+#define TEST_INT_MIN (-2147483647 - 1)
+#define TEST_INT_MAX 2147483647
+
+// Testing get/set functions
+void tst_QGL::getSetCheck()
+{
+ QGLFormat obj1;
+ // int QGLFormat::depthBufferSize()
+ // void QGLFormat::setDepthBufferSize(int)
+ QCOMPARE(-1, obj1.depthBufferSize());
+ obj1.setDepthBufferSize(0);
+ QCOMPARE(0, obj1.depthBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648");
+ obj1.setDepthBufferSize(TEST_INT_MIN);
+ QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setDepthBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1");
+ obj1.setDepthBufferSize(-1);
+ QCOMPARE(3, obj1.depthBufferSize());
+ obj1.setDepthBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize());
+
+ // int QGLFormat::accumBufferSize()
+ // void QGLFormat::setAccumBufferSize(int)
+ QCOMPARE(-1, obj1.accumBufferSize());
+ obj1.setAccumBufferSize(0);
+ QCOMPARE(0, obj1.accumBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648");
+ obj1.setAccumBufferSize(TEST_INT_MIN);
+ QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setAccumBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1");
+ obj1.setAccumBufferSize(-1);
+ QCOMPARE(3, obj1.accumBufferSize());
+ obj1.setAccumBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize());
+
+ // int QGLFormat::redBufferSize()
+ // void QGLFormat::setRedBufferSize(int)
+ QCOMPARE(-1, obj1.redBufferSize());
+ obj1.setRedBufferSize(0);
+ QCOMPARE(0, obj1.redBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648");
+ obj1.setRedBufferSize(TEST_INT_MIN);
+ QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setRedBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1");
+ obj1.setRedBufferSize(-1);
+ QCOMPARE(3, obj1.redBufferSize());
+ obj1.setRedBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.redBufferSize());
+
+ // int QGLFormat::greenBufferSize()
+ // void QGLFormat::setGreenBufferSize(int)
+ QCOMPARE(-1, obj1.greenBufferSize());
+ obj1.setGreenBufferSize(0);
+ QCOMPARE(0, obj1.greenBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648");
+ obj1.setGreenBufferSize(TEST_INT_MIN);
+ QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setGreenBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1");
+ obj1.setGreenBufferSize(-1);
+ QCOMPARE(3, obj1.greenBufferSize());
+ obj1.setGreenBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize());
+
+ // int QGLFormat::blueBufferSize()
+ // void QGLFormat::setBlueBufferSize(int)
+ QCOMPARE(-1, obj1.blueBufferSize());
+ obj1.setBlueBufferSize(0);
+ QCOMPARE(0, obj1.blueBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648");
+ obj1.setBlueBufferSize(TEST_INT_MIN);
+ QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setBlueBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1");
+ obj1.setBlueBufferSize(-1);
+ QCOMPARE(3, obj1.blueBufferSize());
+ obj1.setBlueBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize());
+
+ // int QGLFormat::alphaBufferSize()
+ // void QGLFormat::setAlphaBufferSize(int)
+ QCOMPARE(-1, obj1.alphaBufferSize());
+ QCOMPARE(false, obj1.alpha());
+ QVERIFY(!obj1.testOption(QGL::AlphaChannel));
+ QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
+ obj1.setAlphaBufferSize(1);
+ QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha.
+ QCOMPARE(1, obj1.alphaBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648");
+ obj1.setAlphaBufferSize(TEST_INT_MIN);
+ QCOMPARE(1, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setAlphaBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1");
+ obj1.setAlphaBufferSize(-1);
+ QCOMPARE(3, obj1.alphaBufferSize());
+ obj1.setAlphaBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize());
+
+ // int QGLFormat::stencilBufferSize()
+ // void QGLFormat::setStencilBufferSize(int)
+ QCOMPARE(-1, obj1.stencilBufferSize());
+ obj1.setStencilBufferSize(1);
+ QCOMPARE(1, obj1.stencilBufferSize());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648");
+ obj1.setStencilBufferSize(TEST_INT_MIN);
+ QCOMPARE(1, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size
+ obj1.setStencilBufferSize(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1");
+ obj1.setStencilBufferSize(-1);
+ QCOMPARE(3, obj1.stencilBufferSize());
+ obj1.setStencilBufferSize(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize());
+
+ // bool QGLFormat::sampleBuffers()
+ // void QGLFormat::setSampleBuffers(bool)
+ QCOMPARE(false, obj1.sampleBuffers());
+ QVERIFY(!obj1.testOption(QGL::SampleBuffers));
+ QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
+
+ obj1.setSampleBuffers(false);
+ QCOMPARE(false, obj1.sampleBuffers());
+ QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
+ obj1.setSampleBuffers(true);
+ QCOMPARE(true, obj1.sampleBuffers());
+ QVERIFY(obj1.testOption(QGL::SampleBuffers));
+
+ // int QGLFormat::samples()
+ // void QGLFormat::setSamples(int)
+ QCOMPARE(-1, obj1.samples());
+ obj1.setSamples(0);
+ QCOMPARE(0, obj1.samples());
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648");
+ obj1.setSamples(TEST_INT_MIN);
+ QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size
+ obj1.setSamples(3);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1");
+ obj1.setSamples(-1);
+ QCOMPARE(3, obj1.samples());
+ obj1.setSamples(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.samples());
+
+ // int QGLFormat::swapInterval()
+ // void QGLFormat::setSwapInterval(int)
+ QCOMPARE(-1, obj1.swapInterval());
+ obj1.setSwapInterval(0);
+ QCOMPARE(0, obj1.swapInterval());
+ obj1.setSwapInterval(TEST_INT_MIN);
+ QCOMPARE(TEST_INT_MIN, obj1.swapInterval());
+ obj1.setSwapInterval(-1);
+ QCOMPARE(-1, obj1.swapInterval());
+ obj1.setSwapInterval(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.swapInterval());
+
+ // bool QGLFormat::doubleBuffer()
+ // void QGLFormat::setDoubleBuffer(bool)
+ QCOMPARE(true, obj1.doubleBuffer());
+ QVERIFY(obj1.testOption(QGL::DoubleBuffer));
+ QVERIFY(!obj1.testOption(QGL::SingleBuffer));
+ obj1.setDoubleBuffer(false);
+ QCOMPARE(false, obj1.doubleBuffer());
+ QVERIFY(!obj1.testOption(QGL::DoubleBuffer));
+ QVERIFY(obj1.testOption(QGL::SingleBuffer));
+ obj1.setDoubleBuffer(true);
+ QCOMPARE(true, obj1.doubleBuffer());
+ QVERIFY(obj1.testOption(QGL::DoubleBuffer));
+ QVERIFY(!obj1.testOption(QGL::SingleBuffer));
+
+ // bool QGLFormat::depth()
+ // void QGLFormat::setDepth(bool)
+ QCOMPARE(true, obj1.depth());
+ QVERIFY(obj1.testOption(QGL::DepthBuffer));
+ QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
+ obj1.setDepth(false);
+ QCOMPARE(false, obj1.depth());
+ QVERIFY(!obj1.testOption(QGL::DepthBuffer));
+ QVERIFY(obj1.testOption(QGL::NoDepthBuffer));
+ obj1.setDepth(true);
+ QCOMPARE(true, obj1.depth());
+ QVERIFY(obj1.testOption(QGL::DepthBuffer));
+ QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
+
+ // bool QGLFormat::rgba()
+ // void QGLFormat::setRgba(bool)
+ QCOMPARE(true, obj1.rgba());
+ QVERIFY(obj1.testOption(QGL::Rgba));
+ QVERIFY(!obj1.testOption(QGL::ColorIndex));
+ obj1.setRgba(false);
+ QCOMPARE(false, obj1.rgba());
+ QVERIFY(!obj1.testOption(QGL::Rgba));
+ QVERIFY(obj1.testOption(QGL::ColorIndex));
+ obj1.setRgba(true);
+ QCOMPARE(true, obj1.rgba());
+ QVERIFY(obj1.testOption(QGL::Rgba));
+ QVERIFY(!obj1.testOption(QGL::ColorIndex));
+
+ // bool QGLFormat::alpha()
+ // void QGLFormat::setAlpha(bool)
+ QVERIFY(obj1.testOption(QGL::AlphaChannel));
+ QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
+ obj1.setAlpha(false);
+ QCOMPARE(false, obj1.alpha());
+ QVERIFY(!obj1.testOption(QGL::AlphaChannel));
+ QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
+ obj1.setAlpha(true);
+ QCOMPARE(true, obj1.alpha());
+ QVERIFY(obj1.testOption(QGL::AlphaChannel));
+ QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
+
+ // bool QGLFormat::accum()
+ // void QGLFormat::setAccum(bool)
+ obj1.setAccumBufferSize(0);
+ QCOMPARE(false, obj1.accum());
+ QVERIFY(!obj1.testOption(QGL::AccumBuffer));
+ QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
+ obj1.setAccum(false);
+ QCOMPARE(false, obj1.accum());
+ QVERIFY(!obj1.testOption(QGL::AccumBuffer));
+ QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
+ obj1.setAccum(true);
+ QCOMPARE(true, obj1.accum());
+ QVERIFY(obj1.testOption(QGL::AccumBuffer));
+ QVERIFY(!obj1.testOption(QGL::NoAccumBuffer));
+
+ // bool QGLFormat::stencil()
+ // void QGLFormat::setStencil(bool)
+ QCOMPARE(true, obj1.stencil());
+ QVERIFY(obj1.testOption(QGL::StencilBuffer));
+ QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
+ obj1.setStencil(false);
+ QCOMPARE(false, obj1.stencil());
+ QVERIFY(!obj1.testOption(QGL::StencilBuffer));
+ QVERIFY(obj1.testOption(QGL::NoStencilBuffer));
+ obj1.setStencil(true);
+ QCOMPARE(true, obj1.stencil());
+ QVERIFY(obj1.testOption(QGL::StencilBuffer));
+ QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
+
+ // bool QGLFormat::stereo()
+ // void QGLFormat::setStereo(bool)
+ QCOMPARE(false, obj1.stereo());
+ QVERIFY(!obj1.testOption(QGL::StereoBuffers));
+ QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
+ obj1.setStereo(false);
+ QCOMPARE(false, obj1.stereo());
+ QVERIFY(!obj1.testOption(QGL::StereoBuffers));
+ QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
+ obj1.setStereo(true);
+ QCOMPARE(true, obj1.stereo());
+ QVERIFY(obj1.testOption(QGL::StereoBuffers));
+ QVERIFY(!obj1.testOption(QGL::NoStereoBuffers));
+
+ // bool QGLFormat::directRendering()
+ // void QGLFormat::setDirectRendering(bool)
+ QCOMPARE(true, obj1.directRendering());
+ QVERIFY(obj1.testOption(QGL::DirectRendering));
+ QVERIFY(!obj1.testOption(QGL::IndirectRendering));
+ obj1.setDirectRendering(false);
+ QCOMPARE(false, obj1.directRendering());
+ QVERIFY(!obj1.testOption(QGL::DirectRendering));
+ QVERIFY(obj1.testOption(QGL::IndirectRendering));
+ obj1.setDirectRendering(true);
+ QCOMPARE(true, obj1.directRendering());
+ QVERIFY(obj1.testOption(QGL::DirectRendering));
+ QVERIFY(!obj1.testOption(QGL::IndirectRendering));
+
+ // bool QGLFormat::overlay()
+ // void QGLFormat::setOverlay(bool)
+ QCOMPARE(false, obj1.hasOverlay());
+ QVERIFY(!obj1.testOption(QGL::HasOverlay));
+ QVERIFY(obj1.testOption(QGL::NoOverlay));
+ obj1.setOverlay(false);
+ QCOMPARE(false, obj1.hasOverlay());
+ QVERIFY(!obj1.testOption(QGL::HasOverlay));
+ QVERIFY(obj1.testOption(QGL::NoOverlay));
+ obj1.setOverlay(true);
+ QCOMPARE(true, obj1.hasOverlay());
+ QVERIFY(obj1.testOption(QGL::HasOverlay));
+ QVERIFY(!obj1.testOption(QGL::NoOverlay));
+
+ // int QGLFormat::plane()
+ // void QGLFormat::setPlane(int)
+ QCOMPARE(0, obj1.plane());
+ obj1.setPlane(0);
+ QCOMPARE(0, obj1.plane());
+ obj1.setPlane(TEST_INT_MIN);
+ QCOMPARE(TEST_INT_MIN, obj1.plane());
+ obj1.setPlane(TEST_INT_MAX);
+ QCOMPARE(TEST_INT_MAX, obj1.plane());
+
+ // int QGLFormat::major/minorVersion()
+ // void QGLFormat::setVersion(int, int)
+ QCOMPARE(obj1.majorVersion(), 2);
+ QCOMPARE(obj1.minorVersion(), 0);
+ obj1.setVersion(3, 2);
+ QCOMPARE(obj1.majorVersion(), 3);
+ QCOMPARE(obj1.minorVersion(), 2);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 0.1");
+ obj1.setVersion(0, 1);
+ QCOMPARE(obj1.majorVersion(), 3);
+ QCOMPARE(obj1.minorVersion(), 2);
+ QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 3.-1");
+ obj1.setVersion(3, -1);
+ QCOMPARE(obj1.majorVersion(), 3);
+ QCOMPARE(obj1.minorVersion(), 2);
+ obj1.setVersion(TEST_INT_MAX, TEST_INT_MAX - 1);
+ QCOMPARE(obj1.majorVersion(), TEST_INT_MAX);
+ QCOMPARE(obj1.minorVersion(), TEST_INT_MAX - 1);
+
+
+ // operator== and operator!= for QGLFormat
+ QGLFormat format1;
+ QGLFormat format2;
+
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+ format1.setDoubleBuffer(false);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setDoubleBuffer(false);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setDepthBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setDepthBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setAccumBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setAccumBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setRedBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setRedBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setGreenBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setGreenBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setBlueBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setBlueBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setAlphaBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setAlphaBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setStencilBufferSize(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setStencilBufferSize(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setSamples(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setSamples(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setSwapInterval(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setSwapInterval(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setPlane(8);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setPlane(8);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setVersion(3, 2);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setVersion(3, 2);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setProfile(QGLFormat::CoreProfile);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setProfile(QGLFormat::CoreProfile);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ format1.setOption(QGL::NoDeprecatedFunctions);
+ QVERIFY(!(format1 == format2));
+ QVERIFY(format1 != format2);
+ format2.setOption(QGL::NoDeprecatedFunctions);
+ QCOMPARE(format1, format2);
+ QVERIFY(!(format1 != format2));
+
+ // Copy constructor and assignment for QGLFormat.
+ QGLFormat format3(format1);
+ QGLFormat format4;
+ QCOMPARE(format1, format3);
+ QVERIFY(format1 != format4);
+ format4 = format1;
+ QCOMPARE(format1, format4);
+
+ // Check that modifying a copy doesn't affect the original.
+ format3.setRedBufferSize(16);
+ format4.setPlane(16);
+ QCOMPARE(format1.redBufferSize(), 8);
+ QCOMPARE(format1.plane(), 8);
+
+ // Check the QGLFormat constructor that takes an option list.
+ QGLFormat format5
+ (QGL::DepthBuffer | QGL::StereoBuffers | QGL::ColorIndex, 3);
+ QVERIFY(format5.depth());
+ QVERIFY(format5.stereo());
+ QVERIFY(format5.doubleBuffer()); // From defaultFormat()
+ QVERIFY(!format5.hasOverlay()); // From defaultFormat()
+ QVERIFY(!format5.rgba());
+ QCOMPARE(format5.plane(), 3);
+
+ // The default format should be the same as QGLFormat().
+ QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
+
+ // Modify the default format and check that it was changed.
+ QGLFormat::setDefaultFormat(format1);
+ QCOMPARE(QGLFormat::defaultFormat(), format1);
+
+ // Restore the default format.
+ QGLFormat::setDefaultFormat(QGLFormat());
+ QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
+
+ // Check the default overlay format's expected values.
+ QGLFormat overlay(QGLFormat::defaultOverlayFormat());
+ QCOMPARE(overlay.depthBufferSize(), -1);
+ QCOMPARE(overlay.accumBufferSize(), -1);
+ QCOMPARE(overlay.redBufferSize(), -1);
+ QCOMPARE(overlay.greenBufferSize(), -1);
+ QCOMPARE(overlay.blueBufferSize(), -1);
+ QCOMPARE(overlay.alphaBufferSize(), -1);
+ QCOMPARE(overlay.samples(), -1);
+ QCOMPARE(overlay.swapInterval(), -1);
+ QCOMPARE(overlay.plane(), 1);
+ QVERIFY(!overlay.sampleBuffers());
+ QVERIFY(!overlay.doubleBuffer());
+ QVERIFY(!overlay.depth());
+ QVERIFY(!overlay.rgba());
+ QVERIFY(!overlay.alpha());
+ QVERIFY(!overlay.accum());
+ QVERIFY(!overlay.stencil());
+ QVERIFY(!overlay.stereo());
+ QVERIFY(overlay.directRendering()); // Only option that should be on.
+ QVERIFY(!overlay.hasOverlay()); // Overlay doesn't need an overlay!
+
+ // Modify the default overlay format and check that it was changed.
+ QGLFormat::setDefaultOverlayFormat(format1);
+ QCOMPARE(QGLFormat::defaultOverlayFormat(), format1);
+
+ // Restore the default overlay format.
+ QGLFormat::setDefaultOverlayFormat(overlay);
+ QCOMPARE(QGLFormat::defaultOverlayFormat(), overlay);
+
+ MyGLContext obj2(obj1);
+ // bool QGLContext::windowCreated()
+ // void QGLContext::setWindowCreated(bool)
+ obj2.setWindowCreated(false);
+ QCOMPARE(false, obj2.windowCreated());
+ obj2.setWindowCreated(true);
+ QCOMPARE(true, obj2.windowCreated());
+
+ // bool QGLContext::initialized()
+ // void QGLContext::setInitialized(bool)
+ obj2.setInitialized(false);
+ QCOMPARE(false, obj2.initialized());
+ obj2.setInitialized(true);
+ QCOMPARE(true, obj2.initialized());
+
+ MyGLWidget obj3;
+ // bool QGLWidget::autoBufferSwap()
+ // void QGLWidget::setAutoBufferSwap(bool)
+ obj3.setAutoBufferSwap(false);
+ QCOMPARE(false, obj3.autoBufferSwap());
+ obj3.setAutoBufferSwap(true);
+ QCOMPARE(true, obj3.autoBufferSwap());
+}
+
+#ifdef QT_BUILD_INTERNAL
+QT_BEGIN_NAMESPACE
+extern QGLFormat::OpenGLVersionFlags qOpenGLVersionFlagsFromString(const QString &versionString);
+QT_END_NAMESPACE
+#endif
+
+#ifdef QT_BUILD_INTERNAL
+void tst_QGL::openGLVersionCheck()
+{
+ QString versionString;
+ QGLFormat::OpenGLVersionFlags expectedFlag;
+ QGLFormat::OpenGLVersionFlags versionFlag;
+
+ versionString = "1.1 Irix 6.5";
+ expectedFlag = QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "1.2 Microsoft";
+ expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "1.2.1";
+ expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "1.3 NVIDIA";
+ expectedFlag = QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "1.4";
+ expectedFlag = QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "1.5 NVIDIA";
+ expectedFlag = QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "2.0.2 NVIDIA 87.62";
+ expectedFlag = QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "2.1 NVIDIA";
+ expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "2.1";
+ expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "OpenGL ES-CM 1.0 ATI";
+ expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "OpenGL ES-CL 1.0 ATI";
+ expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "OpenGL ES-CM 1.1 ATI";
+ expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "OpenGL ES-CL 1.1 ATI";
+ expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "OpenGL ES 2.0 ATI";
+ expectedFlag = QGLFormat::OpenGL_ES_Version_2_0;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ versionString = "3.0";
+ expectedFlag = QGLFormat::OpenGL_Version_3_0 | QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
+ versionFlag = qOpenGLVersionFlagsFromString(versionString);
+ QCOMPARE(versionFlag, expectedFlag);
+
+ QGLWidget glWidget;
+ glWidget.show();
+ glWidget.makeCurrent();
+
+ // This is unfortunately the only test we can make on the actual openGLVersionFlags()
+ // However, the complicated parts are in openGLVersionFlags(const QString &versionString)
+ // tested above
+
+#if defined(QT_OPENGL_ES_2)
+ QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
+#else
+ if (QOpenGLContext::currentContext()->isOpenGLES())
+ QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
+ else
+ QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1);
+#endif //defined(QT_OPENGL_ES_2)
+}
+#endif //QT_BUILD_INTERNAL
+
+static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1)
+{
+ static int maxFuzz = 1;
+ static bool maxFuzzSet = false;
+
+ // On 16 bpp systems, we need to allow for more fuzz:
+ if (!maxFuzzSet) {
+ maxFuzzSet = true;
+ if (appDefaultDepth() < 24)
+ maxFuzz = 32;
+ }
+
+ int redFuzz = qAbs(qRed(testPixel) - qRed(refPixel));
+ int greenFuzz = qAbs(qGreen(testPixel) - qGreen(refPixel));
+ int blueFuzz = qAbs(qBlue(testPixel) - qBlue(refPixel));
+ int alphaFuzz = qAbs(qAlpha(testPixel) - qAlpha(refPixel));
+
+ if (refPixel != 0 && testPixel == 0) {
+ QString msg;
+ if (x >= 0) {
+ msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)")
+ .arg(x).arg(y)
+ .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
+ } else {
+ msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)")
+ .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
+ }
+
+ QTest::qFail(msg.toLatin1(), file, line);
+ return false;
+ }
+
+ if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) {
+ QString msg;
+
+ if (x >= 0)
+ msg = QString("Pixel [%1,%2]: ").arg(x).arg(y);
+ else
+ msg = QString("Pixel ");
+
+ msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)")
+ .arg(maxFuzz)
+ .arg(qRed(testPixel)).arg(qGreen(testPixel)).arg(qBlue(testPixel)).arg(qAlpha(testPixel))
+ .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
+ QTest::qFail(msg.toLatin1(), file, line);
+ return false;
+ }
+ return true;
+}
+
+static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line)
+{
+ QCOMPARE(testImage.width(), referenceImage.width());
+ QCOMPARE(testImage.height(), referenceImage.height());
+
+ for (int y = 0; y < testImage.height(); y++) {
+ for (int x = 0; x < testImage.width(); x++) {
+ if (!fuzzyComparePixels(testImage.pixel(x, y), referenceImage.pixel(x, y), file, line, x, y)) {
+ // Might as well save the images for easier debugging:
+ referenceImage.save("referenceImage.png");
+ testImage.save("testImage.png");
+ return;
+ }
+ }
+ }
+}
+
+#define QFUZZY_COMPARE_IMAGES(A,B) \
+ fuzzyCompareImages(A, B, __FILE__, __LINE__)
+
+#define QFUZZY_COMPARE_PIXELS(A,B) \
+ fuzzyComparePixels(A, B, __FILE__, __LINE__)
+
+class UnclippedWidget : public QWidget
+{
+public:
+ bool painted;
+
+ UnclippedWidget()
+ : painted(false)
+ {
+ }
+
+ void paintEvent(QPaintEvent *)
+ {
+ QPainter p(this);
+ p.fillRect(rect().adjusted(-1000, -1000, 1000, 1000), Qt::black);
+
+ painted = true;
+ }
+};
+
+void tst_QGL::graphicsViewClipping()
+{
+ const int size = 64;
+ UnclippedWidget *widget = new UnclippedWidget;
+ widget->setFixedSize(size, size);
+
+ QGraphicsScene scene;
+
+ scene.addWidget(widget)->setPos(0, 0);
+
+ QGraphicsView view(&scene);
+ // Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
+ view.setWindowFlags(view.windowFlags() | Qt::Tool);
+ view.setBackgroundBrush(Qt::white);
+ view.resize(2*size, 2*size);
+
+ QGLWidget *viewport = new QGLWidget;
+ view.setViewport(viewport);
+ view.show();
+ qApp->setActiveWindow(&view);
+
+ if (!viewport->isValid())
+ return;
+
+ scene.setSceneRect(view.viewport()->rect());
+
+ QVERIFY(QTest::qWaitForWindowExposed(view.viewport()->windowHandle()));
+ #ifdef Q_OS_MAC
+ // The black rectangle jumps from the center to the upper left for some reason.
+ QTest::qWait(100);
+ #endif
+
+ QTRY_VERIFY(widget->painted);
+
+ QImage image = viewport->grabFrameBuffer();
+ QImage expected = image;
+
+ QPainter p(&expected);
+ p.fillRect(expected.rect(), Qt::white);
+ p.fillRect(QRect(0, 0, size, size), Qt::black);
+ p.end();
+
+ QFUZZY_COMPARE_IMAGES(image, expected);
+}
+
+void tst_QGL::partialGLWidgetUpdates_data()
+{
+ QTest::addColumn<bool>("doubleBufferedContext");
+ QTest::addColumn<bool>("autoFillBackground");
+ QTest::addColumn<bool>("supportsPartialUpdates");
+
+ QTest::newRow("Double buffered context") << true << true << false;
+ QTest::newRow("Double buffered context without auto-fill background") << true << false << false;
+ QTest::newRow("Single buffered context") << false << true << false;
+ QTest::newRow("Single buffered context without auto-fill background") << false << false << true;
+}
+
+void tst_QGL::partialGLWidgetUpdates()
+{
+ QFETCH(bool, doubleBufferedContext);
+ QFETCH(bool, autoFillBackground);
+ QFETCH(bool, supportsPartialUpdates);
+
+ class MyGLWidget : public QGLWidget
+ {
+ public:
+ QRegion paintEventRegion;
+ void paintEvent(QPaintEvent *e)
+ {
+ paintEventRegion = e->region();
+ }
+ };
+
+ QGLFormat format = QGLFormat::defaultFormat();
+ format.setDoubleBuffer(doubleBufferedContext);
+ QGLFormat::setDefaultFormat(format);
+
+ MyGLWidget widget;
+ widget.setFixedSize(150, 150);
+ widget.setAutoFillBackground(autoFillBackground);
+ widget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&widget));
+ QCoreApplication::processEvents(); // Process all queued paint events
+
+ if (widget.format().doubleBuffer() != doubleBufferedContext)
+ QSKIP("Platform does not support requested format");
+
+ widget.paintEventRegion = QRegion();
+ widget.repaint(50, 50, 50, 50);
+
+ if (supportsPartialUpdates)
+ QCOMPARE(widget.paintEventRegion, QRegion(50, 50, 50, 50));
+ else
+ QCOMPARE(widget.paintEventRegion, QRegion(widget.rect()));
+}
+
+
+// This tests that rendering to a QGLPBuffer using QPainter works.
+void tst_QGL::glPBufferRendering()
+{
+ if (!QGLPixelBuffer::hasOpenGLPbuffers())
+ QSKIP("QGLPixelBuffer not supported on this platform");
+
+ QGLPixelBuffer* pbuf = new QGLPixelBuffer(128, 128);
+
+ QPainter p;
+ bool begun = p.begin(pbuf);
+ QVERIFY(begun);
+
+ QPaintEngine::Type engineType = p.paintEngine()->type();
+ QVERIFY(engineType == QPaintEngine::OpenGL || engineType == QPaintEngine::OpenGL2);
+
+ p.fillRect(0, 0, 128, 128, Qt::red);
+ p.fillRect(32, 32, 64, 64, Qt::blue);
+ p.end();
+
+ QImage fb = pbuf->toImage();
+ delete pbuf;
+
+ QImage reference(128, 128, fb.format());
+ p.begin(&reference);
+ p.fillRect(0, 0, 128, 128, Qt::red);
+ p.fillRect(32, 32, 64, 64, Qt::blue);
+ p.end();
+
+ QFUZZY_COMPARE_IMAGES(fb, reference);
+}
+
+void tst_QGL::glWidgetWithAlpha()
+{
+ QGLWidget* w = new QGLWidget(QGLFormat(QGL::AlphaChannel));
+ w->show();
+ QVERIFY(QTest::qWaitForWindowExposed(w));
+
+ delete w;
+}
+
+
+void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height)
+{
+ QPainterPath intersectingPath;
+ intersectingPath.moveTo(0, 0);
+ intersectingPath.lineTo(100, 0);
+ intersectingPath.lineTo(0, 100);
+ intersectingPath.lineTo(100, 100);
+ intersectingPath.closeSubpath();
+
+ QPainterPath trianglePath;
+ trianglePath.moveTo(50, 0);
+ trianglePath.lineTo(100, 100);
+ trianglePath.lineTo(0, 100);
+ trianglePath.closeSubpath();
+
+ painter->setTransform(QTransform()); // reset xform
+ painter->fillRect(-1, -1, width+2, height+2, Qt::red); // Background
+ painter->translate(14, 14);
+ painter->fillPath(intersectingPath, Qt::blue); // Test stencil buffer works
+ painter->translate(128, 0);
+ painter->setClipPath(trianglePath); // Test depth buffer works
+ painter->setTransform(QTransform()); // reset xform ready for fill
+ painter->fillRect(-1, -1, width+2, height+2, Qt::green);
+}
+
+void qt_opengl_check_test_pattern(const QImage& img)
+{
+ // As we're doing more than trivial painting, we can't just compare to
+ // an image rendered with raster. Instead, we sample at well-defined
+ // test-points:
+ QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb());
+
+ QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb());
+}
+
+class GLWidget : public QGLWidget
+{
+public:
+ GLWidget(QWidget* p = 0)
+ : QGLWidget(p), beginOk(false), engineType(QPaintEngine::MaxUser) {}
+ bool beginOk;
+ QPaintEngine::Type engineType;
+ void paintGL()
+ {
+ QPainter p;
+ beginOk = p.begin(this);
+ QPaintEngine* pe = p.paintEngine();
+ engineType = pe->type();
+
+ qt_opengl_draw_test_pattern(&p, width(), height());
+
+ // No p.end() or swap buffers, should be done automatically
+ }
+
+};
+
+void tst_QGL::glWidgetRendering()
+{
+ GLWidget w;
+ w.resize(256, 128);
+ w.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&w));
+
+ QVERIFY(w.beginOk);
+ QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2);
+
+#if defined(Q_OS_QNX)
+ // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
+ // after a buffer swap. This is why we have to swap the buffer explicitly before calling
+ // grabFrameBuffer to retrieve the content of the front buffer.
+ w.swapBuffers();
+#endif
+ QImage fb = w.grabFrameBuffer(false);
+ qt_opengl_check_test_pattern(fb);
+}
+
+void tst_QGL::glFBOSimpleRendering()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ // No multisample with combined depth/stencil attachment:
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
+
+ QGLFramebufferObject *fbo = new QGLFramebufferObject(200, 100, fboFormat);
+
+ fbo->bind();
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glClearColor(1.0, 0.0, 0.0, 1.0);
+ funcs->glClear(GL_COLOR_BUFFER_BIT);
+ funcs->glFinish();
+
+ QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
+ QImage reference(fb.size(), QImage::Format_RGB32);
+ reference.fill(0xffff0000);
+
+ QFUZZY_COMPARE_IMAGES(fb, reference);
+
+ delete fbo;
+}
+
+// NOTE: This tests that CombinedDepthStencil attachment works by assuming the
+// GL2 engine is being used and is implemented the same way as it was when
+// this autotest was written. If this is not the case, there may be some
+// false-positives: I.e. The test passes when either the depth or stencil
+// buffer is actually missing. But that's probably ok anyway.
+void tst_QGL::glFBORendering()
+{
+#if defined(Q_OS_QNX)
+ QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
+#endif
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ // No multisample with combined depth/stencil attachment:
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+
+ // Don't complicate things by using NPOT:
+ QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
+
+ if (fbo->attachment() != QGLFramebufferObject::CombinedDepthStencil) {
+ delete fbo;
+ QSKIP("FBOs missing combined depth~stencil support");
+ }
+
+ QPainter fboPainter;
+ bool painterBegun = fboPainter.begin(fbo);
+ QVERIFY(painterBegun);
+
+ qt_opengl_draw_test_pattern(&fboPainter, fbo->width(), fbo->height());
+
+ fboPainter.end();
+
+ QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
+ delete fbo;
+
+ qt_opengl_check_test_pattern(fb);
+}
+
+class QOpenGLFramebufferObjectPaintDevice : public QOpenGLPaintDevice
+{
+public:
+ QOpenGLFramebufferObjectPaintDevice(int width, int height)
+ : QOpenGLPaintDevice(width, height)
+ , m_fbo(width, height, QOpenGLFramebufferObject::CombinedDepthStencil)
+ {
+ }
+
+ void ensureActiveTarget()
+ {
+ m_fbo.bind();
+ }
+
+ QImage toImage() const
+ {
+ return m_fbo.toImage();
+ }
+
+private:
+ QOpenGLFramebufferObject m_fbo;
+};
+
+void tst_QGL::currentFboSync()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+#if defined(Q_OS_QNX)
+ QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
+#endif
+
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ // For some reason we offer inter-operatibility between QGL and QOpenGL
+ // paint engines. (?!) Let's check if the two engines can be used to perform
+ // drawing in turns on different targets within the same context.
+
+ {
+ QGLFramebufferObject fbo1(256, 256, QGLFramebufferObject::CombinedDepthStencil);
+
+ QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
+
+ QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
+ QPainter sourcePainter(&sourceImage);
+ qt_opengl_draw_test_pattern(&sourcePainter, 256, 256);
+
+ QPainter fbo1Painter(&fbo1);
+
+ QPainter fbo2Painter(&fbo2);
+ fbo2Painter.drawImage(0, 0, sourceImage);
+ fbo2Painter.end();
+
+ QImage fbo2Image = fbo2.toImage();
+
+ fbo1Painter.drawImage(0, 0, sourceImage);
+ fbo1Painter.end();
+
+ QGLFramebufferObject::bindDefault();
+
+ // Convert the QGLFBO's result since QOpenGLFBO uses a wider
+ // variety of possible return formats.
+ QCOMPARE(fbo1.toImage().convertToFormat(fbo2Image.format()), fbo2Image);
+ }
+
+ {
+ QGLFramebufferObject fbo1(512, 512, QGLFramebufferObject::CombinedDepthStencil);
+
+ QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
+
+ QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
+ QPainter sourcePainter(&sourceImage);
+ qt_opengl_draw_test_pattern(&sourcePainter, 256, 256);
+
+ QPainter fbo2Painter(&fbo2);
+ fbo2Painter.drawImage(0, 0, sourceImage);
+ QImage fbo2Image1 = fbo2.toImage();
+ fbo2Painter.fillRect(0, 0, 256, 256, Qt::white);
+
+ QPainter fbo1Painter(&fbo1);
+ fbo1Painter.drawImage(0, 0, sourceImage);
+ fbo1Painter.end();
+
+ // check that the OpenGL paint engine now knows it needs to sync
+ fbo2Painter.drawImage(0, 0, sourceImage);
+ QImage fbo2Image2 = fbo2.toImage();
+
+ fbo2Painter.end();
+
+ QCOMPARE(fbo2Image1, fbo2Image2);
+ }
+}
+
+// Tests multiple QPainters active on different FBOs at the same time, with
+// interleaving painting. Performance-wise, this is sub-optimal, but it still
+// has to work flawlessly
+void tst_QGL::multipleFBOInterleavedRendering()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ // No multisample with combined depth/stencil attachment:
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+
+ QGLFramebufferObject *fbo1 = new QGLFramebufferObject(256, 128, fboFormat);
+ QGLFramebufferObject *fbo2 = new QGLFramebufferObject(256, 128, fboFormat);
+ QGLFramebufferObject *fbo3 = new QGLFramebufferObject(256, 128, fboFormat);
+
+ if ( (fbo1->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
+ (fbo2->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
+ (fbo3->attachment() != QGLFramebufferObject::CombinedDepthStencil) )
+ {
+ delete fbo1;
+ delete fbo2;
+ delete fbo3;
+ QSKIP("FBOs missing combined depth~stencil support");
+ }
+
+ QPainter fbo1Painter;
+ QPainter fbo2Painter;
+ QPainter fbo3Painter;
+
+ QVERIFY(fbo1Painter.begin(fbo1));
+ QVERIFY(fbo2Painter.begin(fbo2));
+ QVERIFY(fbo3Painter.begin(fbo3));
+
+ // Confirm we're using the GL2 engine, as interleaved rendering isn't supported
+ // on the GL1 engine:
+ if (fbo1Painter.paintEngine()->type() != QPaintEngine::OpenGL2)
+ QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher");
+
+ QPainterPath intersectingPath;
+ intersectingPath.moveTo(0, 0);
+ intersectingPath.lineTo(100, 0);
+ intersectingPath.lineTo(0, 100);
+ intersectingPath.lineTo(100, 100);
+ intersectingPath.closeSubpath();
+
+ QPainterPath trianglePath;
+ trianglePath.moveTo(50, 0);
+ trianglePath.lineTo(100, 100);
+ trianglePath.lineTo(0, 100);
+ trianglePath.closeSubpath();
+
+ fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::red); // Background
+ fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::green); // Background
+ fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::blue); // Background
+
+ fbo1Painter.translate(14, 14);
+ fbo2Painter.translate(14, 14);
+ fbo3Painter.translate(14, 14);
+
+ fbo1Painter.fillPath(intersectingPath, Qt::blue); // Test stencil buffer works
+ fbo2Painter.fillPath(intersectingPath, Qt::red); // Test stencil buffer works
+ fbo3Painter.fillPath(intersectingPath, Qt::green); // Test stencil buffer works
+
+ fbo1Painter.translate(128, 0);
+ fbo2Painter.translate(128, 0);
+ fbo3Painter.translate(128, 0);
+
+ fbo1Painter.setClipPath(trianglePath);
+ fbo2Painter.setClipPath(trianglePath);
+ fbo3Painter.setClipPath(trianglePath);
+
+ fbo1Painter.setTransform(QTransform()); // reset xform
+ fbo2Painter.setTransform(QTransform()); // reset xform
+ fbo3Painter.setTransform(QTransform()); // reset xform
+
+ fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::green);
+ fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::blue);
+ fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::red);
+
+ fbo1Painter.end();
+ fbo2Painter.end();
+ fbo3Painter.end();
+
+ QImage fb1 = fbo1->toImage().convertToFormat(QImage::Format_RGB32);
+ QImage fb2 = fbo2->toImage().convertToFormat(QImage::Format_RGB32);
+ QImage fb3 = fbo3->toImage().convertToFormat(QImage::Format_RGB32);
+ delete fbo1;
+ delete fbo2;
+ delete fbo3;
+
+ // As we're doing more than trivial painting, we can't just compare to
+ // an image rendered with raster. Instead, we sample at well-defined
+ // test-points:
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(39, 64), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(89, 64), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 39), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 89), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(167, 39), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(217, 39), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb1.pixel(192, 64), QColor(Qt::green).rgb());
+
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(39, 64), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(89, 64), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 39), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 89), QColor(Qt::red).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(167, 39), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(217, 39), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb2.pixel(192, 64), QColor(Qt::blue).rgb());
+
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(39, 64), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(89, 64), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 39), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 89), QColor(Qt::green).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(167, 39), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(217, 39), QColor(Qt::blue).rgb());
+ QFUZZY_COMPARE_PIXELS(fb3.pixel(192, 64), QColor(Qt::red).rgb());
+}
+
+class FBOUseInGLWidget : public QGLWidget
+{
+public:
+ bool widgetPainterBeginOk;
+ bool fboPainterBeginOk;
+ QImage fboImage;
+protected:
+ void paintEvent(QPaintEvent*)
+ {
+ QPainter widgetPainter;
+ widgetPainterBeginOk = widgetPainter.begin(this);
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
+ QGLFramebufferObject *fbo = new QGLFramebufferObject(100, 100, fboFormat);
+
+ QPainter fboPainter;
+ fboPainterBeginOk = fboPainter.begin(fbo);
+ fboPainter.fillRect(-1, -1, 130, 130, Qt::red);
+ fboPainter.end();
+ fboImage = fbo->toImage();
+
+ widgetPainter.fillRect(-1, -1, width()+2, height()+2, Qt::blue);
+
+ delete fbo;
+ }
+
+};
+
+void tst_QGL::glFBOUseInGLWidget()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ FBOUseInGLWidget w;
+ w.resize(100, 100);
+ w.showNormal();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&w));
+
+ QVERIFY(w.widgetPainterBeginOk);
+ QVERIFY(w.fboPainterBeginOk);
+
+#if defined(Q_OS_QNX)
+ // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
+ // after a buffer swap. This is why we have to swap the buffer explicitly before calling
+ // grabFrameBuffer to retrieve the content of the front buffer
+ w.swapBuffers();
+#endif
+
+ QImage widgetFB = w.grabFrameBuffer(false);
+ QImage widgetReference(widgetFB.size(), widgetFB.format());
+ widgetReference.fill(0xff0000ff);
+ QFUZZY_COMPARE_IMAGES(widgetFB, widgetReference);
+
+ QImage fboReference(w.fboImage.size(), w.fboImage.format());
+ fboReference.fill(0xffff0000);
+ QFUZZY_COMPARE_IMAGES(w.fboImage, fboReference);
+}
+
+void tst_QGL::glWidgetReparent()
+{
+ // Try it as a top-level first:
+ GLWidget *widget = new GLWidget;
+ widget->setObjectName(QStringLiteral("glWidget1"));
+ widget->setGeometry(0, 0, 200, 30);
+ widget->show();
+
+ QWidget grandParentWidget;
+ grandParentWidget.setObjectName(QStringLiteral("grandParentWidget"));
+ grandParentWidget.setPalette(Qt::blue);
+ QVBoxLayout grandParentLayout(&grandParentWidget);
+
+ QWidget parentWidget(&grandParentWidget);
+ parentWidget.setObjectName(QStringLiteral("parentWidget"));
+ grandParentLayout.addWidget(&parentWidget);
+ parentWidget.setPalette(Qt::green);
+ parentWidget.setAutoFillBackground(true);
+ QVBoxLayout parentLayout(&parentWidget);
+
+ grandParentWidget.setGeometry(0, 100, 200, 200);
+ grandParentWidget.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(widget));
+ QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
+
+ QVERIFY(parentWidget.children().count() == 1); // The layout
+
+ // Now both widgets should be created & shown, time to re-parent:
+ parentLayout.addWidget(widget);
+
+ QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
+
+ QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
+ QVERIFY(parentWidget.children().contains(widget));
+ QTRY_VERIFY(widget->height() > 30);
+
+ delete widget;
+
+ QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
+
+ QVERIFY(parentWidget.children().count() == 1); // The layout
+
+ // Now do pretty much the same thing, but don't show the
+ // widget first:
+ widget = new GLWidget;
+ widget->setObjectName(QStringLiteral("glWidget2"));
+ parentLayout.addWidget(widget);
+
+ QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
+
+ QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
+ QVERIFY(parentWidget.children().contains(widget));
+ QVERIFY(widget->height() > 30);
+
+ delete widget;
+}
+
+class RenderPixmapWidget : public QGLWidget
+{
+protected:
+ void initializeGL() {
+ // Set some gl state:
+ QOpenGLContext::currentContext()->functions()->glClearColor(1.0, 0.0, 0.0, 1.0);
+ }
+
+ void paintGL() {
+ QOpenGLContext::currentContext()->functions()->glClear(GL_COLOR_BUFFER_BIT);
+ }
+};
+
+void tst_QGL::glWidgetRenderPixmap()
+{
+ RenderPixmapWidget *w = new RenderPixmapWidget;
+
+ QSize pmSize = QSize(100, 100);
+ QPixmap pm = w->renderPixmap(pmSize.width(), pmSize.height(), false);
+
+ delete w;
+
+ QImage fb = pm.toImage().convertToFormat(QImage::Format_RGB32);
+ QImage reference(pmSize, QImage::Format_RGB32);
+ reference.fill(0xffff0000);
+
+ QFUZZY_COMPARE_IMAGES(fb, reference);
+}
+
+class ColormapExtended : public QGLColormap
+{
+public:
+ ColormapExtended() {}
+
+ Qt::HANDLE handle() { return QGLColormap::handle(); }
+ void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); }
+};
+
+void tst_QGL::colormap()
+{
+ // Check the properties of the default empty colormap.
+ QGLColormap cmap1;
+ QVERIFY(cmap1.isEmpty());
+ QCOMPARE(cmap1.size(), 0);
+ QCOMPARE(cmap1.entryRgb(0), QRgb(0));
+ QCOMPARE(cmap1.entryRgb(-1), QRgb(0));
+ QCOMPARE(cmap1.entryRgb(100), QRgb(0));
+ QVERIFY(!cmap1.entryColor(0).isValid());
+ QVERIFY(!cmap1.entryColor(-1).isValid());
+ QVERIFY(!cmap1.entryColor(100).isValid());
+ QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1);
+ QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1);
+
+ // Set an entry and re-test.
+ cmap1.setEntry(56, qRgb(255, 0, 0));
+ // The colormap is still considered "empty" even though it
+ // has entries in it now. The isEmpty() method is used to
+ // detect when the colormap is in use by a GL widget,
+ // not to detect when it is empty!
+ QVERIFY(cmap1.isEmpty());
+ QCOMPARE(cmap1.size(), 256);
+ QCOMPARE(cmap1.entryRgb(0), QRgb(0));
+ QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255));
+ QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0));
+ QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255));
+ QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
+ QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
+
+ // Set some more entries.
+ static QRgb const colors[] = {
+ qRgb(255, 0, 0),
+ qRgb(0, 255, 0),
+ qRgb(255, 255, 255),
+ qRgb(0, 0, 255),
+ qRgb(0, 0, 0)
+ };
+ cmap1.setEntry(57, QColor(0, 255, 0));
+ cmap1.setEntries(3, colors + 2, 58);
+ cmap1.setEntries(5, colors, 251);
+ int idx;
+ for (idx = 0; idx < 5; ++idx) {
+ QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]);
+ QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx]));
+ QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]);
+ QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx]));
+ }
+ QCOMPARE(cmap1.size(), 256);
+
+ // Perform color lookups.
+ QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
+ QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255.
+ QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0);
+ QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57);
+ QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58);
+ QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59);
+ QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1);
+ QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1);
+ QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1);
+ QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1);
+ QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1);
+ QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1);
+ QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60);
+ QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57);
+ QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59);
+ QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59);
+ QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0);
+ QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0);
+
+ // Make some copies of the colormap and check that they are the same.
+ QGLColormap cmap2(cmap1);
+ QGLColormap cmap3;
+ cmap3 = cmap1;
+ QVERIFY(cmap2.isEmpty());
+ QVERIFY(cmap3.isEmpty());
+ QCOMPARE(cmap2.size(), 256);
+ QCOMPARE(cmap3.size(), 256);
+ for (idx = 0; idx < 256; ++idx) {
+ QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
+ QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
+ }
+
+ // Modify an entry in one of the copies and recheck the original.
+ cmap2.setEntry(45, qRgb(255, 0, 0));
+ for (idx = 0; idx < 256; ++idx) {
+ if (idx != 45)
+ QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
+ else
+ QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0));
+ QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
+ }
+
+ // Check that setting the handle will cause isEmpty() to work right.
+ ColormapExtended cmap4;
+ cmap4.setEntry(56, qRgb(255, 0, 0));
+ QVERIFY(cmap4.isEmpty());
+ QCOMPARE(cmap4.size(), 256);
+ cmap4.setHandle(Qt::HANDLE(42));
+ QCOMPARE(cmap4.handle(), Qt::HANDLE(42));
+ QVERIFY(!cmap4.isEmpty());
+ QCOMPARE(cmap4.size(), 256);
+}
+
+#ifndef GL_TEXTURE_3D
+#define GL_TEXTURE_3D 0x806F
+#endif
+
+#ifndef GL_RGB16
+#define GL_RGB16 0x8054
+#endif
+
+void tst_QGL::fboFormat()
+{
+ // Check the initial conditions.
+ QGLFramebufferObjectFormat format1;
+ QCOMPARE(format1.samples(), 0);
+ QCOMPARE(format1.attachment(), QGLFramebufferObject::NoAttachment);
+ QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D));
+ int expectedFormat =
+#ifdef QT_OPENGL_ES_2
+ GL_RGBA;
+#else
+ QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8;
+#endif
+ QCOMPARE(int(format1.internalTextureFormat()), expectedFormat);
+
+ // Modify the values and re-check.
+ format1.setSamples(8);
+ format1.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format1.setTextureTarget(GL_TEXTURE_3D);
+ format1.setInternalTextureFormat(GL_RGB16);
+ QCOMPARE(format1.samples(), 8);
+ QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
+ QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
+ QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
+
+ // Make copies and check that they are the same.
+ QGLFramebufferObjectFormat format2(format1);
+ QGLFramebufferObjectFormat format3;
+ QCOMPARE(format2.samples(), 8);
+ QCOMPARE(format2.attachment(), QGLFramebufferObject::CombinedDepthStencil);
+ QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D));
+ QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16));
+ format3 = format1;
+ QCOMPARE(format3.samples(), 8);
+ QCOMPARE(format3.attachment(), QGLFramebufferObject::CombinedDepthStencil);
+ QCOMPARE(int(format3.textureTarget()), int(GL_TEXTURE_3D));
+ QCOMPARE(int(format3.internalTextureFormat()), int(GL_RGB16));
+
+ // Modify the copies and check that the original is unchanged.
+ format2.setSamples(9);
+ format3.setTextureTarget(GL_TEXTURE_2D);
+ QCOMPARE(format1.samples(), 8);
+ QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
+ QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
+ QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
+
+ // operator== and operator!= for QGLFramebufferObjectFormat.
+ QGLFramebufferObjectFormat format1c;
+ QGLFramebufferObjectFormat format2c;
+
+ QCOMPARE(format1c, format2c);
+ QVERIFY(!(format1c != format2c));
+ format1c.setSamples(8);
+ QVERIFY(!(format1c == format2c));
+ QVERIFY(format1c != format2c);
+ format2c.setSamples(8);
+ QCOMPARE(format1c, format2c);
+ QVERIFY(!(format1c != format2c));
+
+ format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ QVERIFY(!(format1c == format2c));
+ QVERIFY(format1c != format2c);
+ format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ QCOMPARE(format1c, format2c);
+ QVERIFY(!(format1c != format2c));
+
+ format1c.setTextureTarget(GL_TEXTURE_3D);
+ QVERIFY(!(format1c == format2c));
+ QVERIFY(format1c != format2c);
+ format2c.setTextureTarget(GL_TEXTURE_3D);
+ QCOMPARE(format1c, format2c);
+ QVERIFY(!(format1c != format2c));
+
+ format1c.setInternalTextureFormat(GL_RGB16);
+ QVERIFY(!(format1c == format2c));
+ QVERIFY(format1c != format2c);
+ format2c.setInternalTextureFormat(GL_RGB16);
+ QCOMPARE(format1c, format2c);
+ QVERIFY(!(format1c != format2c));
+
+ QGLFramebufferObjectFormat format3c(format1c);
+ QGLFramebufferObjectFormat format4c;
+ QCOMPARE(format1c, format3c);
+ QVERIFY(!(format1c != format3c));
+ format3c.setInternalTextureFormat(
+#ifdef QT_OPENGL_ES_2
+ GL_RGBA
+#else
+ QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
+#endif
+ );
+ QVERIFY(!(format1c == format3c));
+ QVERIFY(format1c != format3c);
+
+ format4c = format1c;
+ QCOMPARE(format1c, format4c);
+ QVERIFY(!(format1c != format4c));
+ format4c.setInternalTextureFormat(
+#ifdef QT_OPENGL_ES_2
+ GL_RGBA
+#else
+ QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
+#endif
+ );
+ QVERIFY(!(format1c == format4c));
+ QVERIFY(format1c != format4c);
+}
+
+void tst_QGL::testDontCrashOnDanglingResources()
+{
+ // We have a number of Q_GLOBAL_STATICS inside the Qt OpenGL
+ // module. This test is verify that we don't crash as a result of
+ // them calling into libgl on application shutdown.
+ QWidget *widget = new UnclippedWidget();
+ widget->show();
+ qApp->processEvents();
+ widget->hide();
+}
+
+class ReplaceClippingGLWidget : public QGLWidget
+{
+public:
+ void paint(QPainter *painter)
+ {
+ painter->fillRect(rect(), Qt::white);
+
+ QPainterPath path;
+ path.addRect(0, 0, 100, 100);
+ path.addRect(50, 50, 100, 100);
+
+ painter->setClipRect(0, 0, 150, 150);
+ painter->fillPath(path, Qt::red);
+
+ painter->translate(150, 150);
+ painter->setClipRect(0, 0, 150, 150);
+ painter->fillPath(path, Qt::red);
+ }
+
+protected:
+ void paintEvent(QPaintEvent*)
+ {
+ // clear the stencil with junk
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glStencilMask(0xFFFF);
+ funcs->glClearStencil(0xFFFF);
+ funcs->glDisable(GL_STENCIL_TEST);
+ funcs->glDisable(GL_SCISSOR_TEST);
+ funcs->glClear(GL_STENCIL_BUFFER_BIT);
+
+ QPainter painter(this);
+ paint(&painter);
+ }
+};
+
+void tst_QGL::replaceClipping()
+{
+ ReplaceClippingGLWidget glw;
+ glw.resize(300, 300);
+ glw.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&glw));
+
+ QImage reference(300, 300, QImage::Format_RGB32);
+ QPainter referencePainter(&reference);
+ glw.paint(&referencePainter);
+ referencePainter.end();
+
+#if defined(Q_OS_QNX)
+ // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
+ // after a buffer swap. This is why we have to swap the buffer explicitly before calling
+ // grabFrameBuffer to retrieve the content of the front buffer
+ glw.swapBuffers();
+#endif
+ const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
+
+ // Sample pixels in a grid pattern which avoids false failures due to
+ // off-by-one pixel errors on some buggy GL implementations
+ for (int x = 25; x < reference.width(); x += 50) {
+ for (int y = 25; y < reference.width(); y += 50) {
+ QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
+ }
+ }
+}
+
+class ClipTestGLWidget : public QGLWidget
+{
+public:
+ void paint(QPainter *painter)
+ {
+ painter->fillRect(-1, -1, width()+2, height()+2, Qt::white);
+ painter->setClipRect(10, 10, width()-20, height()-20);
+ painter->fillRect(rect(), Qt::cyan);
+
+ painter->save();
+ painter->setClipRect(10, 10, 100, 100, Qt::IntersectClip);
+
+ painter->fillRect(rect(), Qt::blue);
+
+ painter->save();
+ painter->setClipRect(10, 10, 50, 50, Qt::IntersectClip);
+ painter->fillRect(rect(), Qt::red);
+ painter->restore();
+ painter->fillRect(0, 0, 40, 40, Qt::white);
+ painter->save();
+
+ painter->setClipRect(0, 0, 35, 35, Qt::IntersectClip);
+ painter->fillRect(rect(), Qt::black);
+ painter->restore();
+
+ painter->fillRect(0, 0, 30, 30, Qt::magenta);
+
+ painter->save();
+ painter->setClipRect(60, 10, 50, 50, Qt::ReplaceClip);
+ painter->fillRect(rect(), Qt::green);
+ painter->restore();
+
+ painter->restore();
+
+ painter->translate(100, 100);
+
+ {
+ QPainterPath path;
+ path.addRect(10, 10, 100, 100);
+ path.addRect(10, 10, 10, 10);
+ painter->setClipPath(path, Qt::IntersectClip);
+ }
+
+ painter->fillRect(rect(), Qt::blue);
+
+ painter->save();
+ {
+ QPainterPath path;
+ path.addRect(10, 10, 50, 50);
+ path.addRect(10, 10, 10, 10);
+ painter->setClipPath(path, Qt::IntersectClip);
+ }
+ painter->fillRect(rect(), Qt::red);
+ painter->restore();
+ painter->fillRect(0, 0, 40, 40, Qt::white);
+ painter->save();
+
+ {
+ QPainterPath path;
+ path.addRect(0, 0, 35, 35);
+ path.addRect(10, 10, 10, 10);
+ painter->setClipPath(path, Qt::IntersectClip);
+ }
+ painter->fillRect(rect(), Qt::black);
+ painter->restore();
+
+ painter->fillRect(0, 0, 30, 30, Qt::magenta);
+
+ painter->save();
+ {
+ QPainterPath path;
+ path.addRect(60, 10, 50, 50);
+ path.addRect(10, 10, 10, 10);
+ painter->setClipPath(path, Qt::ReplaceClip);
+ }
+ painter->fillRect(rect(), Qt::green);
+ painter->restore();
+ }
+
+protected:
+ void paintEvent(QPaintEvent*)
+ {
+ QPainter painter(this);
+ paint(&painter);
+ }
+};
+
+void tst_QGL::clipTest()
+{
+ ClipTestGLWidget glw;
+ glw.resize(220, 220);
+ glw.showNormal();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&glw));
+
+ QImage reference(glw.size(), QImage::Format_RGB32);
+ QPainter referencePainter(&reference);
+ glw.paint(&referencePainter);
+ referencePainter.end();
+
+#if defined(Q_OS_QNX)
+ // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
+ // after a buffer swap. This is why we have to swap the buffer explicitly before calling
+ // grabFrameBuffer to retrieve the content of the front buffer
+ glw.swapBuffers();
+#endif
+ const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
+
+ // Sample pixels in a grid pattern which avoids false failures due to
+ // off-by-one pixel errors on some buggy GL implementations
+ for (int x = 2; x < reference.width(); x += 5) {
+ for (int y = 2; y < reference.height(); y += 5) {
+ QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
+ }
+ }
+}
+
+void tst_QGL::destroyFBOAfterContext()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ QGLWidget *glw = new QGLWidget();
+ glw->makeCurrent();
+
+ // No multisample with combined depth/stencil attachment:
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+
+ // Don't complicate things by using NPOT:
+ QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
+
+ // The handle should be valid until the context is destroyed.
+ QVERIFY(fbo->handle() != 0);
+ QVERIFY(fbo->isValid());
+
+ delete glw;
+
+ // The handle should now be zero.
+ QVERIFY(!fbo->handle());
+ QVERIFY(!fbo->isValid());
+
+ delete fbo;
+}
+
+#ifdef QT_BUILD_INTERNAL
+
+class tst_QGLResource
+{
+public:
+ tst_QGLResource(const QGLContext * = 0) {}
+ ~tst_QGLResource() { ++deletions; }
+
+ static int deletions;
+};
+
+int tst_QGLResource::deletions = 0;
+
+#ifdef TODO
+Q_GLOBAL_STATIC(QOpenGLContextGroupResource<tst_QGLResource>, qt_shared_test)
+#endif //TODO
+#endif // QT_BUILD_INTERNAL
+
+#ifdef QT_BUILD_INTERNAL
+void tst_QGL::shareRegister()
+{
+#ifdef TODO
+ // Create a context.
+ QGLWidget *glw1 = new QGLWidget();
+ glw1->makeCurrent();
+
+ // Nothing should be sharing with glw1's context yet.
+ QVERIFY(!glw1->isSharing());
+
+ // Create a guard for the first context.
+ QOpenGLSharedResourceGuard guard(glw1->context()->contextHandle());
+ QCOMPARE(guard.id(), 0);
+ guard.setId(3);
+ QCOMPARE(guard.id(), 3);
+
+ // Request a tst_QGLResource object for the first context.
+ tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()->contextHandle());
+ QVERIFY(res1);
+ QCOMPARE(qt_shared_test()->value(glw1->context()->contextHandle()), res1);
+
+ // Create another context that shares with the first.
+ QVERIFY(!glw1->isSharing());
+ QGLWidget *glw2 = new QGLWidget(0, glw1);
+ if (!glw2->isSharing()) {
+ delete glw2;
+ delete glw1;
+ QSKIP("Context sharing is not supported");
+ }
+ QVERIFY(glw1->isSharing());
+ QVERIFY(glw1->context() != glw2->context());
+
+ // Check that the first context's resource is also on the second.
+ QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
+ QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
+
+ // Guard should still be the same.
+ QCOMPARE(guard.context(), glw1->context());
+ QCOMPARE(guard.id(), 3);
+
+ // Check the sharing relationships.
+ QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
+ QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
+ QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
+ QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
+ QVERIFY(!QGLContext::areSharing(0, glw2->context()));
+ QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
+ QVERIFY(!QGLContext::areSharing(0, 0));
+
+ // Create a third context, not sharing with the others.
+ QGLWidget *glw3 = new QGLWidget();
+ QVERIFY(!glw3->isSharing());
+
+ // Create a guard on the standalone context.
+ QGLSharedResourceGuard guard3(glw3->context());
+ guard3.setId(5);
+
+ // Request a resource to the third context.
+ tst_QGLResource *res3 = qt_shared_test()->value(glw3->context());
+ QVERIFY(res3);
+ QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
+ QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
+ QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
+
+ // Check the sharing relationships again.
+ QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
+ QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
+ QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
+ QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
+ QVERIFY(!QGLContext::areSharing(glw1->context(), glw3->context()));
+ QVERIFY(!QGLContext::areSharing(glw2->context(), glw3->context()));
+ QVERIFY(!QGLContext::areSharing(glw3->context(), glw1->context()));
+ QVERIFY(!QGLContext::areSharing(glw3->context(), glw2->context()));
+ QVERIFY(QGLContext::areSharing(glw3->context(), glw3->context()));
+ QVERIFY(!QGLContext::areSharing(0, glw2->context()));
+ QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
+ QVERIFY(!QGLContext::areSharing(0, glw3->context()));
+ QVERIFY(!QGLContext::areSharing(glw3->context(), 0));
+ QVERIFY(!QGLContext::areSharing(0, 0));
+
+ // Shared guard should still be the same.
+ QCOMPARE(guard.context(), glw1->context());
+ QCOMPARE(guard.id(), 3);
+
+ // Delete the first context.
+ delete glw1;
+
+ // The second context should no longer register as sharing.
+ QVERIFY(!glw2->isSharing());
+
+ // The first context's resource should transfer to the second context.
+ QCOMPARE(tst_QGLResource::deletions, 0);
+ QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
+ QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
+
+ // Shared guard should now be the second context, with the id the same.
+ QCOMPARE(guard.context(), glw2->context());
+ QCOMPARE(guard.id(), 3);
+ QCOMPARE(guard3.context(), glw3->context());
+ QCOMPARE(guard3.id(), 5);
+
+ // Clean up and check that the resources are properly deleted.
+ delete glw2;
+ QCOMPARE(tst_QGLResource::deletions, 1);
+ delete glw3;
+ QCOMPARE(tst_QGLResource::deletions, 2);
+
+ // Guards should now be null and the id zero.
+ QVERIFY(guard.context() == 0);
+ QVERIFY(guard.id() == 0);
+ QVERIFY(guard3.context() == 0);
+ QVERIFY(guard3.id() == 0);
+#endif //TODO
+}
+#endif
+
+// Tests QGLContext::bindTexture with default options
+#ifdef QT_BUILD_INTERNAL
+void tst_QGL::qglContextDefaultBindTexture()
+{
+ QGLWidget w;
+ w.makeCurrent();
+ QGLContext *ctx = const_cast<QGLContext*>(w.context());
+
+ QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
+ boundImage->fill(0xFFFFFFFF);
+ QPixmap *boundPixmap = new QPixmap(256, 256);
+ boundPixmap->fill(Qt::red);
+
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+
+ GLuint boundImageTextureId = ctx->bindTexture(*boundImage);
+ GLuint boundPixmapTextureId = ctx->bindTexture(*boundPixmap);
+
+ // Make sure the image & pixmap have been added to the cache:
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ // Make sure the image & pixmap have the is_cached flag set:
+ QVERIFY(QImagePixmapCleanupHooks::isImageCached(*boundImage));
+ QVERIFY(QImagePixmapCleanupHooks::isPixmapCached(*boundPixmap));
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ // Make sure the texture IDs returned are valid:
+ QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
+ QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
+
+ // Make sure the textures are still valid after we delete the image/pixmap:
+ // Also check that although the textures are left intact, the cache entries are removed:
+ delete boundImage;
+ boundImage = 0;
+ QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+ delete boundPixmap;
+ boundPixmap = 0;
+ QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ // Finally, make sure QGLContext::deleteTexture deletes the texture IDs:
+ ctx->deleteTexture(boundImageTextureId);
+ ctx->deleteTexture(boundPixmapTextureId);
+ QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_FALSE));
+ QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_FALSE));
+}
+#endif
+
+#ifdef QT_BUILD_INTERNAL
+void tst_QGL::textureCleanup()
+{
+ QGLWidget w;
+ w.resize(200,200);
+ w.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&w));
+ w.makeCurrent();
+
+ // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
+ // when the pixmap cache is cleared
+ {
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ QPainter p(&w);
+
+ QPixmap boundPixmap(":designer.png");
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawPixmap(0, 0, boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ // Check that the texture doesn't get removed from the cache when the pixmap is cleared
+ // as it should still be in the cache:
+ boundPixmap = QPixmap();
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ QPixmapCache::clear();
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+ }
+
+ // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
+ // when they are explicitly removed from the pixmap cache
+ {
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ QPainter p(&w);
+
+ QPixmap boundPixmap(128, 128);
+ QString cacheKey = QString::fromLatin1("myPixmap");
+ QPixmapCache::insert(cacheKey, boundPixmap);
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawPixmap(0, 0, boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ // Check that the texture doesn't get removed from the cache when the pixmap is cleared
+ // as it should still be in the cache:
+ boundPixmap = QPixmap();
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ // Finally, we check that the texture cache entry is removed when we remove the
+ // pixmap cache entry, which should hold the last reference:
+ QPixmapCache::remove(cacheKey);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+ }
+
+ // Check images & pixmaps are removed from the cache when they are deleted
+ {
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ QPainter p(&w);
+
+ QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
+ boundImage->fill(0xFFFFFFFF);
+ QPixmap *boundPixmap = new QPixmap(256, 256);
+ boundPixmap->fill(Qt::red);
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawImage(0, 0, *boundImage);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ p.drawPixmap(0, 0, *boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ delete boundImage;
+ boundImage = 0;
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ delete boundPixmap;
+ boundPixmap = 0;
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+ }
+
+ // Check images & pixmaps are removed from the cache when they are assigned to
+ {
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ QPainter p(&w);
+
+ QImage boundImage(256, 256, QImage::Format_RGB32);
+ boundImage.fill(0xFFFFFFFF);
+ QPixmap boundPixmap(256, 256);
+ boundPixmap.fill(Qt::red);
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawImage(0, 0, boundImage);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ p.drawPixmap(0, 0, boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ boundImage = QImage(64, 64, QImage::Format_RGB32);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ boundPixmap = QPixmap(64, 64);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+ }
+
+ // Check images & pixmaps are removed from the cache when they are modified (detached)
+ {
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ QPainter p(&w);
+
+ QImage boundImage(256, 256, QImage::Format_RGB32);
+ boundImage.fill(0xFFFFFFFF);
+ QPixmap boundPixmap(256, 256);
+ boundPixmap.fill(Qt::red);
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawImage(0, 0, boundImage);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ p.drawPixmap(0, 0, boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ boundImage.fill(0x00000000);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ boundPixmap.fill(Qt::blue);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+ }
+
+ // Check that images/pixmaps aren't removed from the cache if a shallow copy has been made
+ QImage copyOfImage;
+ QPixmap copyOfPixmap;
+ int startCacheItemCount = QGLTextureCache::instance()->size();
+ {
+ QPainter p(&w);
+
+ QImage boundImage(256, 256, QImage::Format_RGB32);
+ boundImage.fill(0xFFFFFFFF);
+ QPixmap boundPixmap(256, 256);
+ boundPixmap.fill(Qt::red);
+
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+
+ p.drawImage(0, 0, boundImage);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ p.drawPixmap(0, 0, boundPixmap);
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
+ p.end();
+
+ copyOfImage = boundImage;
+ copyOfPixmap = boundPixmap;
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+ } // boundImage & boundPixmap would have been deleted when they went out of scope
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
+
+ copyOfImage = QImage();
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
+
+ copyOfPixmap = QPixmap();
+ QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
+}
+#endif
+
+namespace ThreadImages {
+
+class Producer : public QObject
+{
+ Q_OBJECT
+public:
+ Producer()
+ {
+ startTimer(20);
+
+ QThread *thread = new QThread;
+ thread->start();
+
+ connect(this, SIGNAL(destroyed()), thread, SLOT(quit()));
+
+ moveToThread(thread);
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ }
+
+signals:
+ void imageReady(const QImage &image);
+
+protected:
+ void timerEvent(QTimerEvent *)
+ {
+ QImage image(256, 256, QImage::Format_RGB32);
+ QLinearGradient g(0, 0, 0, 256);
+ g.setColorAt(0, QColor(255, 180, 180));
+ g.setColorAt(1, Qt::white);
+ g.setSpread(QGradient::ReflectSpread);
+
+ QBrush brush(g);
+ brush.setTransform(QTransform::fromTranslate(0, delta));
+ delta += 10;
+
+ QPainter p(&image);
+ p.fillRect(image.rect(), brush);
+
+ if (images.size() > 10)
+ images.removeFirst();
+
+ images.append(image);
+
+ emit imageReady(image);
+ }
+
+private:
+ QList<QImage> images;
+ int delta;
+};
+
+
+class DisplayWidget : public QGLWidget
+{
+ Q_OBJECT
+public:
+ DisplayWidget(QWidget *parent) : QGLWidget(parent) {}
+ void paintEvent(QPaintEvent *)
+ {
+ QPainter p(this);
+ p.drawImage(rect(), m_image);
+ }
+
+public slots:
+ void setImage(const QImage &image)
+ {
+ m_image = image;
+ update();
+ }
+
+private:
+ QImage m_image;
+};
+
+class Widget : public QWidget
+{
+ Q_OBJECT
+public:
+ Widget()
+ : iterations(0)
+ , display(0)
+ , producer(new Producer)
+ {
+ startTimer(400);
+ connect(this, SIGNAL(destroyed()), producer, SLOT(deleteLater()));
+ }
+
+ int iterations;
+
+protected:
+ void timerEvent(QTimerEvent *)
+ {
+ ++iterations;
+
+ delete display;
+ display = new DisplayWidget(this);
+ connect(producer, SIGNAL(imageReady(QImage)), display, SLOT(setImage(QImage)));
+
+ display->setGeometry(rect());
+ display->show();
+ }
+
+private:
+ DisplayWidget *display;
+ Producer *producer;
+};
+
+}
+
+void tst_QGL::threadImages()
+{
+ ThreadImages::Widget *widget = new ThreadImages::Widget;
+ widget->show();
+
+ while (widget->iterations <= 5) {
+ qApp->processEvents();
+ }
+
+ delete widget;
+}
+
+void tst_QGL::nullRectCrash()
+{
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
+ QSKIP("QGLFramebufferObject not supported on this platform");
+
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ QGLFramebufferObjectFormat fboFormat;
+ fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+
+ QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat);
+
+ QPainter fboPainter(fbo);
+
+ fboPainter.setPen(QPen(QColor(255, 127, 127, 127), 2));
+ fboPainter.setBrush(QColor(127, 255, 127, 127));
+ fboPainter.drawRect(QRectF());
+
+ fboPainter.end();
+}
+
+void tst_QGL::extensions()
+{
+ QGLWidget glw;
+ glw.makeCurrent();
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QVERIFY(ctx);
+ QOpenGLFunctions *funcs = ctx->functions();
+ QVERIFY(funcs);
+ QSurfaceFormat format = ctx->format();
+
+#ifdef QT_BUILD_INTERNAL
+ QOpenGLExtensions *exts = static_cast<QOpenGLExtensions *>(funcs);
+ QOpenGLExtensions::OpenGLExtensions allExts = exts->openGLExtensions();
+ // Mipmapping is always available in GL2/GLES2+. Verify this.
+ if (format.majorVersion() >= 2)
+ QVERIFY(allExts.testFlag(QOpenGLExtensions::GenerateMipmap));
+#endif
+
+ // Now look for some features should always be available in a given version.
+ QOpenGLFunctions::OpenGLFeatures allFeatures = funcs->openGLFeatures();
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multitexture));
+ if (format.majorVersion() >= 2) {
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Shaders));
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Buffers));
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multisample));
+ QVERIFY(!ctx->isOpenGLES() || allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::NPOTTextures)
+ && allFeatures.testFlag(QOpenGLFunctions::NPOTTextureRepeat));
+ if (ctx->isOpenGLES()) {
+ QVERIFY(!allFeatures.testFlag(QOpenGLFunctions::FixedFunctionPipeline));
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
+ }
+ }
+ if (format.majorVersion() >= 3)
+ QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
+}
+
+QTEST_MAIN(tst_QGL)
+#include "tst_qgl.moc"