LINUX.ORG.RU

История изменений

Исправление 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) все не то... блин...