История изменений
Исправление kuzulis, (текущая версия) :
аналог твоего кода для виджетов делается ещё быстрее и проще
Где там оно быстрее и проще?
QQuickRenderControl::grab() внутри дергает то же самое что и QOpenGLFramebufferObject::toImage(), через qt_gl_read_framebuffer(...).
Набросал тут код чтобы мониторить конец перерисовки окна и после этого граббить содержимое нужного айтема (со всеми его детьми):
#include "grabber.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QSGRootNode>
#include <QOpenGLFramebufferObject>
#include <private/qquickitem_p.h>
#include <private/qsgrenderer_p.h>
class BindableFbo final : public QSGBindable
{
public:
explicit BindableFbo(QOpenGLFramebufferObject *fbo)
: m_fbo(fbo)
{ }
void bind() const final { m_fbo->bind(); }
private:
QOpenGLFramebufferObject *m_fbo = nullptr;
};
Grabber::Grabber(QQuickItem *parent)
: QQuickItem(parent)
{
setFlag(QQuickItem::ItemHasContents);
connect(this, &Grabber::windowChanged,
this, &Grabber::scheduleWindowChange);
}
void Grabber::setSourceItem(QQuickItem *sourceItem)
{
if (sourceItem == m_sourceItem)
return;
m_sourceItem = sourceItem;
emit sourceItemChanged(m_sourceItem);
update();
}
QQuickItem *Grabber::sourceItem() const
{
return m_sourceItem;
}
QSGNode *Grabber::updatePaintNode(QSGNode *oldNode,
UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData);
if (!m_sourceItem)
return oldNode;
m_running = true;
const auto itemNode = QQuickItemPrivate::get(m_sourceItem)->itemNode();
const auto parentNode = itemNode->parent();
const auto siblingNode = itemNode->previousSibling();
if (parentNode)
parentNode->removeChildNode(itemNode);
const QScopedPointer<QSGRenderer> renderer(
QQuickItemPrivate::get(this)->
sceneGraphRenderContext()->createRenderer());
QSGRootNode rootNode;
rootNode.appendChildNode(itemNode);
renderer->setRootNode(&rootNode);
const QSize size(m_sourceItem->width(), m_sourceItem->height());
renderer->setDeviceRect(size);
renderer->setViewportRect(size);
renderer->setProjectionMatrixToRect(QRectF(QPointF(), size));
renderer->setClearColor(Qt::transparent);
QOpenGLFramebufferObject fbo(size);
renderer->renderScene(BindableFbo(&fbo));
fbo.release();
rootNode.removeChildNode(itemNode);
if (parentNode) {
if (siblingNode) {
parentNode->insertChildNodeAfter(itemNode, siblingNode);
} else {
parentNode->prependChildNode(itemNode);
}
}
QElapsedTimer et;
et.start();
const QImage image = fbo.toImage(); // TOO LONG!
static int count = 0;
qDebug() << "Elapsed:" << et.elapsed() << count;
++count;
//image.save(tr("test%1.png").arg(count++));
return oldNode;
}
void Grabber::scheduleWindowChange(QQuickWindow *window)
{
disconnect(m_updateConnection);
if (window) {
m_updateConnection = connect(window, &QQuickWindow::afterRendering,
this, &Grabber::scheduleUpdate);
}
}
void Grabber::scheduleUpdate()
{
if (!m_running)
update();
else
m_running = false;
}
Юзать как то так:
import QtQuick 2.9
import QtQuick.Window 2.2
import com.semsotec.horex 1.0
Window {
visible: true
width: 800
height: 600
title: qsTr("Hello World")
Item {
id: root
anchors.fill: parent
Rectangle {
width: 200
height: 200
color: "red"
Behavior on x {
NumberAnimation { from: 0; to: 800; duration: 2000; loops: Animation.Infinite }
}
Component.onCompleted: x = 100
Text {
id: name
text: qsTr("text")
}
}
Rectangle {
y: root.height / 1.5
width: 20
height: 20
color: "green"
Behavior on x {
NumberAnimation { from: 800; to: 0; duration: 500; loops: Animation.Infinite }
}
Component.onCompleted: x = 100
}
}
Grabber {
sourceItem: root
}
}
В данном случае у меня fbo.toImage() тратит ~8 мсек при размере окна 800х600, и ~30 мсек при 1920х1080 (подразумевается, что захватываемая Item имеет размер на все окно). Т.е. получаются результаты около 120 fps и 30 fps соответственно, быстрее не получается. Я на винде проверял пока что только, но нужно будет сделать это для андройда, и, думаю, там результаты будут похуже.
Все-равно получается как-то убого, и не понятно как прикрутить QQuickRenderControl чтобы граббить нужный айтем... Везде в примерах (а имеется только один пример в examples) все не то... блин...
Исправление kuzulis, :
аналог твоего кода для виджетов делается ещё быстрее и проще
Где там оно быстрее и проще?
QQuickRenderControl::grab() внутри дергает то же самое что и QOpenGLFramebufferObject::toImage().
Набросал тут код чтобы мониторить конец перерисовки окна и после этого граббить содержимое нужного айтема (со всеми его детьми):
#include "grabber.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QSGRootNode>
#include <QOpenGLFramebufferObject>
#include <private/qquickitem_p.h>
#include <private/qsgrenderer_p.h>
class BindableFbo final : public QSGBindable
{
public:
explicit BindableFbo(QOpenGLFramebufferObject *fbo)
: m_fbo(fbo)
{ }
void bind() const final { m_fbo->bind(); }
private:
QOpenGLFramebufferObject *m_fbo = nullptr;
};
Grabber::Grabber(QQuickItem *parent)
: QQuickItem(parent)
{
setFlag(QQuickItem::ItemHasContents);
connect(this, &Grabber::windowChanged,
this, &Grabber::scheduleWindowChange);
}
void Grabber::setSourceItem(QQuickItem *sourceItem)
{
if (sourceItem == m_sourceItem)
return;
m_sourceItem = sourceItem;
emit sourceItemChanged(m_sourceItem);
update();
}
QQuickItem *Grabber::sourceItem() const
{
return m_sourceItem;
}
QSGNode *Grabber::updatePaintNode(QSGNode *oldNode,
UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData);
if (!m_sourceItem)
return oldNode;
m_running = true;
const auto itemNode = QQuickItemPrivate::get(m_sourceItem)->itemNode();
const auto parentNode = itemNode->parent();
const auto siblingNode = itemNode->previousSibling();
if (parentNode)
parentNode->removeChildNode(itemNode);
const QScopedPointer<QSGRenderer> renderer(
QQuickItemPrivate::get(this)->
sceneGraphRenderContext()->createRenderer());
QSGRootNode rootNode;
rootNode.appendChildNode(itemNode);
renderer->setRootNode(&rootNode);
const QSize size(m_sourceItem->width(), m_sourceItem->height());
renderer->setDeviceRect(size);
renderer->setViewportRect(size);
renderer->setProjectionMatrixToRect(QRectF(QPointF(), size));
renderer->setClearColor(Qt::transparent);
QOpenGLFramebufferObject fbo(size);
renderer->renderScene(BindableFbo(&fbo));
fbo.release();
rootNode.removeChildNode(itemNode);
if (parentNode) {
if (siblingNode) {
parentNode->insertChildNodeAfter(itemNode, siblingNode);
} else {
parentNode->prependChildNode(itemNode);
}
}
QElapsedTimer et;
et.start();
const QImage image = fbo.toImage(); // TOO LONG!
static int count = 0;
qDebug() << "Elapsed:" << et.elapsed() << count;
++count;
//image.save(tr("test%1.png").arg(count++));
return oldNode;
}
void Grabber::scheduleWindowChange(QQuickWindow *window)
{
disconnect(m_updateConnection);
if (window) {
m_updateConnection = connect(window, &QQuickWindow::afterRendering,
this, &Grabber::scheduleUpdate);
}
}
void Grabber::scheduleUpdate()
{
if (!m_running)
update();
else
m_running = false;
}
Юзать как то так:
import QtQuick 2.9
import QtQuick.Window 2.2
import com.semsotec.horex 1.0
Window {
visible: true
width: 800
height: 600
title: qsTr("Hello World")
Item {
id: root
anchors.fill: parent
Rectangle {
width: 200
height: 200
color: "red"
Behavior on x {
NumberAnimation { from: 0; to: 800; duration: 2000; loops: Animation.Infinite }
}
Component.onCompleted: x = 100
Text {
id: name
text: qsTr("text")
}
}
Rectangle {
y: root.height / 1.5
width: 20
height: 20
color: "green"
Behavior on x {
NumberAnimation { from: 800; to: 0; duration: 500; loops: Animation.Infinite }
}
Component.onCompleted: x = 100
}
}
Grabber {
sourceItem: root
}
}
В данном случае у меня fbo.toImage() тратит ~8 мсек при размере окна 800х600, и ~30 мсек при 1920х1080 (подразумевается, что захватываемая Item имеет размер на все окно). Т.е. получаются результаты около 120 fps и 30 fps соответственно, быстрее не получается. Я на винде проверял пока что только, но нужно будет сделать это для андройда, и, думаю, там результаты будут похуже.
Все-равно получается как-то убого, и не понятно как прикрутить QQuickRenderControl чтобы граббить нужный айтем... Везде в примерах (а имеется только один пример в examples) все не то... блин...
Исходная версия kuzulis, :
аналог твоего кода для виджетов делается ещё быстрее и проще
Где там оно быстрее и проще?
QQuickRenderControl::grab() внутри дергает то же самое что и QOpenGLFramebufferObject::toImage().
Набросал тут код чтобы мониторить конец перерисовки окна и после этого граббить содержимое нужного айтема (со всеми его детьми):
#include "grabber.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QSGRootNode>
#include <QOpenGLFramebufferObject>
#include <private/qquickitem_p.h>
#include <private/qsgrenderer_p.h>
class BindableFbo final : public QSGBindable
{
public:
explicit BindableFbo(QOpenGLFramebufferObject *fbo)
: m_fbo(fbo)
{ }
void bind() const final { m_fbo->bind(); }
private:
QOpenGLFramebufferObject *m_fbo = nullptr;
};
Grabber::Grabber(QQuickItem *parent)
: QQuickItem(parent)
{
setFlag(QQuickItem::ItemHasContents);
connect(this, &Grabber::windowChanged,
this, &Grabber::scheduleWindowChange);
}
void Grabber::setSourceItem(QQuickItem *sourceItem)
{
if (sourceItem == m_sourceItem)
return;
m_sourceItem = sourceItem;
emit sourceItemChanged(m_sourceItem);
update();
}
QQuickItem *Grabber::sourceItem() const
{
return m_sourceItem;
}
QSGNode *Grabber::updatePaintNode(QSGNode *oldNode,
UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData);
if (!m_sourceItem)
return oldNode;
m_running = true;
const auto itemNode = QQuickItemPrivate::get(m_sourceItem)->itemNode();
const auto parentNode = itemNode->parent();
const auto siblingNode = itemNode->previousSibling();
if (parentNode)
parentNode->removeChildNode(itemNode);
const QScopedPointer<QSGRenderer> renderer(
QQuickItemPrivate::get(this)->
sceneGraphRenderContext()->createRenderer());
QSGRootNode rootNode;
rootNode.appendChildNode(itemNode);
renderer->setRootNode(&rootNode);
const QSize size(m_sourceItem->width(), m_sourceItem->height());
renderer->setDeviceRect(size);
renderer->setViewportRect(size);
renderer->setProjectionMatrixToRect(QRectF(QPointF(), size));
renderer->setClearColor(Qt::transparent);
QOpenGLFramebufferObject fbo(size);
renderer->renderScene(BindableFbo(&fbo));
fbo.release();
rootNode.removeChildNode(itemNode);
if (parentNode) {
if (siblingNode) {
parentNode->insertChildNodeAfter(itemNode, siblingNode);
} else {
parentNode->prependChildNode(itemNode);
}
}
QElapsedTimer et;
et.start();
const QImage image = fbo.toImage(); // TOO LONG!
static int count = 0;
qDebug() << "Elapsed:" << et.elapsed() << count;
++count;
//image.save(tr("test%1.png").arg(count++));
return oldNode;
}
void Grabber::scheduleWindowChange(QQuickWindow *window)
{
disconnect(m_updateConnection);
if (window) {
m_updateConnection = connect(window, &QQuickWindow::afterRendering,
this, &Grabber::scheduleUpdate);
}
}
void Grabber::scheduleUpdate()
{
if (!m_running)
update();
else
m_running = false;
}
В данном случае у меня fbo.toImage() тратит ~8 мсек при размере окна 800х600, и ~30 мсек при 1920х1080 (подразумевается, что захватываемая Item имеет размер на все окно). Т.е. получаются результаты около 120 fps и 30 fps соответственно, быстрее не получается. Я на винде проверял пока что только, но нужно будет сделать это для андройда, и, думаю, там результаты будут похуже.
Все-равно получается как-то убого, и не понятно как прикрутить QQuickRenderControl чтобы граббить нужный айтем... Везде в примерах (а имеется только один пример в examples) все не то... блин...