1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <qtest.h>
30
31#include <QtQuick/qquickitem.h>
32#include <QtQuick/qquickview.h>
33#include <QtQuick/qsgnode.h>
34#include <QtQuick/qsggeometry.h>
35#include <QtQuick/qsgflatcolormaterial.h>
36#include <QtGui/qscreen.h>
37#include <QtGui/qopenglcontext.h>
38
39#include "../../shared/util.h"
40
41class tst_drawingmodes : public QQmlDataTest
42{
43 Q_OBJECT
44public:
45 tst_drawingmodes();
46
47 bool hasPixelAround(const QImage &fb, int centerX, int centerY);
48 QImage runTest(const QString &fileName)
49 {
50 QQuickView view(&outerWindow);
51 view.setResizeMode(QQuickView::SizeViewToRootObject);
52 view.setSource(testFileUrl(fileName));
53 view.setVisible(true);
54 bool exposed = QTest::qWaitForWindowExposed(window: &view);
55 return exposed ? view.grabWindow() : QImage();
56 }
57
58 //It is important for platforms that only are able to show fullscreen windows
59 //to have a container for the window that is painted on.
60 QQuickWindow outerWindow;
61 const QRgb black;
62 const QRgb red;
63
64private slots:
65 void points();
66 void lines();
67 void lineStrip();
68 void lineLoop();
69 void triangles();
70 void triangleStrip();
71 void triangleFan();
72
73private:
74 bool isRunningOnRhi() const;
75};
76
77class DrawingModeItem : public QQuickItem
78{
79 Q_OBJECT
80public:
81 static GLenum drawingMode;
82
83 DrawingModeItem() : first(QSGGeometry::defaultAttributes_Point2D(), 5),
84 second(QSGGeometry::defaultAttributes_Point2D(), 5)
85 {
86 setFlag(flag: ItemHasContents, enabled: true);
87 material.setColor(Qt::red);
88 }
89
90protected:
91 QSGGeometry first;
92 QSGGeometry second;
93 QSGFlatColorMaterial material;
94
95 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
96 {
97 if (!node) {
98 QRect bounds(0, 0, 200, 200);
99 first.setDrawingMode(drawingMode);
100 second.setDrawingMode(drawingMode);
101
102 QSGGeometry::Point2D *v = first.vertexDataAsPoint2D();
103 v[0].set(nx: bounds.width() * 2 / 8, ny: bounds.height() / 2);
104 v[1].set(nx: bounds.width() / 8, ny: bounds.height() / 4);
105 v[2].set(nx: bounds.width() * 3 / 8, ny: bounds.height() / 4);
106 v[3].set(nx: bounds.width() * 3 / 8, ny: bounds.height() * 3 / 4);
107 v[4].set(nx: bounds.width() / 8, ny: bounds.height() * 3 / 4);
108
109 v = second.vertexDataAsPoint2D();
110 v[0].set(nx: bounds.width() * 6 / 8, ny: bounds.height() / 2);
111 v[1].set(nx: bounds.width() * 5 / 8, ny: bounds.height() / 4);
112 v[2].set(nx: bounds.width() * 7 / 8, ny: bounds.height() / 4);
113 v[3].set(nx: bounds.width() * 7 / 8, ny: bounds.height() * 3 / 4);
114 v[4].set(nx: bounds.width() * 5 / 8, ny: bounds.height() * 3 / 4);
115
116 node = new QSGNode;
117 QSGGeometryNode *child = new QSGGeometryNode;
118 child->setGeometry(&first);
119 child->setMaterial(&material);
120 node->appendChildNode(node: child);
121 child = new QSGGeometryNode;
122 child->setGeometry(&second);
123 child->setMaterial(&material);
124 node->appendChildNode(node: child);
125 }
126 return node;
127 }
128};
129
130GLenum DrawingModeItem::drawingMode;
131
132bool tst_drawingmodes::hasPixelAround(const QImage &fb, int centerX, int centerY) {
133 for (int x = centerX - 2; x <= centerX + 2; ++x) {
134 for (int y = centerY - 2; y <= centerY + 2; ++y) {
135 if (fb.pixel(x, y) == red)
136 return true;
137 }
138 }
139 return false;
140}
141
142tst_drawingmodes::tst_drawingmodes() : black(qRgb(r: 0, g: 0, b: 0)), red(qRgb(r: 0xff, g: 0, b: 0))
143{
144 qmlRegisterType<DrawingModeItem>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "DrawingModeItem");
145 outerWindow.showNormal();
146 outerWindow.setGeometry(posx: 0,posy: 0,w: 400,h: 400);
147}
148
149void tst_drawingmodes::points()
150{
151 DrawingModeItem::drawingMode = GL_POINTS;
152 if (QGuiApplication::primaryScreen()->depth() < 24)
153 QSKIP("This test does not work at display depths < 24");
154
155 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
156 || (QGuiApplication::platformName() == QLatin1String("minimal")))
157 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
158
159#ifdef Q_OS_WIN
160 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
161 QSKIP("ANGLE cannot draw GL_POINTS.");
162#endif
163
164 QImage fb = runTest(fileName: "DrawingModes.qml");
165
166 QVERIFY(hasPixelAround(fb, 50, 100));
167 QVERIFY(hasPixelAround(fb, 25, 50));
168 QVERIFY(hasPixelAround(fb, 75, 50));
169 QVERIFY(hasPixelAround(fb, 75, 150));
170 QVERIFY(hasPixelAround(fb, 25, 150));
171
172 QVERIFY(hasPixelAround(fb, 150, 100));
173 QVERIFY(hasPixelAround(fb, 125, 50));
174 QVERIFY(hasPixelAround(fb, 175, 50));
175 QVERIFY(hasPixelAround(fb, 175, 150));
176 QVERIFY(hasPixelAround(fb, 125, 150));
177
178 QVERIFY(!hasPixelAround(fb, 135, 70));
179 QVERIFY(!hasPixelAround(fb, 175, 100));
180 QVERIFY(!hasPixelAround(fb, 110, 140));
181 QVERIFY(!hasPixelAround(fb, 50, 50));
182 QVERIFY(!hasPixelAround(fb, 50, 150));
183 QVERIFY(!hasPixelAround(fb, 25, 100));
184 QVERIFY(!hasPixelAround(fb, 75, 100));
185 QVERIFY(!hasPixelAround(fb, 125, 100));
186 QVERIFY(!hasPixelAround(fb, 150, 50));
187 QVERIFY(!hasPixelAround(fb, 150, 150));
188 QVERIFY(!hasPixelAround(fb, 135, 130));
189 QVERIFY(!hasPixelAround(fb, 35, 130));
190}
191
192void tst_drawingmodes::lines()
193{
194 DrawingModeItem::drawingMode = GL_LINES;
195 if (QGuiApplication::primaryScreen()->depth() < 24)
196 QSKIP("This test does not work at display depths < 24");
197
198 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
199 || (QGuiApplication::platformName() == QLatin1String("minimal")))
200 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
201
202 QImage fb = runTest(fileName: "DrawingModes.qml");
203
204 QCOMPARE(fb.width(), 200);
205 QCOMPARE(fb.height(), 200);
206
207 QVERIFY(hasPixelAround(fb, 135, 70));
208 QVERIFY(hasPixelAround(fb, 175, 100));
209 QVERIFY(!hasPixelAround(fb, 110, 140));
210 QVERIFY(!hasPixelAround(fb, 50, 50));
211 QVERIFY(!hasPixelAround(fb, 50, 150));
212
213 QVERIFY(hasPixelAround(fb, 35, 70));
214 QVERIFY(hasPixelAround(fb, 75, 100));
215 QVERIFY(!hasPixelAround(fb, 25, 100));
216 QVERIFY(!hasPixelAround(fb, 125, 100));
217 QVERIFY(!hasPixelAround(fb, 150, 50));
218 QVERIFY(!hasPixelAround(fb, 150, 150));
219 QVERIFY(!hasPixelAround(fb, 135, 130));
220 QVERIFY(!hasPixelAround(fb, 35, 130));
221}
222
223void tst_drawingmodes::lineStrip()
224{
225 DrawingModeItem::drawingMode = GL_LINE_STRIP;
226 if (QGuiApplication::primaryScreen()->depth() < 24)
227 QSKIP("This test does not work at display depths < 24");
228
229 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
230 || (QGuiApplication::platformName() == QLatin1String("minimal")))
231 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
232
233 QImage fb = runTest(fileName: "DrawingModes.qml");
234
235 QCOMPARE(fb.width(), 200);
236 QCOMPARE(fb.height(), 200);
237
238 QVERIFY(hasPixelAround(fb, 135, 70));
239 QVERIFY(hasPixelAround(fb, 150, 50));
240 QVERIFY(hasPixelAround(fb, 175, 100));
241 QVERIFY(hasPixelAround(fb, 150, 150));
242
243 QVERIFY(hasPixelAround(fb, 35, 70));
244 QVERIFY(hasPixelAround(fb, 50, 50));
245 QVERIFY(hasPixelAround(fb, 75, 100));
246 QVERIFY(hasPixelAround(fb, 50, 150));
247
248 QVERIFY(!hasPixelAround(fb, 110, 140)); // bad line not there => line strip unbatched
249
250 QVERIFY(!hasPixelAround(fb, 25, 100));
251 QVERIFY(!hasPixelAround(fb, 125, 100));
252 QVERIFY(!hasPixelAround(fb, 135, 130));
253 QVERIFY(!hasPixelAround(fb, 35, 130));
254}
255
256void tst_drawingmodes::lineLoop()
257{
258 DrawingModeItem::drawingMode = GL_LINE_LOOP;
259 if (QGuiApplication::primaryScreen()->depth() < 24)
260 QSKIP("This test does not work at display depths < 24");
261
262 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
263 || (QGuiApplication::platformName() == QLatin1String("minimal")))
264 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
265
266 if (isRunningOnRhi())
267 QSKIP("Line loops are not supported by some modern graphics APIs - skipping test");
268
269 QImage fb = runTest(fileName: "DrawingModes.qml");
270
271 QCOMPARE(fb.width(), 200);
272 QCOMPARE(fb.height(), 200);
273
274 QVERIFY(hasPixelAround(fb, 135, 70));
275 QVERIFY(hasPixelAround(fb, 135, 130));
276 QVERIFY(hasPixelAround(fb, 150, 50));
277 QVERIFY(hasPixelAround(fb, 175, 100));
278 QVERIFY(hasPixelAround(fb, 150, 150));
279
280 QVERIFY(hasPixelAround(fb, 35, 70));
281 QVERIFY(hasPixelAround(fb, 35, 130));
282 QVERIFY(hasPixelAround(fb, 50, 50));
283 QVERIFY(hasPixelAround(fb, 75, 100));
284 QVERIFY(hasPixelAround(fb, 50, 150));
285
286 QVERIFY(!hasPixelAround(fb, 110, 140)); // bad line not there => line loop unbatched
287
288 QVERIFY(!hasPixelAround(fb, 25, 100));
289 QVERIFY(!hasPixelAround(fb, 125, 100));
290}
291
292void tst_drawingmodes::triangles()
293{
294 DrawingModeItem::drawingMode = GL_TRIANGLES;
295 if (QGuiApplication::primaryScreen()->depth() < 24)
296 QSKIP("This test does not work at display depths < 24");
297
298 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
299 || (QGuiApplication::platformName() == QLatin1String("minimal")))
300 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
301
302 QImage fb = runTest(fileName: "DrawingModes.qml");
303
304 QCOMPARE(fb.width(), 200);
305 QCOMPARE(fb.height(), 200);
306
307 QVERIFY(hasPixelAround(fb, 150, 75));
308 QVERIFY(!hasPixelAround(fb, 162, 100));
309 QVERIFY(!hasPixelAround(fb, 150, 125));
310 QVERIFY(!hasPixelAround(fb, 137, 100));
311
312 QVERIFY(!hasPixelAround(fb, 100, 125));
313
314 QVERIFY(hasPixelAround(fb, 50, 75));
315 QVERIFY(!hasPixelAround(fb, 62, 100));
316 QVERIFY(!hasPixelAround(fb, 50, 125));
317 QVERIFY(!hasPixelAround(fb, 37, 100));
318}
319
320
321void tst_drawingmodes::triangleStrip()
322{
323 DrawingModeItem::drawingMode = GL_TRIANGLE_STRIP;
324 if (QGuiApplication::primaryScreen()->depth() < 24)
325 QSKIP("This test does not work at display depths < 24");
326
327 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
328 || (QGuiApplication::platformName() == QLatin1String("minimal")))
329 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
330
331 QImage fb = runTest(fileName: "DrawingModes.qml");
332
333 QCOMPARE(fb.width(), 200);
334 QCOMPARE(fb.height(), 200);
335
336 QVERIFY(hasPixelAround(fb, 150, 75));
337 QVERIFY(hasPixelAround(fb, 162, 100));
338 QVERIFY(hasPixelAround(fb, 150, 125));
339 QVERIFY(!hasPixelAround(fb, 137, 100));
340
341 QVERIFY(!hasPixelAround(fb, 100, 125)); // batching avoids extra triangle by duplicating vertices.
342
343 QVERIFY(hasPixelAround(fb, 50, 75));
344 QVERIFY(hasPixelAround(fb, 62, 100));
345 QVERIFY(hasPixelAround(fb, 50, 125));
346 QVERIFY(!hasPixelAround(fb, 37, 100));
347}
348
349void tst_drawingmodes::triangleFan()
350{
351 DrawingModeItem::drawingMode = GL_TRIANGLE_FAN;
352 if (QGuiApplication::primaryScreen()->depth() < 24)
353 QSKIP("This test does not work at display depths < 24");
354
355 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
356 || (QGuiApplication::platformName() == QLatin1String("minimal")))
357 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
358
359 if (isRunningOnRhi())
360 QSKIP("Triangle fans are not supported by some modern graphics APIs - skipping test");
361
362 QImage fb = runTest(fileName: "DrawingModes.qml");
363
364 QCOMPARE(fb.width(), 200);
365 QCOMPARE(fb.height(), 200);
366
367 QVERIFY(hasPixelAround(fb, 150, 75));
368 QVERIFY(hasPixelAround(fb, 162, 100));
369 QVERIFY(hasPixelAround(fb, 150, 125));
370 QVERIFY(!hasPixelAround(fb, 137, 100));
371
372 QVERIFY(!hasPixelAround(fb, 100, 125)); // no extra triangle; triangle fan is not batched
373
374 QVERIFY(hasPixelAround(fb, 50, 75));
375 QVERIFY(hasPixelAround(fb, 62, 100));
376 QVERIFY(hasPixelAround(fb, 50, 125));
377 QVERIFY(!hasPixelAround(fb, 37, 100));
378}
379
380bool tst_drawingmodes::isRunningOnRhi() const
381{
382 static bool retval = false;
383 static bool decided = false;
384 if (!decided) {
385 decided = true;
386 QQuickView dummy;
387 dummy.show();
388 if (QTest::qWaitForWindowExposed(window: &dummy)) {
389 QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi();
390 retval = QSGRendererInterface::isApiRhiBased(api);
391 }
392 dummy.hide();
393 }
394 return retval;
395}
396
397
398QTEST_MAIN(tst_drawingmodes)
399
400#include "tst_drawingmodes.moc"
401

source code of qtdeclarative/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp