-
Suggestion
-
Resolution: Done
-
P2: Important
-
5.4.2, 5.9
-
None
-
Windows 7 x64, Qt 5.4.2 x64, Nvidia Quadro K3100M, recent drivers
-
2ea90c56f2924acc5c620ed7c29a48c72a42efd3
I have a hidden QOpenGLWidget I want to capture. This is only possible by doing something along those lines:
void GLWidget::drawOffscreen()
{
//the context should be valid. make sure it is current for painting
makeCurrent();
if (!m_isInitialized)
{
initializeGL();
resizeGL(width(), height());
}
if (!m_fbo || m_fbo->width() != width() || m_fbo->height() != height())
{
//allocate additional? FBO for rendering or resize it if widget size changed
delete m_fbo;
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
m_fbo = new QOpenGLFramebufferObject(width(), height(), format);
resizeGL(width(), height());
}
//#1 DOES NOT WORK: bind FBO and render() widget
m_fbo->bind();
QOpenGLPaintDevice fboPaintDev(width(), height());
QPainter painter(&fboPaintDev);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
render(&painter);
painter.end();
//You could now grab the content of the framebuffer we've rendered to
QImage image1 = m_fbo->toImage();
image1.save(QString("fb1.png"));
m_fbo->release();
//#1 --------------------------------------------------------------
//#2 WORKS: bind FBO and render stuff with paintGL() call
m_fbo->bind();
paintGL();
//You could now grab the content of the framebuffer we've rendered to
QImage image2 = m_fbo->toImage();
image2.save(QString("fb2.png"));
m_fbo->release();
//#2 --------------------------------------------------------------
//bind default framebuffer again. not sure if this necessary and isn't supposed to use defaultFramebuffer()...
m_fbo->bindDefault();
doneCurrent();
}
I had expected due to QOpenGLWidget being backed by a FBO the widget would render() just fine, even without an extra FBO.
Now, Additionally inside paintGL, when using QPainter to paint, this only works when the widget is visible. If the widget is hidden you have to provide a QOpenGLPaintDevice in paintGL():
void GLWidget::paintGL()
{
//When doing mixed QPainter/OpenGL rendering make sure to use a QOpenGLPaintDevice, otherwise only OpenGL content is visible!
//I'm not sure why, because according to the docs (http://doc.qt.io/qt-5/topics-graphics.html) this is supposed to be the same...
QOpenGLPaintDevice fboPaintDev(width(), height());
QPainter painter(&fboPaintDev);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
//This is what you'd use (and what would work) if the widget was visible
//QPainter painter;
//painter.begin(this);
//now start OpenGL painting
painter.beginNativePainting();
glClearColor(0.5f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
...
painter.endNativePainting();
//draw non-OpenGL stuff with QPainter
painter.drawText(20, 40, "Foo");
...
painter.end();
}
What is strange about this, is that when using a QGraphicsView with a QOpenGLWidget set as its viewport, the following code works, even with mixed OpenGL + non-OpenGL QGraphicsItems:
MainWindow::MainWindow()
{
scene = new QGraphicsScene;
hiddenView = new QGraphicsView(scene);
hiddenGLWidget = new QOpenGLWidget;
hiddenView->setViewport(hiddenGLWidget);
//hiddenView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
//hiddenView->show();
}
void MainWindow::screenshot()
{
//DOES NOT WORK: try regular grab functions
QPixmap pixmap1 = hiddenView->grab(); //image with scrollbars, no OpenGL content
pixmap1.save("bla1.png");
QPixmap pixmap2 = hiddenGLWidget->grab(); //produces an empty image
pixmap2.save("bla2.png");
//try grabbing only the QOpenGLWidget framebuffer
QImage image1 = hiddenGLWidget->grabFramebuffer(); //null image
image1.save("bla3.png");
//WORKS: render via FBO
hiddenGLWidget->makeCurrent();
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
QOpenGLFramebufferObject * fbo = new QOpenGLFramebufferObject(hiddenView->width(), hiddenView->height(), format);
fbo->bind();
QOpenGLPaintDevice fboPaintDev(hiddenView->width(), hiddenView->height());
QPainter painter(&fboPaintDev);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
hiddenView->render(&painter); //WORKS and captures mixed OpenGL and non-OpenGL QGraphicsitems
//hiddenView->repaint(); //does not work
//hiddenView->scene()->render(&painter); //does not work
//hiddenGLWidget->paintGL(); //might work. can not call, protected
//hiddenGLWidget->render(&painter); //does not work
//hiddenGLWidget->repaint(); //does not work
painter.end();
QImage image2 = fbo->toImage();
image2.save("bla4.png");
fbo->release();
delete fbo;
}
UPDATE with "MISSING INFO": I have roled my own class now, base on QWindow and createWindowContainer(), because QOpenGLWidget just wasn't working in my case. You can see how I did it at the end of this StackOverflow question. The new widget works fine when rendering while it's hidden.
- relates to
-
QTBUG-61280 Hidden floating QOpenGLWidget produces a warning when calling render() on the MainWindow
-
- Closed
-
-
QTBUG-61295 grab() for QGraphicsView backed by QOpenGLWidget viewport
-
- Open
-
| For Gerrit Dashboard: QTBUG-47185 | ||||||
|---|---|---|---|---|---|---|
| # | Subject | Branch | Project | Status | CR | V |
| 196713,5 | Start supporting purely offscreen QOpenGLWidget | dev | qt/qtbase | Status: MERGED | +2 | 0 |
| 196747,6 | Support grab() for QOpenGLWidget-backed QGraphicsViews | dev | qt/qtbase | Status: DEFERRED | -2 | 0 |