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

source code of qtbase/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp