| /**************************************************************************** |
| ** |
| ** 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 <qtest.h> |
| |
| #include <QtQuick/qquickitem.h> |
| #include <QtQuick/qquickview.h> |
| #include <QtQuick/qsgnode.h> |
| #include <QtQuick/qsggeometry.h> |
| #include <QtQuick/qsgflatcolormaterial.h> |
| #include <QtGui/qscreen.h> |
| #include <QtGui/qopenglcontext.h> |
| |
| #include "../../shared/util.h" |
| |
| class tst_drawingmodes : public QQmlDataTest |
| { |
| Q_OBJECT |
| public: |
| tst_drawingmodes(); |
| |
| bool hasPixelAround(const QImage &fb, int centerX, int centerY); |
| QImage runTest(const QString &fileName) |
| { |
| QQuickView view(&outerWindow); |
| view.setResizeMode(QQuickView::SizeViewToRootObject); |
| view.setSource(testFileUrl(fileName)); |
| view.setVisible(true); |
| bool exposed = QTest::qWaitForWindowExposed(&view); |
| return exposed ? view.grabWindow() : QImage(); |
| } |
| |
| //It is important for platforms that only are able to show fullscreen windows |
| //to have a container for the window that is painted on. |
| QQuickWindow outerWindow; |
| const QRgb black; |
| const QRgb red; |
| |
| private slots: |
| void points(); |
| void lines(); |
| void lineStrip(); |
| void lineLoop(); |
| void triangles(); |
| void triangleStrip(); |
| void triangleFan(); |
| |
| private: |
| bool isRunningOnRhi() const; |
| }; |
| |
| class DrawingModeItem : public QQuickItem |
| { |
| Q_OBJECT |
| public: |
| static GLenum drawingMode; |
| |
| DrawingModeItem() : first(QSGGeometry::defaultAttributes_Point2D(), 5), |
| second(QSGGeometry::defaultAttributes_Point2D(), 5) |
| { |
| setFlag(ItemHasContents, true); |
| material.setColor(Qt::red); |
| } |
| |
| protected: |
| QSGGeometry first; |
| QSGGeometry second; |
| QSGFlatColorMaterial material; |
| |
| virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) |
| { |
| if (!node) { |
| QRect bounds(0, 0, 200, 200); |
| first.setDrawingMode(drawingMode); |
| second.setDrawingMode(drawingMode); |
| |
| QSGGeometry::Point2D *v = first.vertexDataAsPoint2D(); |
| v[0].set(bounds.width() * 2 / 8, bounds.height() / 2); |
| v[1].set(bounds.width() / 8, bounds.height() / 4); |
| v[2].set(bounds.width() * 3 / 8, bounds.height() / 4); |
| v[3].set(bounds.width() * 3 / 8, bounds.height() * 3 / 4); |
| v[4].set(bounds.width() / 8, bounds.height() * 3 / 4); |
| |
| v = second.vertexDataAsPoint2D(); |
| v[0].set(bounds.width() * 6 / 8, bounds.height() / 2); |
| v[1].set(bounds.width() * 5 / 8, bounds.height() / 4); |
| v[2].set(bounds.width() * 7 / 8, bounds.height() / 4); |
| v[3].set(bounds.width() * 7 / 8, bounds.height() * 3 / 4); |
| v[4].set(bounds.width() * 5 / 8, bounds.height() * 3 / 4); |
| |
| node = new QSGNode; |
| QSGGeometryNode *child = new QSGGeometryNode; |
| child->setGeometry(&first); |
| child->setMaterial(&material); |
| node->appendChildNode(child); |
| child = new QSGGeometryNode; |
| child->setGeometry(&second); |
| child->setMaterial(&material); |
| node->appendChildNode(child); |
| } |
| return node; |
| } |
| }; |
| |
| GLenum DrawingModeItem::drawingMode; |
| |
| bool tst_drawingmodes::hasPixelAround(const QImage &fb, int centerX, int centerY) { |
| for (int x = centerX - 2; x <= centerX + 2; ++x) { |
| for (int y = centerY - 2; y <= centerY + 2; ++y) { |
| if (fb.pixel(x, y) == red) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| tst_drawingmodes::tst_drawingmodes() : black(qRgb(0, 0, 0)), red(qRgb(0xff, 0, 0)) |
| { |
| qmlRegisterType<DrawingModeItem>("Test", 1, 0, "DrawingModeItem"); |
| outerWindow.showNormal(); |
| outerWindow.setGeometry(0,0,400,400); |
| } |
| |
| void tst_drawingmodes::points() |
| { |
| DrawingModeItem::drawingMode = GL_POINTS; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| #ifdef Q_OS_WIN |
| if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) |
| QSKIP("ANGLE cannot draw GL_POINTS."); |
| #endif |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QVERIFY(hasPixelAround(fb, 50, 100)); |
| QVERIFY(hasPixelAround(fb, 25, 50)); |
| QVERIFY(hasPixelAround(fb, 75, 50)); |
| QVERIFY(hasPixelAround(fb, 75, 150)); |
| QVERIFY(hasPixelAround(fb, 25, 150)); |
| |
| QVERIFY(hasPixelAround(fb, 150, 100)); |
| QVERIFY(hasPixelAround(fb, 125, 50)); |
| QVERIFY(hasPixelAround(fb, 175, 50)); |
| QVERIFY(hasPixelAround(fb, 175, 150)); |
| QVERIFY(hasPixelAround(fb, 125, 150)); |
| |
| QVERIFY(!hasPixelAround(fb, 135, 70)); |
| QVERIFY(!hasPixelAround(fb, 175, 100)); |
| QVERIFY(!hasPixelAround(fb, 110, 140)); |
| QVERIFY(!hasPixelAround(fb, 50, 50)); |
| QVERIFY(!hasPixelAround(fb, 50, 150)); |
| QVERIFY(!hasPixelAround(fb, 25, 100)); |
| QVERIFY(!hasPixelAround(fb, 75, 100)); |
| QVERIFY(!hasPixelAround(fb, 125, 100)); |
| QVERIFY(!hasPixelAround(fb, 150, 50)); |
| QVERIFY(!hasPixelAround(fb, 150, 150)); |
| QVERIFY(!hasPixelAround(fb, 135, 130)); |
| QVERIFY(!hasPixelAround(fb, 35, 130)); |
| } |
| |
| void tst_drawingmodes::lines() |
| { |
| DrawingModeItem::drawingMode = GL_LINES; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 135, 70)); |
| QVERIFY(hasPixelAround(fb, 175, 100)); |
| QVERIFY(!hasPixelAround(fb, 110, 140)); |
| QVERIFY(!hasPixelAround(fb, 50, 50)); |
| QVERIFY(!hasPixelAround(fb, 50, 150)); |
| |
| QVERIFY(hasPixelAround(fb, 35, 70)); |
| QVERIFY(hasPixelAround(fb, 75, 100)); |
| QVERIFY(!hasPixelAround(fb, 25, 100)); |
| QVERIFY(!hasPixelAround(fb, 125, 100)); |
| QVERIFY(!hasPixelAround(fb, 150, 50)); |
| QVERIFY(!hasPixelAround(fb, 150, 150)); |
| QVERIFY(!hasPixelAround(fb, 135, 130)); |
| QVERIFY(!hasPixelAround(fb, 35, 130)); |
| } |
| |
| void tst_drawingmodes::lineStrip() |
| { |
| DrawingModeItem::drawingMode = GL_LINE_STRIP; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 135, 70)); |
| QVERIFY(hasPixelAround(fb, 150, 50)); |
| QVERIFY(hasPixelAround(fb, 175, 100)); |
| QVERIFY(hasPixelAround(fb, 150, 150)); |
| |
| QVERIFY(hasPixelAround(fb, 35, 70)); |
| QVERIFY(hasPixelAround(fb, 50, 50)); |
| QVERIFY(hasPixelAround(fb, 75, 100)); |
| QVERIFY(hasPixelAround(fb, 50, 150)); |
| |
| QVERIFY(!hasPixelAround(fb, 110, 140)); // bad line not there => line strip unbatched |
| |
| QVERIFY(!hasPixelAround(fb, 25, 100)); |
| QVERIFY(!hasPixelAround(fb, 125, 100)); |
| QVERIFY(!hasPixelAround(fb, 135, 130)); |
| QVERIFY(!hasPixelAround(fb, 35, 130)); |
| } |
| |
| void tst_drawingmodes::lineLoop() |
| { |
| DrawingModeItem::drawingMode = GL_LINE_LOOP; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| if (isRunningOnRhi()) |
| QSKIP("Line loops are not supported by some modern graphics APIs - skipping test"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 135, 70)); |
| QVERIFY(hasPixelAround(fb, 135, 130)); |
| QVERIFY(hasPixelAround(fb, 150, 50)); |
| QVERIFY(hasPixelAround(fb, 175, 100)); |
| QVERIFY(hasPixelAround(fb, 150, 150)); |
| |
| QVERIFY(hasPixelAround(fb, 35, 70)); |
| QVERIFY(hasPixelAround(fb, 35, 130)); |
| QVERIFY(hasPixelAround(fb, 50, 50)); |
| QVERIFY(hasPixelAround(fb, 75, 100)); |
| QVERIFY(hasPixelAround(fb, 50, 150)); |
| |
| QVERIFY(!hasPixelAround(fb, 110, 140)); // bad line not there => line loop unbatched |
| |
| QVERIFY(!hasPixelAround(fb, 25, 100)); |
| QVERIFY(!hasPixelAround(fb, 125, 100)); |
| } |
| |
| void tst_drawingmodes::triangles() |
| { |
| DrawingModeItem::drawingMode = GL_TRIANGLES; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 150, 75)); |
| QVERIFY(!hasPixelAround(fb, 162, 100)); |
| QVERIFY(!hasPixelAround(fb, 150, 125)); |
| QVERIFY(!hasPixelAround(fb, 137, 100)); |
| |
| QVERIFY(!hasPixelAround(fb, 100, 125)); |
| |
| QVERIFY(hasPixelAround(fb, 50, 75)); |
| QVERIFY(!hasPixelAround(fb, 62, 100)); |
| QVERIFY(!hasPixelAround(fb, 50, 125)); |
| QVERIFY(!hasPixelAround(fb, 37, 100)); |
| } |
| |
| |
| void tst_drawingmodes::triangleStrip() |
| { |
| DrawingModeItem::drawingMode = GL_TRIANGLE_STRIP; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 150, 75)); |
| QVERIFY(hasPixelAround(fb, 162, 100)); |
| QVERIFY(hasPixelAround(fb, 150, 125)); |
| QVERIFY(!hasPixelAround(fb, 137, 100)); |
| |
| QVERIFY(!hasPixelAround(fb, 100, 125)); // batching avoids extra triangle by duplicating vertices. |
| |
| QVERIFY(hasPixelAround(fb, 50, 75)); |
| QVERIFY(hasPixelAround(fb, 62, 100)); |
| QVERIFY(hasPixelAround(fb, 50, 125)); |
| QVERIFY(!hasPixelAround(fb, 37, 100)); |
| } |
| |
| void tst_drawingmodes::triangleFan() |
| { |
| DrawingModeItem::drawingMode = GL_TRIANGLE_FAN; |
| if (QGuiApplication::primaryScreen()->depth() < 24) |
| QSKIP("This test does not work at display depths < 24"); |
| |
| if ((QGuiApplication::platformName() == QLatin1String("offscreen")) |
| || (QGuiApplication::platformName() == QLatin1String("minimal"))) |
| QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); |
| |
| if (isRunningOnRhi()) |
| QSKIP("Triangle fans are not supported by some modern graphics APIs - skipping test"); |
| |
| QImage fb = runTest("DrawingModes.qml"); |
| |
| QCOMPARE(fb.width(), 200); |
| QCOMPARE(fb.height(), 200); |
| |
| QVERIFY(hasPixelAround(fb, 150, 75)); |
| QVERIFY(hasPixelAround(fb, 162, 100)); |
| QVERIFY(hasPixelAround(fb, 150, 125)); |
| QVERIFY(!hasPixelAround(fb, 137, 100)); |
| |
| QVERIFY(!hasPixelAround(fb, 100, 125)); // no extra triangle; triangle fan is not batched |
| |
| QVERIFY(hasPixelAround(fb, 50, 75)); |
| QVERIFY(hasPixelAround(fb, 62, 100)); |
| QVERIFY(hasPixelAround(fb, 50, 125)); |
| QVERIFY(!hasPixelAround(fb, 37, 100)); |
| } |
| |
| bool tst_drawingmodes::isRunningOnRhi() const |
| { |
| static bool retval = false; |
| static bool decided = false; |
| if (!decided) { |
| decided = true; |
| QQuickView dummy; |
| dummy.show(); |
| if (QTest::qWaitForWindowExposed(&dummy)) { |
| QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi(); |
| retval = QSGRendererInterface::isApiRhiBased(api); |
| } |
| dummy.hide(); |
| } |
| return retval; |
| } |
| |
| |
| QTEST_MAIN(tst_drawingmodes) |
| |
| #include "tst_drawingmodes.moc" |