/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #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("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(); 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"