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 <QtWidgets/QOpenGLWidget>
30#include <QtGui/QOpenGLFunctions>
31#include <QtGui/QPainter>
32#include <QtGui/QScreen>
33#include <QtGui/QStaticText>
34#include <QtWidgets/QDesktopWidget>
35#include <QtWidgets/QGraphicsView>
36#include <QtWidgets/QGraphicsScene>
37#include <QtWidgets/QGraphicsRectItem>
38#include <QtWidgets/QVBoxLayout>
39#include <QtWidgets/QPushButton>
40#include <QtWidgets/QStackedWidget>
41#include <QtTest/QtTest>
42#include <QSignalSpy>
43#include <private/qguiapplication_p.h>
44#include <private/qstatictext_p.h>
45#include <private/qopengltextureglyphcache_p.h>
46#include <qpa/qplatformintegration.h>
47
48class tst_QOpenGLWidget : public QObject
49{
50 Q_OBJECT
51
52private slots:
53 void initTestCase();
54 void create();
55 void clearAndGrab();
56 void clearAndResizeAndGrab();
57 void createNonTopLevel();
58 void painter();
59 void reparentToAlreadyCreated();
60 void reparentToNotYetCreated();
61 void reparentHidden();
62 void asViewport();
63 void requestUpdate();
64 void fboRedirect();
65 void showHide();
66 void nativeWindow();
67 void stackWidgetOpaqueChildIsVisible();
68 void offscreen();
69 void offscreenThenOnscreen();
70
71#ifdef QT_BUILD_INTERNAL
72 void staticTextDanglingPointer();
73#endif
74};
75
76void tst_QOpenGLWidget::initTestCase()
77{
78 // See QOpenGLWidget constructor
79 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
80 QSKIP("QOpenGLWidget is not supported on this platform.");
81}
82
83void tst_QOpenGLWidget::create()
84{
85 QScopedPointer<QOpenGLWidget> w(new QOpenGLWidget);
86 QVERIFY(!w->isValid());
87 QVERIFY(w->textureFormat() == 0);
88 QSignalSpy frameSwappedSpy(w.data(), SIGNAL(frameSwapped()));
89 w->show();
90 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
91 QVERIFY(frameSwappedSpy.count() > 0);
92
93 QVERIFY(w->isValid());
94 QVERIFY(w->context());
95 QCOMPARE(w->context()->format(), w->format());
96 QVERIFY(w->defaultFramebufferObject() != 0);
97 QVERIFY(w->textureFormat() != 0);
98}
99
100class ClearWidget : public QOpenGLWidget, protected QOpenGLFunctions
101{
102public:
103 ClearWidget(QWidget *parent, int expectedWidth, int expectedHeight)
104 : QOpenGLWidget(parent),
105 m_initCalled(false), m_paintCalled(false), m_resizeCalled(false),
106 m_resizeOk(false),
107 m_w(expectedWidth), m_h(expectedHeight),
108 r(1.0f), g(0.0f), b(0.0f) { }
109
110 void initializeGL() override {
111 m_initCalled = true;
112 initializeOpenGLFunctions();
113 }
114 void paintGL() override {
115 m_paintCalled = true;
116 glClearColor(r, g, b, 1.0f);
117 glClear(GL_COLOR_BUFFER_BIT);
118 }
119 void resizeGL(int w, int h) override {
120 m_resizeCalled = true;
121 m_resizeOk = w == m_w && h == m_h;
122 }
123 void setClearColor(float r, float g, float b) {
124 this->r = r; this->g = g; this->b = b;
125 }
126
127 bool m_initCalled;
128 bool m_paintCalled;
129 bool m_resizeCalled;
130 bool m_resizeOk;
131 int m_w;
132 int m_h;
133 float r, g, b;
134};
135
136void tst_QOpenGLWidget::clearAndGrab()
137{
138 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
139 w->resize(800, 600);
140 w->show();
141 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
142 QVERIFY(w->m_initCalled);
143 QVERIFY(w->m_resizeCalled);
144 QVERIFY(w->m_resizeOk);
145 QVERIFY(w->m_paintCalled);
146
147 QImage image = w->grabFramebuffer();
148 QVERIFY(!image.isNull());
149 QCOMPARE(image.width(), w->width());
150 QCOMPARE(image.height(), w->height());
151 QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
152}
153
154void tst_QOpenGLWidget::clearAndResizeAndGrab()
155{
156 QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480));
157 w->resize(640, 480);
158 w->show();
159 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
160
161 QImage image = w->grabFramebuffer();
162 QVERIFY(!image.isNull());
163 QCOMPARE(image.width(), w->width());
164 QCOMPARE(image.height(), w->height());
165 w->resize(800, 600);
166 image = w->grabFramebuffer();
167 QVERIFY(!image.isNull());
168 QCOMPARE(image.width(), 800);
169 QCOMPARE(image.height(), 600);
170 QCOMPARE(image.width(), w->width());
171 QCOMPARE(image.height(), w->height());
172 QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
173}
174
175void tst_QOpenGLWidget::createNonTopLevel()
176{
177 QWidget w;
178 ClearWidget *glw = new ClearWidget(&w, 600, 700);
179 QSignalSpy frameSwappedSpy(glw, SIGNAL(frameSwapped()));
180 w.resize(400, 400);
181 w.show();
182 QVERIFY(QTest::qWaitForWindowExposed(&w));
183 QVERIFY(frameSwappedSpy.count() > 0);
184
185 QVERIFY(glw->m_resizeCalled);
186 glw->m_resizeCalled = false;
187 QVERIFY(!glw->m_resizeOk);
188 glw->resize(600, 700);
189
190 QVERIFY(glw->m_initCalled);
191 QVERIFY(glw->m_resizeCalled);
192 QVERIFY(glw->m_resizeOk);
193 QVERIFY(glw->m_paintCalled);
194
195 QImage image = glw->grabFramebuffer();
196 QVERIFY(!image.isNull());
197 QCOMPARE(image.width(), glw->width());
198 QCOMPARE(image.height(), glw->height());
199 QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
200
201 glw->doneCurrent();
202 QVERIFY(!QOpenGLContext::currentContext());
203 glw->makeCurrent();
204 QVERIFY(QOpenGLContext::currentContext() == glw->context() && glw->context());
205}
206
207class PainterWidget : public QOpenGLWidget, protected QOpenGLFunctions
208{
209public:
210 PainterWidget(QWidget *parent)
211 : QOpenGLWidget(parent), m_clear(false) { }
212
213 void initializeGL() override {
214 initializeOpenGLFunctions();
215 }
216 void paintGL() override {
217 QPainter p(this);
218 QCOMPARE(p.device()->width(), width());
219 QCOMPARE(p.device()->height(), height());
220 p.fillRect(QRect(QPoint(0, 0), QSize(width(), height())), Qt::blue);
221 if (m_clear) {
222 p.beginNativePainting();
223 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
224 glClear(GL_COLOR_BUFFER_BIT);
225 p.endNativePainting();
226 }
227 }
228 bool m_clear;
229};
230
231void tst_QOpenGLWidget::painter()
232{
233 QWidget w;
234 PainterWidget *glw = new PainterWidget(&w);
235 w.resize(640, 480);
236 glw->resize(320, 200);
237 w.show();
238 QVERIFY(QTest::qWaitForWindowExposed(&w));
239
240 QImage image = glw->grabFramebuffer();
241 QCOMPARE(image.width(), glw->width());
242 QCOMPARE(image.height(), glw->height());
243 QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
244
245 glw->m_clear = true;
246 image = glw->grabFramebuffer();
247 QVERIFY(image.pixel(20, 10) == qRgb(0, 255, 0));
248
249 QPixmap pix = glw->grab(); // QTBUG-61036
250}
251
252void tst_QOpenGLWidget::reparentToAlreadyCreated()
253{
254 QWidget w1;
255 PainterWidget *glw = new PainterWidget(&w1);
256 w1.resize(640, 480);
257 glw->resize(320, 200);
258 w1.show();
259 QVERIFY(QTest::qWaitForWindowExposed(&w1));
260
261 QWidget w2;
262 w2.show();
263 QVERIFY(QTest::qWaitForWindowExposed(&w2));
264
265 glw->setParent(&w2);
266 glw->show();
267
268 QImage image = glw->grabFramebuffer();
269 QCOMPARE(image.width(), 320);
270 QCOMPARE(image.height(), 200);
271 QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
272}
273
274void tst_QOpenGLWidget::reparentToNotYetCreated()
275{
276 QWidget w1;
277 PainterWidget *glw = new PainterWidget(&w1);
278 w1.resize(640, 480);
279 glw->resize(320, 200);
280 w1.show();
281 QVERIFY(QTest::qWaitForWindowExposed(&w1));
282
283 QWidget w2;
284 glw->setParent(&w2);
285 w2.show();
286 QVERIFY(QTest::qWaitForWindowExposed(&w2));
287
288 QImage image = glw->grabFramebuffer();
289 QCOMPARE(image.width(), 320);
290 QCOMPARE(image.height(), 200);
291 QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255));
292}
293
294void tst_QOpenGLWidget::reparentHidden()
295{
296 // Tests QTBUG-60896
297 QWidget topLevel1;
298
299 QWidget *container = new QWidget(&topLevel1);
300 PainterWidget *glw = new PainterWidget(container);
301 topLevel1.resize(640, 480);
302 glw->resize(320, 200);
303 topLevel1.show();
304
305 glw->hide(); // Explicitly hidden
306
307 QVERIFY(QTest::qWaitForWindowExposed(&topLevel1));
308
309 QWidget topLevel2;
310 topLevel2.resize(640, 480);
311 topLevel2.show();
312 QVERIFY(QTest::qWaitForWindowExposed(&topLevel2));
313
314 QOpenGLContext *originalContext = glw->context();
315 QVERIFY(originalContext);
316
317 container->setParent(&topLevel2);
318 glw->show(); // Should get a new context now
319
320 QOpenGLContext *newContext = glw->context();
321 QVERIFY(originalContext != newContext);
322}
323
324class CountingGraphicsView : public QGraphicsView
325{
326public:
327 CountingGraphicsView(): m_count(0) { }
328 int paintCount() const { return m_count; }
329 void resetPaintCount() { m_count = 0; }
330
331protected:
332 void drawForeground(QPainter *, const QRectF &) override;
333 int m_count;
334};
335
336void CountingGraphicsView::drawForeground(QPainter *, const QRectF &)
337{
338 ++m_count;
339
340 // QTBUG-59318: verify that the context's internal default fbo redirection
341 // is active also when using the QOpenGLWidget as a viewport.
342 GLint currentFbo = -1;
343 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFbo);
344 GLuint defFbo = QOpenGLContext::currentContext()->defaultFramebufferObject();
345 QCOMPARE(GLuint(currentFbo), defFbo);
346}
347
348void tst_QOpenGLWidget::asViewport()
349{
350 // Have a QGraphicsView with a QOpenGLWidget as its viewport.
351 QGraphicsScene scene;
352 scene.addItem(new QGraphicsRectItem(10, 10, 100, 100));
353 CountingGraphicsView *view = new CountingGraphicsView;
354 view->setScene(&scene);
355 view->setViewport(new QOpenGLWidget);
356 QWidget widget;
357 QVBoxLayout *layout = new QVBoxLayout;
358 layout->addWidget(view);
359 QPushButton *btn = new QPushButton("Test");
360 layout->addWidget(btn);
361 widget.setLayout(layout);
362 widget.show();
363 QVERIFY(QTest::qWaitForWindowExposed(&widget));
364
365 QVERIFY(view->paintCount() > 0);
366 view->resetPaintCount();
367
368 // And now trigger a repaint on the push button. We must not
369 // receive paint events for the graphics view. If we do, that's a
370 // side effect of QOpenGLWidget's special behavior and handling in
371 // the widget stack.
372 btn->update();
373 qApp->processEvents();
374 QCOMPARE(view->paintCount(), 0);
375}
376
377class PaintCountWidget : public QOpenGLWidget
378{
379public:
380 PaintCountWidget() : m_count(0) { }
381 void reset() { m_count = 0; }
382 void paintGL() override { ++m_count; }
383 int m_count;
384};
385
386void tst_QOpenGLWidget::requestUpdate()
387{
388 PaintCountWidget w;
389 w.resize(640, 480);
390 w.show();
391 QVERIFY(QTest::qWaitForWindowExposed(&w));
392
393 w.reset();
394 QCOMPARE(w.m_count, 0);
395
396 w.windowHandle()->requestUpdate();
397 QTRY_VERIFY(w.m_count > 0);
398}
399
400class FboCheckWidget : public QOpenGLWidget
401{
402public:
403 void paintGL() override {
404 GLuint reportedDefaultFbo = QOpenGLContext::currentContext()->defaultFramebufferObject();
405 GLuint expectedDefaultFbo = defaultFramebufferObject();
406 QCOMPARE(reportedDefaultFbo, expectedDefaultFbo);
407 }
408};
409
410void tst_QOpenGLWidget::fboRedirect()
411{
412 FboCheckWidget w;
413 w.resize(640, 480);
414 w.show();
415 QVERIFY(QTest::qWaitForWindowExposed(&w));
416
417 // Unlike in paintGL(), the default fbo reported by the context is not affected by the widget,
418 // so we get the real default fbo: either 0 or (on iOS) the fbo associated with the window.
419 w.makeCurrent();
420 GLuint reportedDefaultFbo = QOpenGLContext::currentContext()->defaultFramebufferObject();
421 GLuint widgetFbo = w.defaultFramebufferObject();
422 QVERIFY(reportedDefaultFbo != widgetFbo);
423}
424
425void tst_QOpenGLWidget::showHide()
426{
427 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
428 w->resize(800, 600);
429 w->show();
430 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
431
432 w->hide();
433
434 QImage image = w->grabFramebuffer();
435 QVERIFY(!image.isNull());
436 QCOMPARE(image.width(), w->width());
437 QCOMPARE(image.height(), w->height());
438 QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
439
440 w->setClearColor(0, 0, 1);
441 w->show();
442 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
443
444 image = w->grabFramebuffer();
445 QVERIFY(!image.isNull());
446 QCOMPARE(image.width(), w->width());
447 QCOMPARE(image.height(), w->height());
448 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
449}
450
451void tst_QOpenGLWidget::nativeWindow()
452{
453 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
454 w->resize(800, 600);
455 w->show();
456 w->winId();
457 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
458
459 QImage image = w->grabFramebuffer();
460 QVERIFY(!image.isNull());
461 QCOMPARE(image.width(), w->width());
462 QCOMPARE(image.height(), w->height());
463 QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
464 QVERIFY(w->internalWinId());
465
466 // Now as a native child.
467 QWidget nativeParent;
468 nativeParent.resize(800, 600);
469 nativeParent.setAttribute(Qt::WA_NativeWindow);
470 ClearWidget *child = new ClearWidget(0, 800, 600);
471 child->setClearColor(0, 1, 0);
472 child->setParent(&nativeParent);
473 child->resize(400, 400);
474 child->move(23, 34);
475 nativeParent.show();
476 QVERIFY(QTest::qWaitForWindowExposed(&nativeParent));
477
478 QVERIFY(nativeParent.internalWinId());
479 QVERIFY(!child->internalWinId());
480
481 image = child->grabFramebuffer();
482 QVERIFY(!image.isNull());
483 QCOMPARE(image.width(), child->width());
484 QCOMPARE(image.height(), child->height());
485 QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0));
486}
487
488static inline QString msgRgbMismatch(unsigned actual, unsigned expected)
489{
490 return QString::asprintf("Color mismatch, %#010x != %#010x", actual, expected);
491}
492
493static QPixmap grabWidgetWithoutRepaint(const QWidget *widget, QRect clipArea)
494{
495 const QWidget *targetWidget = widget;
496#ifdef Q_OS_WIN
497 // OpenGL content is not properly grabbed on Windows when passing a top level widget window,
498 // because GDI functions can't grab OpenGL layer content.
499 // Instead the whole screen should be captured, with an adjusted clip area, which contains
500 // the final composited content.
501 QDesktopWidget *desktopWidget = QApplication::desktop();
502 const QWidget *mainScreenWidget = desktopWidget->screen();
503 targetWidget = mainScreenWidget;
504 clipArea = QRect(widget->mapToGlobal(clipArea.topLeft()),
505 widget->mapToGlobal(clipArea.bottomRight()));
506#endif
507
508 const QWindow *window = targetWidget->window()->windowHandle();
509 Q_ASSERT(window);
510 WId windowId = window->winId();
511
512 QScreen *screen = window->screen();
513 Q_ASSERT(screen);
514
515 const QSize size = clipArea.size();
516 const QPixmap result = screen->grabWindow(windowId,
517 clipArea.x(),
518 clipArea.y(),
519 size.width(),
520 size.height());
521 return result;
522}
523
524#define VERIFY_COLOR(child, region, color) verifyColor(child, region, color, __LINE__)
525
526bool verifyColor(const QWidget *widget, const QRect &clipArea, const QColor &color, int callerLine)
527{
528 for (int t = 0; t < 6; t++) {
529 const QPixmap pixmap = grabWidgetWithoutRepaint(widget, clipArea);
530 if (!QTest::qCompare(pixmap.size(),
531 clipArea.size(),
532 "pixmap.size()",
533 "rect.size()",
534 __FILE__,
535 callerLine))
536 return false;
537
538
539 const QImage image = pixmap.toImage();
540 QPixmap expectedPixmap(pixmap); /* ensure equal formats */
541 expectedPixmap.detach();
542 expectedPixmap.fill(color);
543
544 uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0;
545 uint firstPixel = image.pixel(0,0) | alphaCorrection;
546
547 // Retry a couple of times. Some window managers have transparency animation, or are
548 // just slow to render.
549 if (t < 5) {
550 if (firstPixel == QColor(color).rgb()
551 && image == expectedPixmap.toImage())
552 return true;
553 else
554 QTest::qWait(200);
555 } else {
556 if (!QTest::qVerify(firstPixel == QColor(color).rgb(),
557 "firstPixel == QColor(color).rgb()",
558 qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())),
559 __FILE__, callerLine)) {
560 return false;
561 }
562 if (!QTest::qVerify(image == expectedPixmap.toImage(),
563 "image == expectedPixmap.toImage()",
564 "grabbed pixmap differs from expected pixmap",
565 __FILE__, callerLine)) {
566 return false;
567 }
568 }
569 }
570
571 return false;
572}
573
574void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible()
575{
576#ifdef Q_OS_OSX
577 QSKIP("QScreen::grabWindow() doesn't work properly on OSX HighDPI screen: QTBUG-46803");
578 return;
579#endif
580
581 QStackedWidget stack;
582
583 QWidget* emptyWidget = new QWidget(&stack);
584 stack.addWidget(emptyWidget);
585
586 // Create an opaque red QOpenGLWidget.
587 const int dimensionSize = 400;
588 ClearWidget* clearWidget = new ClearWidget(&stack, dimensionSize, dimensionSize);
589 clearWidget->setAttribute(Qt::WA_OpaquePaintEvent);
590 stack.addWidget(clearWidget);
591
592 // Show initial QWidget.
593 stack.setCurrentIndex(0);
594 stack.resize(dimensionSize, dimensionSize);
595 stack.show();
596 QVERIFY(QTest::qWaitForWindowExposed(&stack));
597 QVERIFY(QTest::qWaitForWindowActive(&stack));
598
599 // Switch to the QOpenGLWidget.
600 stack.setCurrentIndex(1);
601 QTRY_COMPARE(clearWidget->m_paintCalled, true);
602
603 // Resize the tested region to be half size in the middle, because some OSes make the widget
604 // have rounded corners (e.g. OSX), and the grabbed window pixmap will not coincide perfectly
605 // with what was actually painted.
606 QRect clipArea = stack.rect();
607 clipArea.setSize(clipArea.size() / 2);
608 const int translationOffsetToMiddle = dimensionSize / 4;
609 clipArea.translate(translationOffsetToMiddle, translationOffsetToMiddle);
610
611 // Verify that the QOpenGLWidget was actually painted AND displayed.
612 const QColor red(255, 0, 0, 255);
613 VERIFY_COLOR(&stack, clipArea, red);
614 #undef VERIFY_COLOR
615}
616
617void tst_QOpenGLWidget::offscreen()
618{
619 {
620 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
621 w->resize(800, 600);
622
623 w->setClearColor(0, 0, 1);
624 QImage image = w->grabFramebuffer();
625
626 QVERIFY(!image.isNull());
627 QCOMPARE(image.width(), w->width());
628 QCOMPARE(image.height(), w->height());
629 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
630 }
631
632 // QWidget::grab() should eventually end up in grabFramebuffer() as well
633 {
634 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
635 w->resize(800, 600);
636
637 w->setClearColor(0, 0, 1);
638 QPixmap pm = w->grab();
639 QImage image = pm.toImage();
640
641 QVERIFY(!image.isNull());
642 QCOMPARE(image.width(), w->width());
643 QCOMPARE(image.height(), w->height());
644 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
645 }
646
647 // ditto for QWidget::render()
648 {
649 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
650 w->resize(800, 600);
651
652 w->setClearColor(0, 0, 1);
653 QImage image(800, 600, QImage::Format_ARGB32);
654 w->render(&image);
655
656 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
657 }
658}
659
660void tst_QOpenGLWidget::offscreenThenOnscreen()
661{
662 QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600));
663 w->resize(800, 600);
664
665 w->setClearColor(0, 0, 1);
666 QImage image = w->grabFramebuffer();
667
668 QVERIFY(!image.isNull());
669 QCOMPARE(image.width(), w->width());
670 QCOMPARE(image.height(), w->height());
671 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
672
673 // now let's make things more challenging: show. Internally this needs
674 // recreating the context.
675 w->show();
676 QVERIFY(QTest::qWaitForWindowExposed(w.data()));
677
678 image = w->grabFramebuffer();
679 QVERIFY(!image.isNull());
680 QCOMPARE(image.width(), w->width());
681 QCOMPARE(image.height(), w->height());
682 QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255));
683}
684
685class StaticTextPainterWidget : public QOpenGLWidget
686{
687public:
688 StaticTextPainterWidget(QWidget *parent = nullptr)
689 : QOpenGLWidget(parent)
690 {
691 }
692
693 void paintEvent(QPaintEvent *)
694 {
695 QPainter p(this);
696 text.setText(QStringLiteral("test"));
697 p.drawStaticText(0, 0, text);
698
699 ctx = QOpenGLContext::currentContext();
700 }
701
702 QStaticText text;
703 QOpenGLContext *ctx;
704};
705
706#ifdef QT_BUILD_INTERNAL
707void tst_QOpenGLWidget::staticTextDanglingPointer()
708{
709 QWidget w;
710 StaticTextPainterWidget *glw = new StaticTextPainterWidget(&w);
711 w.resize(640, 480);
712 glw->resize(320, 200);
713 w.show();
714
715 QVERIFY(QTest::qWaitForWindowExposed(&w));
716 QStaticTextPrivate *d = QStaticTextPrivate::get(&glw->text);
717
718 QCOMPARE(d->itemCount, 1);
719 QFontEngine *fe = d->items->fontEngine();
720
721 for (int i = QFontEngine::Format_None; i <= QFontEngine::Format_ARGB; ++i) {
722 QOpenGLTextureGlyphCache *cache =
723 (QOpenGLTextureGlyphCache *) fe->glyphCache(glw->ctx,
724 QFontEngine::GlyphFormat(i),
725 QTransform());
726 if (cache != nullptr)
727 QCOMPARE(cache->paintEnginePrivate(), nullptr);
728 }
729}
730#endif
731
732QTEST_MAIN(tst_QOpenGLWidget)
733
734#include "tst_qopenglwidget.moc"
735