diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index a852d4a4747..a83884ca03a 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -791,10 +791,8 @@ void QOpenGLWidgetPrivate::initialize() // texture usable by the underlying window's backingstore. QWidget *tlw = q->window(); QOpenGLContext *shareContext = get(tlw)->shareContext(); - if (Q_UNLIKELY(!shareContext)) { - qWarning("QOpenGLWidget: Cannot be used without a context shared with the toplevel."); - return; - } + // If shareContext is null, showing content on-screen will not work. + // However, offscreen rendering and grabFramebuffer() will stay fully functional. // Do not include the sample count. Requesting a multisampled context is not necessary // since we render into an FBO, never to an actual surface. What's more, attempting to @@ -804,9 +802,11 @@ void QOpenGLWidgetPrivate::initialize() requestedFormat.setSamples(0); QScopedPointer ctx(new QOpenGLContext); - ctx->setShareContext(shareContext); ctx->setFormat(requestedFormat); - ctx->setScreen(shareContext->screen()); + if (shareContext) { + ctx->setShareContext(shareContext); + ctx->setScreen(shareContext->screen()); + } if (Q_UNLIKELY(!ctx->create())) { qWarning("QOpenGLWidget: Failed to create context"); return; @@ -815,7 +815,9 @@ void QOpenGLWidgetPrivate::initialize() // Propagate settings that make sense only for the tlw. Note that this only // makes sense for properties that get picked up even after the native // window is created. - QSurfaceFormat tlwFormat = tlw->windowHandle()->format(); + QSurfaceFormat tlwFormat; + if (tlw->windowHandle()) + tlwFormat = tlw->windowHandle()->format(); if (requestedFormat.swapInterval() != tlwFormat.swapInterval()) { // Most platforms will pick up the changed swap interval on the next // makeCurrent or swapBuffers. @@ -918,9 +920,14 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ QImage QOpenGLWidgetPrivate::grabFramebuffer() { Q_Q(QOpenGLWidget); + + initialize(); if (!initialized) return QImage(); + if (!fbo) // could be completely offscreen, without ever getting a resize event + recreateFbo(); + if (!inPaintGL) render(); @@ -1371,7 +1378,7 @@ int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const if (window) return int(window->devicePixelRatio() * devicePixelRatioFScale()); else - return 1.0; + return int(devicePixelRatioFScale()); default: qWarning("QOpenGLWidget::metric(): unknown metric %d", metric); return 0; @@ -1422,7 +1429,14 @@ bool QOpenGLWidget::event(QEvent *e) d->reset(); // FALLTHROUGH case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too - if (!d->initialized && !size().isEmpty() && window() && window()->windowHandle()) { + if (d->initialized && window()->windowHandle() + && d->context->shareContext() != QWidgetPrivate::get(window())->shareContext()) + { + // Special case: did grabFramebuffer() for a hidden widget that then became visible. + // Recreate all resources since the context now needs to share with the TLW's. + d->reset(); + } + if (!d->initialized && !size().isEmpty() && window()->windowHandle()) { d->initialize(); if (d->initialized) d->recreateFbo(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 37114449ba8..370ae2aed15 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -12229,10 +12229,9 @@ QOpenGLContext *QWidgetPrivate::shareContext() const #ifdef QT_NO_OPENGL return 0; #else - if (Q_UNLIKELY(!extra || !extra->topextra || !extra->topextra->window)) { - qWarning("Asking for share context for widget that does not have a window handle"); + if (Q_UNLIKELY(!extra || !extra->topextra || !extra->topextra->window)) return 0; - } + QWidgetPrivate *that = const_cast(this); if (!extra->topextra->shareContext) { QOpenGLContext *ctx = new QOpenGLContext; diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index c7fca550e52..510576f6df7 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -61,6 +61,8 @@ private slots: void showHide(); void nativeWindow(); void stackWidgetOpaqueChildIsVisible(); + void offscreen(); + void offscreenThenOnscreen(); }; void tst_QOpenGLWidget::initTestCase() @@ -572,6 +574,74 @@ void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() #undef VERIFY_COLOR } +void tst_QOpenGLWidget::offscreen() +{ + { + QScopedPointer w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image = w->grabFramebuffer(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // QWidget::grab() should eventually end up in grabFramebuffer() as well + { + QScopedPointer w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QPixmap pm = w->grab(); + QImage image = pm.toImage(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // ditto for QWidget::render() + { + QScopedPointer w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image(800, 600, QImage::Format_ARGB32); + w->render(&image); + + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } +} + +void tst_QOpenGLWidget::offscreenThenOnscreen() +{ + QScopedPointer w(new ClearWidget(0, 800, 600)); + w->resize(800, 600); + + w->setClearColor(0, 0, 1); + QImage image = w->grabFramebuffer(); + + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + + // now let's make things more challenging: show. Internally this needs + // recreating the context. + w->show(); + QTest::qWaitForWindowExposed(w.data()); + + image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); +} + QTEST_MAIN(tst_QOpenGLWidget) #include "tst_qopenglwidget.moc"