1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qcoreapplication.h>
48 #include <qglpixelbuffer.h>
49 #include <qglframebufferobject.h>
50 #include <qglcolormap.h>
51 #include <qpaintengine.h>
53 #include <QGraphicsView>
54 #include <QGraphicsProxyWidget>
55 #include <QVBoxLayout>
57 #ifdef QT_BUILD_INTERNAL
58 #include <qpa/qplatformpixmap.h>
59 #include <QtOpenGL/private/qgl_p.h>
60 #include <QtGui/private/qimage_p.h>
61 #include <QtGui/private/qimagepixmapcleanuphooks_p.h>
64 class tst_QGL : public QObject
75 #ifdef QT_BUILD_INTERNAL
76 void qglContextDefaultBindTexture();
77 void openGLVersionCheck();
79 void textureCleanup();
81 void graphicsViewClipping();
82 void partialGLWidgetUpdates_data();
83 void partialGLWidgetUpdates();
84 void glWidgetWithAlpha();
85 void glWidgetRendering();
86 void glFBOSimpleRendering();
87 void glFBORendering();
88 void multipleFBOInterleavedRendering();
89 void glFBOUseInGLWidget();
90 void glPBufferRendering();
91 void glWidgetReparent();
92 void glWidgetRenderPixmap();
95 void testDontCrashOnDanglingResources();
96 void replaceClipping();
98 void destroyFBOAfterContext();
100 void nullRectCrash();
111 void tst_QGL::initTestCase()
114 if (!glWidget.isValid())
115 QSKIP("QGL is not supported on the test system");
118 class MyGLContext : public QGLContext
121 MyGLContext(const QGLFormat& format) : QGLContext(format) {}
122 bool windowCreated() const { return QGLContext::windowCreated(); }
123 void setWindowCreated(bool on) { QGLContext::setWindowCreated(on); }
124 bool initialized() const { return QGLContext::initialized(); }
125 void setInitialized(bool on) { QGLContext::setInitialized(on); }
128 class MyGLWidget : public QGLWidget
131 MyGLWidget() : QGLWidget() {}
132 bool autoBufferSwap() const { return QGLWidget::autoBufferSwap(); }
133 void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); }
136 static int appDefaultDepth()
138 static int depth = 0;
146 // Using INT_MIN and INT_MAX will cause failures on systems
147 // where "int" is 64-bit, so use the explicit values instead.
148 #define TEST_INT_MIN (-2147483647 - 1)
149 #define TEST_INT_MAX 2147483647
151 // Testing get/set functions
152 void tst_QGL::getSetCheck()
155 // int QGLFormat::depthBufferSize()
156 // void QGLFormat::setDepthBufferSize(int)
157 QCOMPARE(-1, obj1.depthBufferSize());
158 obj1.setDepthBufferSize(0);
159 QCOMPARE(0, obj1.depthBufferSize());
160 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648");
161 obj1.setDepthBufferSize(TEST_INT_MIN);
162 QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size
163 obj1.setDepthBufferSize(3);
164 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1");
165 obj1.setDepthBufferSize(-1);
166 QCOMPARE(3, obj1.depthBufferSize());
167 obj1.setDepthBufferSize(TEST_INT_MAX);
168 QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize());
170 // int QGLFormat::accumBufferSize()
171 // void QGLFormat::setAccumBufferSize(int)
172 QCOMPARE(-1, obj1.accumBufferSize());
173 obj1.setAccumBufferSize(0);
174 QCOMPARE(0, obj1.accumBufferSize());
175 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648");
176 obj1.setAccumBufferSize(TEST_INT_MIN);
177 QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size
178 obj1.setAccumBufferSize(3);
179 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1");
180 obj1.setAccumBufferSize(-1);
181 QCOMPARE(3, obj1.accumBufferSize());
182 obj1.setAccumBufferSize(TEST_INT_MAX);
183 QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize());
185 // int QGLFormat::redBufferSize()
186 // void QGLFormat::setRedBufferSize(int)
187 QCOMPARE(-1, obj1.redBufferSize());
188 obj1.setRedBufferSize(0);
189 QCOMPARE(0, obj1.redBufferSize());
190 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648");
191 obj1.setRedBufferSize(TEST_INT_MIN);
192 QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size
193 obj1.setRedBufferSize(3);
194 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1");
195 obj1.setRedBufferSize(-1);
196 QCOMPARE(3, obj1.redBufferSize());
197 obj1.setRedBufferSize(TEST_INT_MAX);
198 QCOMPARE(TEST_INT_MAX, obj1.redBufferSize());
200 // int QGLFormat::greenBufferSize()
201 // void QGLFormat::setGreenBufferSize(int)
202 QCOMPARE(-1, obj1.greenBufferSize());
203 obj1.setGreenBufferSize(0);
204 QCOMPARE(0, obj1.greenBufferSize());
205 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648");
206 obj1.setGreenBufferSize(TEST_INT_MIN);
207 QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size
208 obj1.setGreenBufferSize(3);
209 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1");
210 obj1.setGreenBufferSize(-1);
211 QCOMPARE(3, obj1.greenBufferSize());
212 obj1.setGreenBufferSize(TEST_INT_MAX);
213 QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize());
215 // int QGLFormat::blueBufferSize()
216 // void QGLFormat::setBlueBufferSize(int)
217 QCOMPARE(-1, obj1.blueBufferSize());
218 obj1.setBlueBufferSize(0);
219 QCOMPARE(0, obj1.blueBufferSize());
220 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648");
221 obj1.setBlueBufferSize(TEST_INT_MIN);
222 QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size
223 obj1.setBlueBufferSize(3);
224 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1");
225 obj1.setBlueBufferSize(-1);
226 QCOMPARE(3, obj1.blueBufferSize());
227 obj1.setBlueBufferSize(TEST_INT_MAX);
228 QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize());
230 // int QGLFormat::alphaBufferSize()
231 // void QGLFormat::setAlphaBufferSize(int)
232 QCOMPARE(-1, obj1.alphaBufferSize());
233 QCOMPARE(false, obj1.alpha());
234 QVERIFY(!obj1.testOption(QGL::AlphaChannel));
235 QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
236 obj1.setAlphaBufferSize(1);
237 QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha.
238 QCOMPARE(1, obj1.alphaBufferSize());
239 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648");
240 obj1.setAlphaBufferSize(TEST_INT_MIN);
241 QCOMPARE(1, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size
242 obj1.setAlphaBufferSize(3);
243 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1");
244 obj1.setAlphaBufferSize(-1);
245 QCOMPARE(3, obj1.alphaBufferSize());
246 obj1.setAlphaBufferSize(TEST_INT_MAX);
247 QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize());
249 // int QGLFormat::stencilBufferSize()
250 // void QGLFormat::setStencilBufferSize(int)
251 QCOMPARE(-1, obj1.stencilBufferSize());
252 obj1.setStencilBufferSize(1);
253 QCOMPARE(1, obj1.stencilBufferSize());
254 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648");
255 obj1.setStencilBufferSize(TEST_INT_MIN);
256 QCOMPARE(1, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size
257 obj1.setStencilBufferSize(3);
258 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1");
259 obj1.setStencilBufferSize(-1);
260 QCOMPARE(3, obj1.stencilBufferSize());
261 obj1.setStencilBufferSize(TEST_INT_MAX);
262 QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize());
264 // bool QGLFormat::sampleBuffers()
265 // void QGLFormat::setSampleBuffers(bool)
266 QCOMPARE(false, obj1.sampleBuffers());
267 QVERIFY(!obj1.testOption(QGL::SampleBuffers));
268 QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
270 obj1.setSampleBuffers(false);
271 QCOMPARE(false, obj1.sampleBuffers());
272 QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
273 obj1.setSampleBuffers(true);
274 QCOMPARE(true, obj1.sampleBuffers());
275 QVERIFY(obj1.testOption(QGL::SampleBuffers));
277 // int QGLFormat::samples()
278 // void QGLFormat::setSamples(int)
279 QCOMPARE(-1, obj1.samples());
281 QCOMPARE(0, obj1.samples());
282 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648");
283 obj1.setSamples(TEST_INT_MIN);
284 QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size
286 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1");
288 QCOMPARE(3, obj1.samples());
289 obj1.setSamples(TEST_INT_MAX);
290 QCOMPARE(TEST_INT_MAX, obj1.samples());
292 // int QGLFormat::swapInterval()
293 // void QGLFormat::setSwapInterval(int)
294 QCOMPARE(-1, obj1.swapInterval());
295 obj1.setSwapInterval(0);
296 QCOMPARE(0, obj1.swapInterval());
297 obj1.setSwapInterval(TEST_INT_MIN);
298 QCOMPARE(TEST_INT_MIN, obj1.swapInterval());
299 obj1.setSwapInterval(-1);
300 QCOMPARE(-1, obj1.swapInterval());
301 obj1.setSwapInterval(TEST_INT_MAX);
302 QCOMPARE(TEST_INT_MAX, obj1.swapInterval());
304 // bool QGLFormat::doubleBuffer()
305 // void QGLFormat::setDoubleBuffer(bool)
306 QCOMPARE(true, obj1.doubleBuffer());
307 QVERIFY(obj1.testOption(QGL::DoubleBuffer));
308 QVERIFY(!obj1.testOption(QGL::SingleBuffer));
309 obj1.setDoubleBuffer(false);
310 QCOMPARE(false, obj1.doubleBuffer());
311 QVERIFY(!obj1.testOption(QGL::DoubleBuffer));
312 QVERIFY(obj1.testOption(QGL::SingleBuffer));
313 obj1.setDoubleBuffer(true);
314 QCOMPARE(true, obj1.doubleBuffer());
315 QVERIFY(obj1.testOption(QGL::DoubleBuffer));
316 QVERIFY(!obj1.testOption(QGL::SingleBuffer));
318 // bool QGLFormat::depth()
319 // void QGLFormat::setDepth(bool)
320 QCOMPARE(true, obj1.depth());
321 QVERIFY(obj1.testOption(QGL::DepthBuffer));
322 QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
323 obj1.setDepth(false);
324 QCOMPARE(false, obj1.depth());
325 QVERIFY(!obj1.testOption(QGL::DepthBuffer));
326 QVERIFY(obj1.testOption(QGL::NoDepthBuffer));
328 QCOMPARE(true, obj1.depth());
329 QVERIFY(obj1.testOption(QGL::DepthBuffer));
330 QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
332 // bool QGLFormat::rgba()
333 // void QGLFormat::setRgba(bool)
334 QCOMPARE(true, obj1.rgba());
335 QVERIFY(obj1.testOption(QGL::Rgba));
336 QVERIFY(!obj1.testOption(QGL::ColorIndex));
338 QCOMPARE(false, obj1.rgba());
339 QVERIFY(!obj1.testOption(QGL::Rgba));
340 QVERIFY(obj1.testOption(QGL::ColorIndex));
342 QCOMPARE(true, obj1.rgba());
343 QVERIFY(obj1.testOption(QGL::Rgba));
344 QVERIFY(!obj1.testOption(QGL::ColorIndex));
346 // bool QGLFormat::alpha()
347 // void QGLFormat::setAlpha(bool)
348 QVERIFY(obj1.testOption(QGL::AlphaChannel));
349 QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
350 obj1.setAlpha(false);
351 QCOMPARE(false, obj1.alpha());
352 QVERIFY(!obj1.testOption(QGL::AlphaChannel));
353 QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
355 QCOMPARE(true, obj1.alpha());
356 QVERIFY(obj1.testOption(QGL::AlphaChannel));
357 QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
359 // bool QGLFormat::accum()
360 // void QGLFormat::setAccum(bool)
361 obj1.setAccumBufferSize(0);
362 QCOMPARE(false, obj1.accum());
363 QVERIFY(!obj1.testOption(QGL::AccumBuffer));
364 QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
365 obj1.setAccum(false);
366 QCOMPARE(false, obj1.accum());
367 QVERIFY(!obj1.testOption(QGL::AccumBuffer));
368 QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
370 QCOMPARE(true, obj1.accum());
371 QVERIFY(obj1.testOption(QGL::AccumBuffer));
372 QVERIFY(!obj1.testOption(QGL::NoAccumBuffer));
374 // bool QGLFormat::stencil()
375 // void QGLFormat::setStencil(bool)
376 QCOMPARE(true, obj1.stencil());
377 QVERIFY(obj1.testOption(QGL::StencilBuffer));
378 QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
379 obj1.setStencil(false);
380 QCOMPARE(false, obj1.stencil());
381 QVERIFY(!obj1.testOption(QGL::StencilBuffer));
382 QVERIFY(obj1.testOption(QGL::NoStencilBuffer));
383 obj1.setStencil(true);
384 QCOMPARE(true, obj1.stencil());
385 QVERIFY(obj1.testOption(QGL::StencilBuffer));
386 QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
388 // bool QGLFormat::stereo()
389 // void QGLFormat::setStereo(bool)
390 QCOMPARE(false, obj1.stereo());
391 QVERIFY(!obj1.testOption(QGL::StereoBuffers));
392 QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
393 obj1.setStereo(false);
394 QCOMPARE(false, obj1.stereo());
395 QVERIFY(!obj1.testOption(QGL::StereoBuffers));
396 QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
397 obj1.setStereo(true);
398 QCOMPARE(true, obj1.stereo());
399 QVERIFY(obj1.testOption(QGL::StereoBuffers));
400 QVERIFY(!obj1.testOption(QGL::NoStereoBuffers));
402 // bool QGLFormat::directRendering()
403 // void QGLFormat::setDirectRendering(bool)
404 QCOMPARE(true, obj1.directRendering());
405 QVERIFY(obj1.testOption(QGL::DirectRendering));
406 QVERIFY(!obj1.testOption(QGL::IndirectRendering));
407 obj1.setDirectRendering(false);
408 QCOMPARE(false, obj1.directRendering());
409 QVERIFY(!obj1.testOption(QGL::DirectRendering));
410 QVERIFY(obj1.testOption(QGL::IndirectRendering));
411 obj1.setDirectRendering(true);
412 QCOMPARE(true, obj1.directRendering());
413 QVERIFY(obj1.testOption(QGL::DirectRendering));
414 QVERIFY(!obj1.testOption(QGL::IndirectRendering));
416 // bool QGLFormat::overlay()
417 // void QGLFormat::setOverlay(bool)
418 QCOMPARE(false, obj1.hasOverlay());
419 QVERIFY(!obj1.testOption(QGL::HasOverlay));
420 QVERIFY(obj1.testOption(QGL::NoOverlay));
421 obj1.setOverlay(false);
422 QCOMPARE(false, obj1.hasOverlay());
423 QVERIFY(!obj1.testOption(QGL::HasOverlay));
424 QVERIFY(obj1.testOption(QGL::NoOverlay));
425 obj1.setOverlay(true);
426 QCOMPARE(true, obj1.hasOverlay());
427 QVERIFY(obj1.testOption(QGL::HasOverlay));
428 QVERIFY(!obj1.testOption(QGL::NoOverlay));
430 // int QGLFormat::plane()
431 // void QGLFormat::setPlane(int)
432 QCOMPARE(0, obj1.plane());
434 QCOMPARE(0, obj1.plane());
435 obj1.setPlane(TEST_INT_MIN);
436 QCOMPARE(TEST_INT_MIN, obj1.plane());
437 obj1.setPlane(TEST_INT_MAX);
438 QCOMPARE(TEST_INT_MAX, obj1.plane());
440 // int QGLFormat::major/minorVersion()
441 // void QGLFormat::setVersion(int, int)
442 QCOMPARE(obj1.majorVersion(), 1);
443 QCOMPARE(obj1.minorVersion(), 0);
444 obj1.setVersion(3, 2);
445 QCOMPARE(obj1.majorVersion(), 3);
446 QCOMPARE(obj1.minorVersion(), 2);
447 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 0.1");
448 obj1.setVersion(0, 1);
449 QCOMPARE(obj1.majorVersion(), 3);
450 QCOMPARE(obj1.minorVersion(), 2);
451 QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 3.-1");
452 obj1.setVersion(3, -1);
453 QCOMPARE(obj1.majorVersion(), 3);
454 QCOMPARE(obj1.minorVersion(), 2);
455 obj1.setVersion(TEST_INT_MAX, TEST_INT_MAX - 1);
456 QCOMPARE(obj1.majorVersion(), TEST_INT_MAX);
457 QCOMPARE(obj1.minorVersion(), TEST_INT_MAX - 1);
460 // operator== and operator!= for QGLFormat
464 QVERIFY(format1 == format2);
465 QVERIFY(!(format1 != format2));
466 format1.setDoubleBuffer(false);
467 QVERIFY(!(format1 == format2));
468 QVERIFY(format1 != format2);
469 format2.setDoubleBuffer(false);
470 QVERIFY(format1 == format2);
471 QVERIFY(!(format1 != format2));
473 format1.setDepthBufferSize(8);
474 QVERIFY(!(format1 == format2));
475 QVERIFY(format1 != format2);
476 format2.setDepthBufferSize(8);
477 QVERIFY(format1 == format2);
478 QVERIFY(!(format1 != format2));
480 format1.setAccumBufferSize(8);
481 QVERIFY(!(format1 == format2));
482 QVERIFY(format1 != format2);
483 format2.setAccumBufferSize(8);
484 QVERIFY(format1 == format2);
485 QVERIFY(!(format1 != format2));
487 format1.setRedBufferSize(8);
488 QVERIFY(!(format1 == format2));
489 QVERIFY(format1 != format2);
490 format2.setRedBufferSize(8);
491 QVERIFY(format1 == format2);
492 QVERIFY(!(format1 != format2));
494 format1.setGreenBufferSize(8);
495 QVERIFY(!(format1 == format2));
496 QVERIFY(format1 != format2);
497 format2.setGreenBufferSize(8);
498 QVERIFY(format1 == format2);
499 QVERIFY(!(format1 != format2));
501 format1.setBlueBufferSize(8);
502 QVERIFY(!(format1 == format2));
503 QVERIFY(format1 != format2);
504 format2.setBlueBufferSize(8);
505 QVERIFY(format1 == format2);
506 QVERIFY(!(format1 != format2));
508 format1.setAlphaBufferSize(8);
509 QVERIFY(!(format1 == format2));
510 QVERIFY(format1 != format2);
511 format2.setAlphaBufferSize(8);
512 QVERIFY(format1 == format2);
513 QVERIFY(!(format1 != format2));
515 format1.setStencilBufferSize(8);
516 QVERIFY(!(format1 == format2));
517 QVERIFY(format1 != format2);
518 format2.setStencilBufferSize(8);
519 QVERIFY(format1 == format2);
520 QVERIFY(!(format1 != format2));
522 format1.setSamples(8);
523 QVERIFY(!(format1 == format2));
524 QVERIFY(format1 != format2);
525 format2.setSamples(8);
526 QVERIFY(format1 == format2);
527 QVERIFY(!(format1 != format2));
529 format1.setSwapInterval(8);
530 QVERIFY(!(format1 == format2));
531 QVERIFY(format1 != format2);
532 format2.setSwapInterval(8);
533 QVERIFY(format1 == format2);
534 QVERIFY(!(format1 != format2));
537 QVERIFY(!(format1 == format2));
538 QVERIFY(format1 != format2);
540 QVERIFY(format1 == format2);
541 QVERIFY(!(format1 != format2));
543 format1.setVersion(3, 2);
544 QVERIFY(!(format1 == format2));
545 QVERIFY(format1 != format2);
546 format2.setVersion(3, 2);
547 QVERIFY(format1 == format2);
548 QVERIFY(!(format1 != format2));
550 format1.setProfile(QGLFormat::CoreProfile);
551 QVERIFY(!(format1 == format2));
552 QVERIFY(format1 != format2);
553 format2.setProfile(QGLFormat::CoreProfile);
554 QVERIFY(format1 == format2);
555 QVERIFY(!(format1 != format2));
557 format1.setOption(QGL::NoDeprecatedFunctions);
558 QVERIFY(!(format1 == format2));
559 QVERIFY(format1 != format2);
560 format2.setOption(QGL::NoDeprecatedFunctions);
561 QVERIFY(format1 == format2);
562 QVERIFY(!(format1 != format2));
564 // Copy constructor and assignment for QGLFormat.
565 QGLFormat format3(format1);
567 QVERIFY(format1 == format3);
568 QVERIFY(format1 != format4);
570 QVERIFY(format1 == format4);
572 // Check that modifying a copy doesn't affect the original.
573 format3.setRedBufferSize(16);
574 format4.setPlane(16);
575 QCOMPARE(format1.redBufferSize(), 8);
576 QCOMPARE(format1.plane(), 8);
578 // Check the QGLFormat constructor that takes an option list.
580 (QGL::DepthBuffer | QGL::StereoBuffers | QGL::ColorIndex, 3);
581 QVERIFY(format5.depth());
582 QVERIFY(format5.stereo());
583 QVERIFY(format5.doubleBuffer()); // From defaultFormat()
584 QVERIFY(!format5.hasOverlay()); // From defaultFormat()
585 QVERIFY(!format5.rgba());
586 QCOMPARE(format5.plane(), 3);
588 // The default format should be the same as QGLFormat().
589 QVERIFY(QGLFormat::defaultFormat() == QGLFormat());
591 // Modify the default format and check that it was changed.
592 QGLFormat::setDefaultFormat(format1);
593 QVERIFY(QGLFormat::defaultFormat() == format1);
595 // Restore the default format.
596 QGLFormat::setDefaultFormat(QGLFormat());
597 QVERIFY(QGLFormat::defaultFormat() == QGLFormat());
599 // Check the default overlay format's expected values.
600 QGLFormat overlay(QGLFormat::defaultOverlayFormat());
601 QCOMPARE(overlay.depthBufferSize(), -1);
602 QCOMPARE(overlay.accumBufferSize(), -1);
603 QCOMPARE(overlay.redBufferSize(), -1);
604 QCOMPARE(overlay.greenBufferSize(), -1);
605 QCOMPARE(overlay.blueBufferSize(), -1);
606 QCOMPARE(overlay.alphaBufferSize(), -1);
607 QCOMPARE(overlay.samples(), -1);
608 QCOMPARE(overlay.swapInterval(), -1);
609 QCOMPARE(overlay.plane(), 1);
610 QVERIFY(!overlay.sampleBuffers());
611 QVERIFY(!overlay.doubleBuffer());
612 QVERIFY(!overlay.depth());
613 QVERIFY(!overlay.rgba());
614 QVERIFY(!overlay.alpha());
615 QVERIFY(!overlay.accum());
616 QVERIFY(!overlay.stencil());
617 QVERIFY(!overlay.stereo());
618 QVERIFY(overlay.directRendering()); // Only option that should be on.
619 QVERIFY(!overlay.hasOverlay()); // Overlay doesn't need an overlay!
621 // Modify the default overlay format and check that it was changed.
622 QGLFormat::setDefaultOverlayFormat(format1);
623 QVERIFY(QGLFormat::defaultOverlayFormat() == format1);
625 // Restore the default overlay format.
626 QGLFormat::setDefaultOverlayFormat(overlay);
627 QVERIFY(QGLFormat::defaultOverlayFormat() == overlay);
629 MyGLContext obj2(obj1);
630 // bool QGLContext::windowCreated()
631 // void QGLContext::setWindowCreated(bool)
632 obj2.setWindowCreated(false);
633 QCOMPARE(false, obj2.windowCreated());
634 obj2.setWindowCreated(true);
635 QCOMPARE(true, obj2.windowCreated());
637 // bool QGLContext::initialized()
638 // void QGLContext::setInitialized(bool)
639 obj2.setInitialized(false);
640 QCOMPARE(false, obj2.initialized());
641 obj2.setInitialized(true);
642 QCOMPARE(true, obj2.initialized());
645 // bool QGLWidget::autoBufferSwap()
646 // void QGLWidget::setAutoBufferSwap(bool)
647 obj3.setAutoBufferSwap(false);
648 QCOMPARE(false, obj3.autoBufferSwap());
649 obj3.setAutoBufferSwap(true);
650 QCOMPARE(true, obj3.autoBufferSwap());
653 #ifdef QT_BUILD_INTERNAL
655 extern QGLFormat::OpenGLVersionFlags qOpenGLVersionFlagsFromString(const QString &versionString);
659 #ifdef QT_BUILD_INTERNAL
660 void tst_QGL::openGLVersionCheck()
662 QString versionString;
663 QGLFormat::OpenGLVersionFlags expectedFlag;
664 QGLFormat::OpenGLVersionFlags versionFlag;
666 versionString = "1.1 Irix 6.5";
667 expectedFlag = QGLFormat::OpenGL_Version_1_1;
668 versionFlag = qOpenGLVersionFlagsFromString(versionString);
669 QCOMPARE(versionFlag, expectedFlag);
671 versionString = "1.2 Microsoft";
672 expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
673 versionFlag = qOpenGLVersionFlagsFromString(versionString);
674 QCOMPARE(versionFlag, expectedFlag);
676 versionString = "1.2.1";
677 expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
678 versionFlag = qOpenGLVersionFlagsFromString(versionString);
679 QCOMPARE(versionFlag, expectedFlag);
681 versionString = "1.3 NVIDIA";
682 expectedFlag = QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
683 versionFlag = qOpenGLVersionFlagsFromString(versionString);
684 QCOMPARE(versionFlag, expectedFlag);
686 versionString = "1.4";
687 expectedFlag = QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
688 versionFlag = qOpenGLVersionFlagsFromString(versionString);
689 QCOMPARE(versionFlag, expectedFlag);
691 versionString = "1.5 NVIDIA";
692 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;
693 versionFlag = qOpenGLVersionFlagsFromString(versionString);
694 QCOMPARE(versionFlag, expectedFlag);
696 versionString = "2.0.2 NVIDIA 87.62";
697 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;
698 versionFlag = qOpenGLVersionFlagsFromString(versionString);
699 QCOMPARE(versionFlag, expectedFlag);
701 versionString = "2.1 NVIDIA";
702 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;
703 versionFlag = qOpenGLVersionFlagsFromString(versionString);
704 QCOMPARE(versionFlag, expectedFlag);
706 versionString = "2.1";
707 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;
708 versionFlag = qOpenGLVersionFlagsFromString(versionString);
709 QCOMPARE(versionFlag, expectedFlag);
711 versionString = "OpenGL ES-CM 1.0 ATI";
712 expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
713 versionFlag = qOpenGLVersionFlagsFromString(versionString);
714 QCOMPARE(versionFlag, expectedFlag);
716 versionString = "OpenGL ES-CL 1.0 ATI";
717 expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
718 versionFlag = qOpenGLVersionFlagsFromString(versionString);
719 QCOMPARE(versionFlag, expectedFlag);
721 versionString = "OpenGL ES-CM 1.1 ATI";
722 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;
723 versionFlag = qOpenGLVersionFlagsFromString(versionString);
724 QCOMPARE(versionFlag, expectedFlag);
726 versionString = "OpenGL ES-CL 1.1 ATI";
727 expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
728 versionFlag = qOpenGLVersionFlagsFromString(versionString);
729 QCOMPARE(versionFlag, expectedFlag);
731 versionString = "OpenGL ES 2.0 ATI";
732 expectedFlag = QGLFormat::OpenGL_ES_Version_2_0;
733 versionFlag = qOpenGLVersionFlagsFromString(versionString);
734 QCOMPARE(versionFlag, expectedFlag);
736 versionString = "3.0";
737 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;
738 versionFlag = qOpenGLVersionFlagsFromString(versionString);
739 QCOMPARE(versionFlag, expectedFlag);
743 glWidget.makeCurrent();
745 // This is unfortunately the only test we can make on the actual openGLVersionFlags()
746 // However, the complicated parts are in openGLVersionFlags(const QString &versionString)
749 #if defined(QT_OPENGL_ES_1)
750 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Common_Version_1_0);
751 #elif defined(QT_OPENGL_ES_2)
752 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
754 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1);
755 #endif //defined(QT_OPENGL_ES_1)
757 #endif //QT_BUILD_INTERNAL
759 static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1)
761 static int maxFuzz = 1;
762 static bool maxFuzzSet = false;
764 // On 16 bpp systems, we need to allow for more fuzz:
767 if (appDefaultDepth() < 24)
771 int redFuzz = qAbs(qRed(testPixel) - qRed(refPixel));
772 int greenFuzz = qAbs(qGreen(testPixel) - qGreen(refPixel));
773 int blueFuzz = qAbs(qBlue(testPixel) - qBlue(refPixel));
774 int alphaFuzz = qAbs(qAlpha(testPixel) - qAlpha(refPixel));
776 if (refPixel != 0 && testPixel == 0) {
779 msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)")
781 .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
783 msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)")
784 .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
787 QTest::qFail(msg.toLatin1(), file, line);
791 if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) {
795 msg = QString("Pixel [%1,%2]: ").arg(x).arg(y);
797 msg = QString("Pixel ");
799 msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)")
801 .arg(qRed(testPixel)).arg(qGreen(testPixel)).arg(qBlue(testPixel)).arg(qAlpha(testPixel))
802 .arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
803 QTest::qFail(msg.toLatin1(), file, line);
809 static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line)
811 QCOMPARE(testImage.width(), referenceImage.width());
812 QCOMPARE(testImage.height(), referenceImage.height());
814 for (int y = 0; y < testImage.height(); y++) {
815 for (int x = 0; x < testImage.width(); x++) {
816 if (!fuzzyComparePixels(testImage.pixel(x, y), referenceImage.pixel(x, y), file, line, x, y)) {
817 // Might as well save the images for easier debugging:
818 referenceImage.save("referenceImage.png");
819 testImage.save("testImage.png");
826 #define QFUZZY_COMPARE_IMAGES(A,B) \
827 fuzzyCompareImages(A, B, __FILE__, __LINE__)
829 #define QFUZZY_COMPARE_PIXELS(A,B) \
830 fuzzyComparePixels(A, B, __FILE__, __LINE__)
832 class UnclippedWidget : public QWidget
835 void paintEvent(QPaintEvent *)
838 p.fillRect(rect().adjusted(-1000, -1000, 1000, 1000), Qt::black);
842 void tst_QGL::graphicsViewClipping()
845 UnclippedWidget *widget = new UnclippedWidget;
846 widget->setFixedSize(size, size);
848 QGraphicsScene scene;
850 scene.addWidget(widget)->setPos(0, 0);
852 QGraphicsView view(&scene);
853 // Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
854 view.setWindowFlags(view.windowFlags() | Qt::Tool);
855 view.setBackgroundBrush(Qt::white);
856 view.resize(2*size, 2*size);
858 QGLWidget *viewport = new QGLWidget;
859 view.setViewport(viewport);
861 qApp->setActiveWindow(&view);
863 if (!viewport->isValid())
866 scene.setSceneRect(view.viewport()->rect());
868 QVERIFY(QTest::qWaitForWindowActive(&view));
870 QImage image = viewport->grabFrameBuffer();
871 QImage expected = image;
873 QPainter p(&expected);
874 p.fillRect(expected.rect(), Qt::white);
875 p.fillRect(QRect(0, 0, size, size), Qt::black);
878 QFUZZY_COMPARE_IMAGES(image, expected);
881 void tst_QGL::partialGLWidgetUpdates_data()
883 QTest::addColumn<bool>("doubleBufferedContext");
884 QTest::addColumn<bool>("autoFillBackground");
885 QTest::addColumn<bool>("supportsPartialUpdates");
887 QTest::newRow("Double buffered context") << true << true << false;
888 QTest::newRow("Double buffered context without auto-fill background") << true << false << false;
889 QTest::newRow("Single buffered context") << false << true << false;
890 QTest::newRow("Single buffered context without auto-fill background") << false << false << true;
893 void tst_QGL::partialGLWidgetUpdates()
895 QFETCH(bool, doubleBufferedContext);
896 QFETCH(bool, autoFillBackground);
897 QFETCH(bool, supportsPartialUpdates);
899 class MyGLWidget : public QGLWidget
902 QRegion paintEventRegion;
903 void paintEvent(QPaintEvent *e)
905 paintEventRegion = e->region();
909 QGLFormat format = QGLFormat::defaultFormat();
910 format.setDoubleBuffer(doubleBufferedContext);
911 QGLFormat::setDefaultFormat(format);
914 widget.setFixedSize(150, 150);
915 widget.setAutoFillBackground(autoFillBackground);
920 if (widget.format().doubleBuffer() != doubleBufferedContext)
921 QSKIP("Platform does not support requested format");
923 widget.paintEventRegion = QRegion();
924 widget.repaint(50, 50, 50, 50);
926 if (supportsPartialUpdates)
927 QCOMPARE(widget.paintEventRegion, QRegion(50, 50, 50, 50));
929 QCOMPARE(widget.paintEventRegion, QRegion(widget.rect()));
933 // This tests that rendering to a QGLPBuffer using QPainter works.
934 void tst_QGL::glPBufferRendering()
936 if (!QGLPixelBuffer::hasOpenGLPbuffers())
937 QSKIP("QGLPixelBuffer not supported on this platform");
939 QGLPixelBuffer* pbuf = new QGLPixelBuffer(128, 128);
942 bool begun = p.begin(pbuf);
945 QPaintEngine::Type engineType = p.paintEngine()->type();
946 QVERIFY(engineType == QPaintEngine::OpenGL || engineType == QPaintEngine::OpenGL2);
948 p.fillRect(0, 0, 128, 128, Qt::red);
949 p.fillRect(32, 32, 64, 64, Qt::blue);
952 QImage fb = pbuf->toImage();
955 QImage reference(128, 128, fb.format());
957 p.fillRect(0, 0, 128, 128, Qt::red);
958 p.fillRect(32, 32, 64, 64, Qt::blue);
961 QFUZZY_COMPARE_IMAGES(fb, reference);
964 void tst_QGL::glWidgetWithAlpha()
966 QGLWidget* w = new QGLWidget(QGLFormat(QGL::AlphaChannel));
968 QVERIFY(QTest::qWaitForWindowExposed(w));
974 void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height)
976 QPainterPath intersectingPath;
977 intersectingPath.moveTo(0, 0);
978 intersectingPath.lineTo(100, 0);
979 intersectingPath.lineTo(0, 100);
980 intersectingPath.lineTo(100, 100);
981 intersectingPath.closeSubpath();
983 QPainterPath trianglePath;
984 trianglePath.moveTo(50, 0);
985 trianglePath.lineTo(100, 100);
986 trianglePath.lineTo(0, 100);
987 trianglePath.closeSubpath();
989 painter->setTransform(QTransform()); // reset xform
990 painter->fillRect(-1, -1, width+2, height+2, Qt::red); // Background
991 painter->translate(14, 14);
992 painter->fillPath(intersectingPath, Qt::blue); // Test stencil buffer works
993 painter->translate(128, 0);
994 painter->setClipPath(trianglePath); // Test depth buffer works
995 painter->setTransform(QTransform()); // reset xform ready for fill
996 painter->fillRect(-1, -1, width+2, height+2, Qt::green);
999 void qt_opengl_check_test_pattern(const QImage& img)
1001 // As we're doing more than trivial painting, we can't just compare to
1002 // an image rendered with raster. Instead, we sample at well-defined
1004 QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb());
1005 QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb());
1006 QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb());
1007 QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb());
1009 QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb());
1010 QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb());
1011 QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb());
1014 class GLWidget : public QGLWidget
1017 GLWidget(QWidget* p = 0)
1018 : QGLWidget(p), beginOk(false), engineType(QPaintEngine::MaxUser) {}
1020 QPaintEngine::Type engineType;
1024 beginOk = p.begin(this);
1025 QPaintEngine* pe = p.paintEngine();
1026 engineType = pe->type();
1028 qt_opengl_draw_test_pattern(&p, width(), height());
1030 // No p.end() or swap buffers, should be done automatically
1035 void tst_QGL::glWidgetRendering()
1041 QVERIFY(QTest::qWaitForWindowExposed(&w));
1044 QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2);
1046 QImage fb = w.grabFrameBuffer(false);
1047 qt_opengl_check_test_pattern(fb);
1050 void tst_QGL::glFBOSimpleRendering()
1052 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1053 QSKIP("QGLFramebufferObject not supported on this platform");
1058 // No multisample with combined depth/stencil attachment:
1059 QGLFramebufferObjectFormat fboFormat;
1060 fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
1062 QGLFramebufferObject *fbo = new QGLFramebufferObject(200, 100, fboFormat);
1066 glClearColor(1.0, 0.0, 0.0, 1.0);
1067 glClear(GL_COLOR_BUFFER_BIT);
1070 QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
1071 QImage reference(fb.size(), QImage::Format_RGB32);
1072 reference.fill(0xffff0000);
1074 QFUZZY_COMPARE_IMAGES(fb, reference);
1079 // NOTE: This tests that CombinedDepthStencil attachment works by assuming the
1080 // GL2 engine is being used and is implemented the same way as it was when
1081 // this autotest was written. If this is not the case, there may be some
1082 // false-positives: I.e. The test passes when either the depth or stencil
1083 // buffer is actually missing. But that's probably ok anyway.
1084 void tst_QGL::glFBORendering()
1086 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1087 QSKIP("QGLFramebufferObject not supported on this platform");
1092 // No multisample with combined depth/stencil attachment:
1093 QGLFramebufferObjectFormat fboFormat;
1094 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1096 // Don't complicate things by using NPOT:
1097 QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
1099 if (fbo->attachment() != QGLFramebufferObject::CombinedDepthStencil) {
1101 QSKIP("FBOs missing combined depth~stencil support");
1104 QPainter fboPainter;
1105 bool painterBegun = fboPainter.begin(fbo);
1106 QVERIFY(painterBegun);
1108 qt_opengl_draw_test_pattern(&fboPainter, fbo->width(), fbo->height());
1112 QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
1115 qt_opengl_check_test_pattern(fb);
1119 // Tests multiple QPainters active on different FBOs at the same time, with
1120 // interleaving painting. Performance-wise, this is sub-optimal, but it still
1121 // has to work flawlessly
1122 void tst_QGL::multipleFBOInterleavedRendering()
1124 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1125 QSKIP("QGLFramebufferObject not supported on this platform");
1130 // No multisample with combined depth/stencil attachment:
1131 QGLFramebufferObjectFormat fboFormat;
1132 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1134 QGLFramebufferObject *fbo1 = new QGLFramebufferObject(256, 128, fboFormat);
1135 QGLFramebufferObject *fbo2 = new QGLFramebufferObject(256, 128, fboFormat);
1136 QGLFramebufferObject *fbo3 = new QGLFramebufferObject(256, 128, fboFormat);
1138 if ( (fbo1->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
1139 (fbo2->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
1140 (fbo3->attachment() != QGLFramebufferObject::CombinedDepthStencil) )
1145 QSKIP("FBOs missing combined depth~stencil support");
1148 QPainter fbo1Painter;
1149 QPainter fbo2Painter;
1150 QPainter fbo3Painter;
1152 QVERIFY(fbo1Painter.begin(fbo1));
1153 QVERIFY(fbo2Painter.begin(fbo2));
1154 QVERIFY(fbo3Painter.begin(fbo3));
1156 // Confirm we're using the GL2 engine, as interleaved rendering isn't supported
1157 // on the GL1 engine:
1158 if (fbo1Painter.paintEngine()->type() != QPaintEngine::OpenGL2)
1159 QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher");
1161 QPainterPath intersectingPath;
1162 intersectingPath.moveTo(0, 0);
1163 intersectingPath.lineTo(100, 0);
1164 intersectingPath.lineTo(0, 100);
1165 intersectingPath.lineTo(100, 100);
1166 intersectingPath.closeSubpath();
1168 QPainterPath trianglePath;
1169 trianglePath.moveTo(50, 0);
1170 trianglePath.lineTo(100, 100);
1171 trianglePath.lineTo(0, 100);
1172 trianglePath.closeSubpath();
1174 fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::red); // Background
1175 fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::green); // Background
1176 fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::blue); // Background
1178 fbo1Painter.translate(14, 14);
1179 fbo2Painter.translate(14, 14);
1180 fbo3Painter.translate(14, 14);
1182 fbo1Painter.fillPath(intersectingPath, Qt::blue); // Test stencil buffer works
1183 fbo2Painter.fillPath(intersectingPath, Qt::red); // Test stencil buffer works
1184 fbo3Painter.fillPath(intersectingPath, Qt::green); // Test stencil buffer works
1186 fbo1Painter.translate(128, 0);
1187 fbo2Painter.translate(128, 0);
1188 fbo3Painter.translate(128, 0);
1190 fbo1Painter.setClipPath(trianglePath);
1191 fbo2Painter.setClipPath(trianglePath);
1192 fbo3Painter.setClipPath(trianglePath);
1194 fbo1Painter.setTransform(QTransform()); // reset xform
1195 fbo2Painter.setTransform(QTransform()); // reset xform
1196 fbo3Painter.setTransform(QTransform()); // reset xform
1198 fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::green);
1199 fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::blue);
1200 fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::red);
1206 QImage fb1 = fbo1->toImage().convertToFormat(QImage::Format_RGB32);
1207 QImage fb2 = fbo2->toImage().convertToFormat(QImage::Format_RGB32);
1208 QImage fb3 = fbo3->toImage().convertToFormat(QImage::Format_RGB32);
1213 // As we're doing more than trivial painting, we can't just compare to
1214 // an image rendered with raster. Instead, we sample at well-defined
1216 QFUZZY_COMPARE_PIXELS(fb1.pixel(39, 64), QColor(Qt::red).rgb());
1217 QFUZZY_COMPARE_PIXELS(fb1.pixel(89, 64), QColor(Qt::red).rgb());
1218 QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 39), QColor(Qt::blue).rgb());
1219 QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 89), QColor(Qt::blue).rgb());
1220 QFUZZY_COMPARE_PIXELS(fb1.pixel(167, 39), QColor(Qt::red).rgb());
1221 QFUZZY_COMPARE_PIXELS(fb1.pixel(217, 39), QColor(Qt::red).rgb());
1222 QFUZZY_COMPARE_PIXELS(fb1.pixel(192, 64), QColor(Qt::green).rgb());
1224 QFUZZY_COMPARE_PIXELS(fb2.pixel(39, 64), QColor(Qt::green).rgb());
1225 QFUZZY_COMPARE_PIXELS(fb2.pixel(89, 64), QColor(Qt::green).rgb());
1226 QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 39), QColor(Qt::red).rgb());
1227 QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 89), QColor(Qt::red).rgb());
1228 QFUZZY_COMPARE_PIXELS(fb2.pixel(167, 39), QColor(Qt::green).rgb());
1229 QFUZZY_COMPARE_PIXELS(fb2.pixel(217, 39), QColor(Qt::green).rgb());
1230 QFUZZY_COMPARE_PIXELS(fb2.pixel(192, 64), QColor(Qt::blue).rgb());
1232 QFUZZY_COMPARE_PIXELS(fb3.pixel(39, 64), QColor(Qt::blue).rgb());
1233 QFUZZY_COMPARE_PIXELS(fb3.pixel(89, 64), QColor(Qt::blue).rgb());
1234 QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 39), QColor(Qt::green).rgb());
1235 QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 89), QColor(Qt::green).rgb());
1236 QFUZZY_COMPARE_PIXELS(fb3.pixel(167, 39), QColor(Qt::blue).rgb());
1237 QFUZZY_COMPARE_PIXELS(fb3.pixel(217, 39), QColor(Qt::blue).rgb());
1238 QFUZZY_COMPARE_PIXELS(fb3.pixel(192, 64), QColor(Qt::red).rgb());
1241 class FBOUseInGLWidget : public QGLWidget
1244 bool widgetPainterBeginOk;
1245 bool fboPainterBeginOk;
1248 void paintEvent(QPaintEvent*)
1250 QPainter widgetPainter;
1251 widgetPainterBeginOk = widgetPainter.begin(this);
1252 QGLFramebufferObjectFormat fboFormat;
1253 fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
1254 QGLFramebufferObject *fbo = new QGLFramebufferObject(100, 100, fboFormat);
1256 QPainter fboPainter;
1257 fboPainterBeginOk = fboPainter.begin(fbo);
1258 fboPainter.fillRect(-1, -1, 130, 130, Qt::red);
1260 fboImage = fbo->toImage();
1262 widgetPainter.fillRect(-1, -1, width()+2, width()+2, Qt::blue);
1269 void tst_QGL::glFBOUseInGLWidget()
1271 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1272 QSKIP("QGLFramebufferObject not supported on this platform");
1278 QVERIFY(QTest::qWaitForWindowExposed(&w));
1280 QVERIFY(w.widgetPainterBeginOk);
1281 QVERIFY(w.fboPainterBeginOk);
1283 QImage widgetFB = w.grabFrameBuffer(false);
1284 QImage widgetReference(widgetFB.size(), widgetFB.format());
1285 widgetReference.fill(0xff0000ff);
1286 QFUZZY_COMPARE_IMAGES(widgetFB, widgetReference);
1288 QImage fboReference(w.fboImage.size(), w.fboImage.format());
1289 fboReference.fill(0xffff0000);
1290 QFUZZY_COMPARE_IMAGES(w.fboImage, fboReference);
1293 void tst_QGL::glWidgetReparent()
1295 // Try it as a top-level first:
1296 GLWidget *widget = new GLWidget;
1297 widget->setGeometry(0, 0, 200, 30);
1300 QWidget grandParentWidget;
1301 grandParentWidget.setPalette(Qt::blue);
1302 QVBoxLayout grandParentLayout(&grandParentWidget);
1304 QWidget parentWidget(&grandParentWidget);
1305 grandParentLayout.addWidget(&parentWidget);
1306 parentWidget.setPalette(Qt::green);
1307 parentWidget.setAutoFillBackground(true);
1308 QVBoxLayout parentLayout(&parentWidget);
1310 grandParentWidget.setGeometry(0, 100, 200, 200);
1311 grandParentWidget.show();
1313 QVERIFY(QTest::qWaitForWindowExposed(widget));
1314 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1316 QVERIFY(parentWidget.children().count() == 1); // The layout
1318 // Now both widgets should be created & shown, time to re-parent:
1319 parentLayout.addWidget(widget);
1321 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1323 QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
1324 QVERIFY(parentWidget.children().contains(widget));
1325 QTRY_VERIFY(widget->height() > 30);
1329 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1331 QVERIFY(parentWidget.children().count() == 1); // The layout
1333 // Now do pretty much the same thing, but don't show the
1335 widget = new GLWidget;
1336 parentLayout.addWidget(widget);
1338 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1340 QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
1341 QVERIFY(parentWidget.children().contains(widget));
1342 QVERIFY(widget->height() > 30);
1347 class RenderPixmapWidget : public QGLWidget
1350 void initializeGL() {
1351 // Set some gl state:
1352 glClearColor(1.0, 0.0, 0.0, 1.0);
1356 glClear(GL_COLOR_BUFFER_BIT);
1360 void tst_QGL::glWidgetRenderPixmap()
1362 RenderPixmapWidget *w = new RenderPixmapWidget;
1364 QPixmap pm = w->renderPixmap(100, 100, false);
1368 QImage fb = pm.toImage().convertToFormat(QImage::Format_RGB32);
1369 QImage reference(fb.size(), QImage::Format_RGB32);
1370 reference.fill(0xffff0000);
1372 QFUZZY_COMPARE_IMAGES(fb, reference);
1375 class ColormapExtended : public QGLColormap
1378 ColormapExtended() {}
1380 Qt::HANDLE handle() { return QGLColormap::handle(); }
1381 void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); }
1384 void tst_QGL::colormap()
1386 // Check the properties of the default empty colormap.
1388 QVERIFY(cmap1.isEmpty());
1389 QCOMPARE(cmap1.size(), 0);
1390 QVERIFY(cmap1.entryRgb(0) == 0);
1391 QVERIFY(cmap1.entryRgb(-1) == 0);
1392 QVERIFY(cmap1.entryRgb(100) == 0);
1393 QVERIFY(!cmap1.entryColor(0).isValid());
1394 QVERIFY(!cmap1.entryColor(-1).isValid());
1395 QVERIFY(!cmap1.entryColor(100).isValid());
1396 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1);
1397 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1);
1399 // Set an entry and re-test.
1400 cmap1.setEntry(56, qRgb(255, 0, 0));
1401 // The colormap is still considered "empty" even though it
1402 // has entries in it now. The isEmpty() method is used to
1403 // detect when the colormap is in use by a GL widget,
1404 // not to detect when it is empty!
1405 QVERIFY(cmap1.isEmpty());
1406 QCOMPARE(cmap1.size(), 256);
1407 QVERIFY(cmap1.entryRgb(0) == 0);
1408 QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255));
1409 QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0));
1410 QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255));
1411 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
1412 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
1414 // Set some more entries.
1415 static QRgb const colors[] = {
1418 qRgb(255, 255, 255),
1422 cmap1.setEntry(57, QColor(0, 255, 0));
1423 cmap1.setEntries(3, colors + 2, 58);
1424 cmap1.setEntries(5, colors, 251);
1426 for (idx = 0; idx < 5; ++idx) {
1427 QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]);
1428 QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx]));
1429 QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]);
1430 QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx]));
1432 QCOMPARE(cmap1.size(), 256);
1434 // Perform color lookups.
1435 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
1436 QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255.
1437 QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0);
1438 QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57);
1439 QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58);
1440 QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59);
1441 QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1);
1442 QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1);
1443 QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1);
1444 QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1);
1445 QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1);
1446 QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1);
1447 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
1448 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60);
1449 QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0);
1450 QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57);
1451 QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58);
1452 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59);
1453 QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56);
1454 QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57);
1455 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59);
1456 QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0);
1457 QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0);
1458 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0);
1460 // Make some copies of the colormap and check that they are the same.
1461 QGLColormap cmap2(cmap1);
1464 QVERIFY(cmap2.isEmpty());
1465 QVERIFY(cmap3.isEmpty());
1466 QCOMPARE(cmap2.size(), 256);
1467 QCOMPARE(cmap3.size(), 256);
1468 for (idx = 0; idx < 256; ++idx) {
1469 QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
1470 QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
1473 // Modify an entry in one of the copies and recheck the original.
1474 cmap2.setEntry(45, qRgb(255, 0, 0));
1475 for (idx = 0; idx < 256; ++idx) {
1477 QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
1479 QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0));
1480 QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
1483 // Check that setting the handle will cause isEmpty() to work right.
1484 ColormapExtended cmap4;
1485 cmap4.setEntry(56, qRgb(255, 0, 0));
1486 QVERIFY(cmap4.isEmpty());
1487 QCOMPARE(cmap4.size(), 256);
1488 cmap4.setHandle(Qt::HANDLE(42));
1489 QVERIFY(cmap4.handle() == Qt::HANDLE(42));
1490 QVERIFY(!cmap4.isEmpty());
1491 QCOMPARE(cmap4.size(), 256);
1494 #ifndef QT_OPENGL_ES
1495 #define DEFAULT_FORMAT GL_RGBA8
1497 #define DEFAULT_FORMAT GL_RGBA
1500 #ifndef GL_TEXTURE_3D
1501 #define GL_TEXTURE_3D 0x806F
1505 #define GL_RGB16 0x8054
1508 void tst_QGL::fboFormat()
1510 // Check the initial conditions.
1511 QGLFramebufferObjectFormat format1;
1512 QCOMPARE(format1.samples(), 0);
1513 QVERIFY(format1.attachment() == QGLFramebufferObject::NoAttachment);
1514 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D));
1515 QCOMPARE(int(format1.internalTextureFormat()), int(DEFAULT_FORMAT));
1517 // Modify the values and re-check.
1518 format1.setSamples(8);
1519 format1.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1520 format1.setTextureTarget(GL_TEXTURE_3D);
1521 format1.setInternalTextureFormat(GL_RGB16);
1522 QCOMPARE(format1.samples(), 8);
1523 QVERIFY(format1.attachment() == QGLFramebufferObject::CombinedDepthStencil);
1524 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
1525 QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
1527 // Make copies and check that they are the same.
1528 QGLFramebufferObjectFormat format2(format1);
1529 QGLFramebufferObjectFormat format3;
1530 QCOMPARE(format2.samples(), 8);
1531 QVERIFY(format2.attachment() == QGLFramebufferObject::CombinedDepthStencil);
1532 QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D));
1533 QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16));
1535 QCOMPARE(format3.samples(), 8);
1536 QVERIFY(format3.attachment() == QGLFramebufferObject::CombinedDepthStencil);
1537 QCOMPARE(int(format3.textureTarget()), int(GL_TEXTURE_3D));
1538 QCOMPARE(int(format3.internalTextureFormat()), int(GL_RGB16));
1540 // Modify the copies and check that the original is unchanged.
1541 format2.setSamples(9);
1542 format3.setTextureTarget(GL_TEXTURE_2D);
1543 QCOMPARE(format1.samples(), 8);
1544 QVERIFY(format1.attachment() == QGLFramebufferObject::CombinedDepthStencil);
1545 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
1546 QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
1548 // operator== and operator!= for QGLFramebufferObjectFormat.
1549 QGLFramebufferObjectFormat format1c;
1550 QGLFramebufferObjectFormat format2c;
1552 QVERIFY(format1c == format2c);
1553 QVERIFY(!(format1c != format2c));
1554 format1c.setSamples(8);
1555 QVERIFY(!(format1c == format2c));
1556 QVERIFY(format1c != format2c);
1557 format2c.setSamples(8);
1558 QVERIFY(format1c == format2c);
1559 QVERIFY(!(format1c != format2c));
1561 format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1562 QVERIFY(!(format1c == format2c));
1563 QVERIFY(format1c != format2c);
1564 format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1565 QVERIFY(format1c == format2c);
1566 QVERIFY(!(format1c != format2c));
1568 format1c.setTextureTarget(GL_TEXTURE_3D);
1569 QVERIFY(!(format1c == format2c));
1570 QVERIFY(format1c != format2c);
1571 format2c.setTextureTarget(GL_TEXTURE_3D);
1572 QVERIFY(format1c == format2c);
1573 QVERIFY(!(format1c != format2c));
1575 format1c.setInternalTextureFormat(GL_RGB16);
1576 QVERIFY(!(format1c == format2c));
1577 QVERIFY(format1c != format2c);
1578 format2c.setInternalTextureFormat(GL_RGB16);
1579 QVERIFY(format1c == format2c);
1580 QVERIFY(!(format1c != format2c));
1582 QGLFramebufferObjectFormat format3c(format1c);
1583 QGLFramebufferObjectFormat format4c;
1584 QVERIFY(format1c == format3c);
1585 QVERIFY(!(format1c != format3c));
1586 format3c.setInternalTextureFormat(DEFAULT_FORMAT);
1587 QVERIFY(!(format1c == format3c));
1588 QVERIFY(format1c != format3c);
1590 format4c = format1c;
1591 QVERIFY(format1c == format4c);
1592 QVERIFY(!(format1c != format4c));
1593 format4c.setInternalTextureFormat(DEFAULT_FORMAT);
1594 QVERIFY(!(format1c == format4c));
1595 QVERIFY(format1c != format4c);
1598 void tst_QGL::testDontCrashOnDanglingResources()
1600 // We have a number of Q_GLOBAL_STATICS inside the QtOpenGL
1601 // library. This test is verify that we don't crash as a result of
1602 // them calling into libgl on application shutdown.
1603 QWidget *widget = new UnclippedWidget();
1605 qApp->processEvents();
1609 class ReplaceClippingGLWidget : public QGLWidget
1612 void paint(QPainter *painter)
1614 painter->fillRect(rect(), Qt::white);
1617 path.addRect(0, 0, 100, 100);
1618 path.addRect(50, 50, 100, 100);
1620 painter->setClipRect(0, 0, 150, 150);
1621 painter->fillPath(path, Qt::red);
1623 painter->translate(150, 150);
1624 painter->setClipRect(0, 0, 150, 150);
1625 painter->fillPath(path, Qt::red);
1629 void paintEvent(QPaintEvent*)
1631 // clear the stencil with junk
1632 glStencilMask(0xFFFF);
1633 glClearStencil(0xFFFF);
1634 glDisable(GL_STENCIL_TEST);
1635 glDisable(GL_SCISSOR_TEST);
1636 glClear(GL_STENCIL_BUFFER_BIT);
1638 QPainter painter(this);
1643 void tst_QGL::replaceClipping()
1645 ReplaceClippingGLWidget glw;
1646 glw.resize(300, 300);
1649 QVERIFY(QTest::qWaitForWindowExposed(&glw));
1651 QImage reference(300, 300, QImage::Format_RGB32);
1652 QPainter referencePainter(&reference);
1653 glw.paint(&referencePainter);
1654 referencePainter.end();
1656 const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
1658 // Sample pixels in a grid pattern which avoids false failures due to
1659 // off-by-one pixel errors on some buggy GL implementations
1660 for (int x = 25; x < reference.width(); x += 50) {
1661 for (int y = 25; y < reference.width(); y += 50) {
1662 QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
1667 class ClipTestGLWidget : public QGLWidget
1670 void paint(QPainter *painter)
1672 painter->fillRect(-1, -1, width()+2, height()+2, Qt::white);
1673 painter->setClipRect(10, 10, width()-20, height()-20);
1674 painter->fillRect(rect(), Qt::cyan);
1677 painter->setClipRect(10, 10, 100, 100, Qt::IntersectClip);
1679 painter->fillRect(rect(), Qt::blue);
1682 painter->setClipRect(10, 10, 50, 50, Qt::IntersectClip);
1683 painter->fillRect(rect(), Qt::red);
1685 painter->fillRect(0, 0, 40, 40, Qt::white);
1688 painter->setClipRect(0, 0, 35, 35, Qt::IntersectClip);
1689 painter->fillRect(rect(), Qt::black);
1692 painter->fillRect(0, 0, 30, 30, Qt::magenta);
1695 painter->setClipRect(60, 10, 50, 50, Qt::ReplaceClip);
1696 painter->fillRect(rect(), Qt::green);
1701 painter->translate(100, 100);
1705 path.addRect(10, 10, 100, 100);
1706 path.addRect(10, 10, 10, 10);
1707 painter->setClipPath(path, Qt::IntersectClip);
1710 painter->fillRect(rect(), Qt::blue);
1715 path.addRect(10, 10, 50, 50);
1716 path.addRect(10, 10, 10, 10);
1717 painter->setClipPath(path, Qt::IntersectClip);
1719 painter->fillRect(rect(), Qt::red);
1721 painter->fillRect(0, 0, 40, 40, Qt::white);
1726 path.addRect(0, 0, 35, 35);
1727 path.addRect(10, 10, 10, 10);
1728 painter->setClipPath(path, Qt::IntersectClip);
1730 painter->fillRect(rect(), Qt::black);
1733 painter->fillRect(0, 0, 30, 30, Qt::magenta);
1738 path.addRect(60, 10, 50, 50);
1739 path.addRect(10, 10, 10, 10);
1740 painter->setClipPath(path, Qt::ReplaceClip);
1742 painter->fillRect(rect(), Qt::green);
1747 void paintEvent(QPaintEvent*)
1749 QPainter painter(this);
1754 void tst_QGL::clipTest()
1756 ClipTestGLWidget glw;
1757 glw.resize(220, 220);
1760 QVERIFY(QTest::qWaitForWindowExposed(&glw));
1762 QImage reference(glw.size(), QImage::Format_RGB32);
1763 QPainter referencePainter(&reference);
1764 glw.paint(&referencePainter);
1765 referencePainter.end();
1767 const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
1769 // Sample pixels in a grid pattern which avoids false failures due to
1770 // off-by-one pixel errors on some buggy GL implementations
1771 for (int x = 2; x < reference.width(); x += 5) {
1772 for (int y = 2; y < reference.height(); y += 5) {
1773 QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
1778 void tst_QGL::destroyFBOAfterContext()
1780 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1781 QSKIP("QGLFramebufferObject not supported on this platform");
1783 QGLWidget *glw = new QGLWidget();
1786 // No multisample with combined depth/stencil attachment:
1787 QGLFramebufferObjectFormat fboFormat;
1788 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1790 // Don't complicate things by using NPOT:
1791 QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
1793 // The handle should be valid until the context is destroyed.
1794 QVERIFY(fbo->handle() != 0);
1795 QVERIFY(fbo->isValid());
1799 // The handle should now be zero.
1800 QVERIFY(fbo->handle() == 0);
1801 QVERIFY(!fbo->isValid());
1806 #ifdef QT_BUILD_INTERNAL
1808 class tst_QGLResource
1811 tst_QGLResource(const QGLContext * = 0) {}
1812 ~tst_QGLResource() { ++deletions; }
1814 static int deletions;
1817 int tst_QGLResource::deletions = 0;
1820 Q_GLOBAL_STATIC(QOpenGLContextGroupResource<tst_QGLResource>, qt_shared_test)
1822 #endif // QT_BUILD_INTERNAL
1824 #ifdef QT_BUILD_INTERNAL
1825 void tst_QGL::shareRegister()
1828 // Create a context.
1829 QGLWidget *glw1 = new QGLWidget();
1830 glw1->makeCurrent();
1832 // Nothing should be sharing with glw1's context yet.
1833 QVERIFY(!glw1->isSharing());
1835 // Create a guard for the first context.
1836 QOpenGLSharedResourceGuard guard(glw1->context()->contextHandle());
1837 QVERIFY(guard.id() == 0);
1839 QVERIFY(guard.id() == 3);
1841 // Request a tst_QGLResource object for the first context.
1842 tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()->contextHandle());
1844 QVERIFY(qt_shared_test()->value(glw1->context()->contextHandle()) == res1);
1846 // Create another context that shares with the first.
1847 QVERIFY(!glw1->isSharing());
1848 QGLWidget *glw2 = new QGLWidget(0, glw1);
1849 if (!glw2->isSharing()) {
1852 QSKIP("Context sharing is not supported");
1854 QVERIFY(glw1->isSharing());
1855 QVERIFY(glw1->context() != glw2->context());
1857 // Check that the first context's resource is also on the second.
1858 QVERIFY(qt_shared_test()->value(glw1->context()) == res1);
1859 QVERIFY(qt_shared_test()->value(glw2->context()) == res1);
1861 // Guard should still be the same.
1862 QVERIFY(guard.context() == glw1->context());
1863 QVERIFY(guard.id() == 3);
1865 // Check the sharing relationships.
1866 QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
1867 QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
1868 QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
1869 QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
1870 QVERIFY(!QGLContext::areSharing(0, glw2->context()));
1871 QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
1872 QVERIFY(!QGLContext::areSharing(0, 0));
1874 // Create a third context, not sharing with the others.
1875 QGLWidget *glw3 = new QGLWidget();
1876 QVERIFY(!glw3->isSharing());
1878 // Create a guard on the standalone context.
1879 QGLSharedResourceGuard guard3(glw3->context());
1882 // Request a resource to the third context.
1883 tst_QGLResource *res3 = qt_shared_test()->value(glw3->context());
1885 QVERIFY(qt_shared_test()->value(glw1->context()) == res1);
1886 QVERIFY(qt_shared_test()->value(glw2->context()) == res1);
1887 QVERIFY(qt_shared_test()->value(glw3->context()) == res3);
1889 // Check the sharing relationships again.
1890 QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
1891 QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
1892 QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
1893 QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
1894 QVERIFY(!QGLContext::areSharing(glw1->context(), glw3->context()));
1895 QVERIFY(!QGLContext::areSharing(glw2->context(), glw3->context()));
1896 QVERIFY(!QGLContext::areSharing(glw3->context(), glw1->context()));
1897 QVERIFY(!QGLContext::areSharing(glw3->context(), glw2->context()));
1898 QVERIFY(QGLContext::areSharing(glw3->context(), glw3->context()));
1899 QVERIFY(!QGLContext::areSharing(0, glw2->context()));
1900 QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
1901 QVERIFY(!QGLContext::areSharing(0, glw3->context()));
1902 QVERIFY(!QGLContext::areSharing(glw3->context(), 0));
1903 QVERIFY(!QGLContext::areSharing(0, 0));
1905 // Shared guard should still be the same.
1906 QVERIFY(guard.context() == glw1->context());
1907 QVERIFY(guard.id() == 3);
1909 // Delete the first context.
1912 // The second context should no longer register as sharing.
1913 QVERIFY(!glw2->isSharing());
1915 // The first context's resource should transfer to the second context.
1916 QCOMPARE(tst_QGLResource::deletions, 0);
1917 QVERIFY(qt_shared_test()->value(glw2->context()) == res1);
1918 QVERIFY(qt_shared_test()->value(glw3->context()) == res3);
1920 // Shared guard should now be the second context, with the id the same.
1921 QVERIFY(guard.context() == glw2->context());
1922 QVERIFY(guard.id() == 3);
1923 QVERIFY(guard3.context() == glw3->context());
1924 QVERIFY(guard3.id() == 5);
1926 // Clean up and check that the resources are properly deleted.
1928 QCOMPARE(tst_QGLResource::deletions, 1);
1930 QCOMPARE(tst_QGLResource::deletions, 2);
1932 // Guards should now be null and the id zero.
1933 QVERIFY(guard.context() == 0);
1934 QVERIFY(guard.id() == 0);
1935 QVERIFY(guard3.context() == 0);
1936 QVERIFY(guard3.id() == 0);
1941 // Tests QGLContext::bindTexture with default options
1942 #ifdef QT_BUILD_INTERNAL
1943 void tst_QGL::qglContextDefaultBindTexture()
1947 QGLContext *ctx = const_cast<QGLContext*>(w.context());
1949 QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
1950 boundImage->fill(0xFFFFFFFF);
1951 QPixmap *boundPixmap = new QPixmap(256, 256);
1952 boundPixmap->fill(Qt::red);
1954 int startCacheItemCount = QGLTextureCache::instance()->size();
1956 GLuint boundImageTextureId = ctx->bindTexture(*boundImage);
1957 GLuint boundPixmapTextureId = ctx->bindTexture(*boundPixmap);
1959 // Make sure the image & pixmap have been added to the cache:
1960 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
1962 // Make sure the image & pixmap have the is_cached flag set:
1963 QVERIFY(QImagePixmapCleanupHooks::isImageCached(*boundImage));
1964 QVERIFY(QImagePixmapCleanupHooks::isPixmapCached(*boundPixmap));
1966 // Make sure the texture IDs returned are valid:
1967 QCOMPARE((bool)glIsTexture(boundImageTextureId), GL_TRUE);
1968 QCOMPARE((bool)glIsTexture(boundPixmapTextureId), GL_TRUE);
1970 // Make sure the textures are still valid after we delete the image/pixmap:
1971 // Also check that although the textures are left intact, the cache entries are removed:
1974 QCOMPARE((bool)glIsTexture(boundImageTextureId), GL_TRUE);
1975 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
1978 QCOMPARE((bool)glIsTexture(boundPixmapTextureId), GL_TRUE);
1979 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
1981 // Finally, make sure QGLContext::deleteTexture deletes the texture IDs:
1982 ctx->deleteTexture(boundImageTextureId);
1983 ctx->deleteTexture(boundPixmapTextureId);
1984 QCOMPARE((bool)glIsTexture(boundImageTextureId), GL_FALSE);
1985 QCOMPARE((bool)glIsTexture(boundPixmapTextureId), GL_FALSE);
1989 #ifdef QT_BUILD_INTERNAL
1990 void tst_QGL::textureCleanup()
1997 // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
1998 // when the pixmap cache is cleared
2000 int startCacheItemCount = QGLTextureCache::instance()->size();
2003 QPixmap boundPixmap(":designer.png");
2005 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2007 p.drawPixmap(0, 0, boundPixmap);
2008 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2010 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2013 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2015 // Check that the texture doesn't get removed from the cache when the pixmap is cleared
2016 // as it should still be in the cache:
2017 boundPixmap = QPixmap();
2018 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2020 QPixmapCache::clear();
2021 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2024 // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
2025 // when they are explicitly removed from the pixmap cache
2027 int startCacheItemCount = QGLTextureCache::instance()->size();
2030 QPixmap boundPixmap(128, 128);
2031 QString cacheKey = QString::fromLatin1("myPixmap");
2032 QPixmapCache::insert(cacheKey, boundPixmap);
2034 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2036 p.drawPixmap(0, 0, boundPixmap);
2037 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2039 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2042 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2044 // Check that the texture doesn't get removed from the cache when the pixmap is cleared
2045 // as it should still be in the cache:
2046 boundPixmap = QPixmap();
2047 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2049 // Finally, we check that the texture cache entry is removed when we remove the
2050 // pixmap cache entry, which should hold the last reference:
2051 QPixmapCache::remove(cacheKey);
2052 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2055 // Check images & pixmaps are removed from the cache when they are deleted
2057 int startCacheItemCount = QGLTextureCache::instance()->size();
2060 QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
2061 boundImage->fill(0xFFFFFFFF);
2062 QPixmap *boundPixmap = new QPixmap(256, 256);
2063 boundPixmap->fill(Qt::red);
2065 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2067 p.drawImage(0, 0, *boundImage);
2068 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2070 p.drawPixmap(0, 0, *boundPixmap);
2071 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2073 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2076 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2080 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2084 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2087 // Check images & pixmaps are removed from the cache when they are assigned to
2089 int startCacheItemCount = QGLTextureCache::instance()->size();
2092 QImage boundImage(256, 256, QImage::Format_RGB32);
2093 boundImage.fill(0xFFFFFFFF);
2094 QPixmap boundPixmap(256, 256);
2095 boundPixmap.fill(Qt::red);
2097 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2099 p.drawImage(0, 0, boundImage);
2100 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2102 p.drawPixmap(0, 0, boundPixmap);
2103 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2105 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2108 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2110 boundImage = QImage(64, 64, QImage::Format_RGB32);
2111 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2113 boundPixmap = QPixmap(64, 64);
2114 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2117 // Check images & pixmaps are removed from the cache when they are modified (detached)
2119 int startCacheItemCount = QGLTextureCache::instance()->size();
2122 QImage boundImage(256, 256, QImage::Format_RGB32);
2123 boundImage.fill(0xFFFFFFFF);
2124 QPixmap boundPixmap(256, 256);
2125 boundPixmap.fill(Qt::red);
2127 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2129 p.drawImage(0, 0, boundImage);
2130 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2132 p.drawPixmap(0, 0, boundPixmap);
2133 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2135 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2138 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2140 boundImage.fill(0x00000000);
2141 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2143 boundPixmap.fill(Qt::blue);
2144 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2147 // Check that images/pixmaps aren't removed from the cache if a shallow copy has been made
2149 QPixmap copyOfPixmap;
2150 int startCacheItemCount = QGLTextureCache::instance()->size();
2154 QImage boundImage(256, 256, QImage::Format_RGB32);
2155 boundImage.fill(0xFFFFFFFF);
2156 QPixmap boundPixmap(256, 256);
2157 boundPixmap.fill(Qt::red);
2159 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2161 p.drawImage(0, 0, boundImage);
2162 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2164 p.drawPixmap(0, 0, boundPixmap);
2165 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2167 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2170 copyOfImage = boundImage;
2171 copyOfPixmap = boundPixmap;
2172 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2173 } // boundImage & boundPixmap would have been deleted when they went out of scope
2174 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2176 copyOfImage = QImage();
2177 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2179 copyOfPixmap = QPixmap();
2180 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2184 namespace ThreadImages {
2186 class Producer : public QObject
2194 QThread *thread = new QThread;
2197 connect(this, SIGNAL(destroyed()), thread, SLOT(quit()));
2199 moveToThread(thread);
2200 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2204 void imageReady(const QImage &image);
2207 void timerEvent(QTimerEvent *)
2209 QImage image(256, 256, QImage::Format_RGB32);
2210 QLinearGradient g(0, 0, 0, 256);
2211 g.setColorAt(0, QColor(255, 180, 180));
2212 g.setColorAt(1, Qt::white);
2213 g.setSpread(QGradient::ReflectSpread);
2216 brush.setTransform(QTransform::fromTranslate(0, delta));
2220 p.fillRect(image.rect(), brush);
2222 if (images.size() > 10)
2223 images.removeFirst();
2225 images.append(image);
2227 emit imageReady(image);
2231 QList<QImage> images;
2236 class DisplayWidget : public QGLWidget
2240 DisplayWidget(QWidget *parent) : QGLWidget(parent) {}
2241 void paintEvent(QPaintEvent *)
2244 p.drawImage(rect(), m_image);
2248 void setImage(const QImage &image)
2258 class Widget : public QWidget
2265 , producer(new Producer)
2268 connect(this, SIGNAL(destroyed()), producer, SLOT(deleteLater()));
2274 void timerEvent(QTimerEvent *)
2279 display = new DisplayWidget(this);
2280 connect(producer, SIGNAL(imageReady(const QImage &)), display, SLOT(setImage(const QImage &)));
2282 display->setGeometry(rect());
2287 DisplayWidget *display;
2293 void tst_QGL::threadImages()
2295 ThreadImages::Widget *widget = new ThreadImages::Widget;
2298 while (widget->iterations <= 5) {
2299 qApp->processEvents();
2305 void tst_QGL::nullRectCrash()
2307 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
2308 QSKIP("QGLFramebufferObject not supported on this platform");
2313 QGLFramebufferObjectFormat fboFormat;
2314 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
2316 QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat);
2318 QPainter fboPainter(fbo);
2320 fboPainter.setPen(QPen(QColor(255, 127, 127, 127), 2));
2321 fboPainter.setBrush(QColor(127, 255, 127, 127));
2322 fboPainter.drawRect(QRectF());
2328 #include "tst_qgl.moc"