[Development] How fast grab a QML item's content to image when it updates?

Denis Shienkov denis.shienkov at gmail.com
Tue Dec 12 14:13:15 CET 2017


Hi all...

Is it possible to grab a QQuickItem content (e.g. with all sub-items)
when an item changes?

E.g. with widgets I use the following code:

bool MyWidget::event(QEvent *event)
{
     if (event->type() == QEvent::UpdateRequest)
         myGrab();
     return QWidget::event(event);
}

void MyWidget::myGrab()
{
     ...
     QBackingStore *store = backingStore();
     Q_ASSERT(store);

     QPaintDevice *pdev = store->paintDevice();
     const auto image = dynamic_cast<QImage *>(pdev);
     ...
}

it is very fast (as I know)...

But with the QML I got a troubles: I can 'grab' the sourceItem, using
the FBO and private functions, but the fbo::toImage() is too slow (~24 
msecs),
and I don't know how to intercept the signal when a watched item updates:

Grabber::Grabber(QQuickItem *parent)
     : QQuickItem(parent)
{
     setFlag(QQuickItem::ItemHasContents);
}

// Where sourceItem - is a watched item.
void Grabber::setSourceItem(QQuickItem *sourceItem)
{
     if (sourceItem == m_sourceItem)
         return;
     m_sourceItem = sourceItem;
     emit sourceItemChanged(m_sourceItem);
     update();
}

QSGNode *Grabber::updatePaintNode(QSGNode *oldNode,
                                   UpdatePaintNodeData *updatePaintNodeData)
{
     Q_UNUSED(updatePaintNodeData);

     if (!m_sourceItem)
         return oldNode;

     QSGRootNode root;
root.appendChildNode(QQuickItemPrivate::get(m_sourceItem)->itemNode());

     const QScopedPointer<QSGRenderer> renderer(
                 QQuickItemPrivate::get(this)->
                 sceneGraphRenderContext()->createRenderer());

     renderer->setRootNode(&root);

     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();

     QElapsedTimer et;
     et.start();
     const QImage image = fbo.toImage(); // TOO LONG ~24 msec!
     qDebug() << "Elapsed:" << et.elapsed();

     return oldNode;
}

it is very fast (as I know).

But with the QML I got a troubles: I can 'grub' an item, using the FBO,

but the toImage() method is too slow (~24 msecs), and I don't know how

to intercept a signal when the watched item updates:

Grabber::Grabber(QQuickItem *parent)
     : QQuickItem(parent)
{
     setFlag(QQuickItem::ItemHasContents);
}

QSGNode *Grabber::updatePaintNode(QSGNode *oldNode,
                                   UpdatePaintNodeData *updatePaintNodeData)
{
     Q_UNUSED(updatePaintNodeData);

     if (!m_sourceItem)
         return oldNode;

     QSGRootNode root;
root.appendChildNode(QQuickItemPrivate::get(m_sourceItem)->itemNode());

     const QScopedPointer<QSGRenderer> renderer(
                 QQuickItemPrivate::get(this)->
                 sceneGraphRenderContext()->createRenderer());

     renderer->setRootNode(&root);

     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();

     QElapsedTimer et;
     et.start();
     const QImage image = fbo.toImage(); // TOO LONG!
     qDebug() << "Elapsed:" << et.elapsed();

     return oldNode;
}








More information about the Development mailing list