捕获QML绘图缓冲区,不显示



我需要抓取每个QML (QtQuick 2)画框并通过网络发送它。目前我已经使用了下面列出的方法,但是这个方法有两个很大的缺点

1)由于Qt5文档grabWindow()函数有性能问题

2)它不能工作与隐藏的QML窗口

是否有可能在QQuickWindow::afterRendering后获得OpenGL渲染缓冲区?使用fbo ?共享opengl上下文?

class Grab: public QObject
{
 public:
 Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
 public slots:
    void Grabme()
    {
       QImage image = wnd_->grabWindow();
    }
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);

QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
                  &grab, &Grab::Grabme, Qt::DirectConnection );

return app.exec();
}

下面的例子可以抓取任何qml内容到FBO,然后通过信号将其作为图像发送。这种方法的唯一问题是可见性,抓取窗口必须是可见的才能成功抓取。如果有人知道如何防止这种情况,你可以帮助我,并提供更先进的方法。

// main.cpp
int main(int argc, char* argv[])
{
  QApplication app(argc, argv);
  GrabWindow grab;
  grab.setResizeMode( QQuickView::SizeViewToRootObject );
  grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
  grab.setFlags( Qt::Popup );
  grab.show();
  return app.exec();
}

// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
  Q_OBJECT
signals:
     void changeImage( const QImage &image );
public:
    GrabWindow( QWindow * parent = 0 );
private slots:
    void afterRendering();
    void beforeRendering();
private:
    QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
    QQuickView( parent )
{
  setClearBeforeRendering( false );
  setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
  connect( this, SIGNAL( afterRendering()  ), SLOT( afterRendering()  ), Qt::DirectConnection );
  connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
  if( !fbo_.isNull() )
  {
    emit changeImage( fbo_->toImage() );
  }
}
void GrabWindow::beforeRendering()
{
  if (!fbo_)
  {
        fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
        setRenderTarget(fbo_.data());
  }
}

Qt 5的后续版本。你也可以使用软件渲染后端。下面的代码在没有任何可见窗口或OpenGL技巧的情况下渲染背景中的任何场景:

// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
    const char *source = "qrc:/main.qml";
    if (argc > 1) source = argv[1];
    QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
    QGuiApplication app{argc, argv};
    QQuickRenderControl renderControl;
    QQuickWindow window{&renderControl};
    QQmlEngine engine;
    QQmlComponent component{
        &engine,
        QUrl{QString::fromUtf8(source)}
    };
    QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
    window.contentItem()->setSize(rootItem->size());
    rootItem->setParentItem(window.contentItem());
    window.resize(rootItem->size().width(), rootItem->size().height());
    QImage image = renderControl.grab();
    image.save("output.png");
    return 0;
}

我设法找到了一个技巧,使grabWindow()工作时,窗口是"不可见"。诀窍是设置窗口的visibility: Window.Minimizedflags: Qt.Tool。窗口不显示给用户,但Qt的内部似乎是可见的,grabWindow()方法调用按预期工作。记住,只有在场景被初始化后才能调用该方法。

这个解决方案的唯一问题(我遇到)是,如果窗口的color属性设置为transparent,捕获的内容具有黑色背景。

相关内容

  • 没有找到相关文章

最新更新