aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitemgrabresult.cpp
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@jollamobile.com>2014-04-28 13:30:04 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-05-01 22:13:11 +0200
commit0cb12e9e01b8309320706fab219945f0ff159413 (patch)
tree15e7e7986b8abf7b1cc81406dd8edc68ef842dae /src/quick/items/qquickitemgrabresult.cpp
parentef5a68dfa19bbdb588e9061ed788b805fc3155e1 (diff)
Say hello to QQuickItem::grabToImage().
[ChangeLog][QtQuick][Item] Added functions QQuickItem::grabToImage() and Item::grabToImage() to allow grabbing of items into system-memory images. Change-Id: I76cd73bb62f7440569c6fce63d63528559845721 Reviewed-by: Michael Brasser <michael.brasser@live.com>
Diffstat (limited to 'src/quick/items/qquickitemgrabresult.cpp')
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
new file mode 100644
index 0000000000..93b9261c44
--- /dev/null
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -0,0 +1,399 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemgrabresult.h"
+
+#include "qquickwindow.h"
+#include "qquickitem.h"
+#include "qquickshadereffectsource_p.h"
+
+#include <QtQml/QQmlEngine>
+
+#include <private/qquickpixmapcache_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const QEvent::Type Event_Grab_Completed = static_cast<QEvent::Type>(QEvent::User + 1);
+
+class QQuickItemGrabResultPrivate : public QObjectPrivate
+{
+public:
+ QQuickItemGrabResultPrivate()
+ : cacheEntry(0)
+ , texture(0)
+ {
+ }
+
+ ~QQuickItemGrabResultPrivate()
+ {
+ delete cacheEntry;
+ }
+
+ void ensureImageInCache() const {
+ if (url.isEmpty() && !image.isNull()) {
+ url.setScheme(QStringLiteral("ItemGrabber"));
+ url.setPath(QVariant::fromValue(item.data()).toString());
+ static uint counter = 0;
+ url.setFragment(QString::number(++counter));
+ cacheEntry = new QQuickPixmap(url, image);
+ }
+ }
+
+ static QQuickItemGrabResult *create(QQuickItem *item, const QSize &size);
+
+ QImage image;
+
+ mutable QUrl url;
+ mutable QQuickPixmap *cacheEntry;
+
+ QQmlEngine *qmlEngine;
+ QJSValue callback;
+
+ QPointer<QQuickItem> item;
+ QPointer<QQuickWindow> window;
+ QQuickShaderEffectTexture *texture;
+ QSizeF itemSize;
+ QSize textureSize;
+};
+
+/*!
+ * \qmlproperty url QtQuick::ItemGrabResult::url
+ *
+ * This property holds a URL which can be used in conjunction with
+ * URL based image consumers, such as the QtQuick::Image type.
+ *
+ * The URL is valid while there is a reference in QML or JavaScript
+ * to the ItemGrabResult or while the image the URL references is
+ * actively used.
+ *
+ * The URL does not represent a valid file or location to read it from, it
+ * is primarily a key to access images through Qt Quick's image-based types.
+ */
+
+/*!
+ * \property QQuickItemGrabResult::url
+ *
+ * This property holds a URL which can be used in conjunction with
+ * URL based image consumers, such as the QtQuick::Image type.
+ *
+ * The URL is valid until the QQuickItemGrabResult object is deleted.
+ *
+ * The URL does not represent a valid file or location to read it from, it
+ * is primarily a key to access images through Qt Quick's image-based types.
+ */
+
+/*!
+ * \qmlproperty variant QtQuick::ItemGrabResult::image
+ *
+ * This property holds the pixel results from a grab in the
+ * form of a QImage.
+ */
+
+/*!
+ * \property QQuickItemGrabResult::image
+ *
+ * This property holds the pixel results from a grab.
+ *
+ * If the grab is not yet complete or if it failed,
+ * an empty image is returned.
+ */
+
+/*!
+ \class QQuickItemGrabResult
+ \inmodule QtQuick
+ \brief The QQuickItemGrabResult contains the result from QQuickItem::grabToImage().
+
+ \sa QQuickItem::grabToImage()
+ */
+
+/*!
+ * \fn void QQuickItemGrabResult::ready()
+ *
+ * This signal is emitted when the grab has completed.
+ */
+
+/*!
+ * \qmltype ItemGrabResult
+ * \instantiates QQuickItemGrabResult
+ * \inherits QtObject
+ * \inqmlmodule QtQuick
+ * \ingroup qtquick-visual
+ * \brief Contains the results from a call to Item::grabToImage().
+ *
+ * The ItemGrabResult is a small container used to encapsulate
+ * the results from Item::grabToImage().
+ *
+ * \sa Item::grabToImage()
+ */
+
+QQuickItemGrabResult::QQuickItemGrabResult(QObject *parent)
+ : QObject(*new QQuickItemGrabResultPrivate, parent)
+{
+}
+
+/*!
+ * \qmlmethod bool QtQuick::ItemGrabResult::saveToFile(fileName)
+ *
+ * Saves the grab result as an image to \a fileName. Returns true
+ * if successful; otherwise returns false.
+ */
+
+/*!
+ * Saves the grab result as an image to \a fileName. Returns true
+ * if successful; otherwise returns false.
+ */
+bool QQuickItemGrabResult::saveToFile(const QString &fileName)
+{
+ Q_D(QQuickItemGrabResult);
+ return d->image.save(fileName);
+}
+
+QUrl QQuickItemGrabResult::url() const
+{
+ Q_D(const QQuickItemGrabResult);
+ d->ensureImageInCache();
+ return d->url;
+}
+
+QImage QQuickItemGrabResult::image() const
+{
+ Q_D(const QQuickItemGrabResult);
+ return d->image;
+}
+
+/*!
+ * \internal
+ */
+bool QQuickItemGrabResult::event(QEvent *e)
+{
+ Q_D(QQuickItemGrabResult);
+ if (e->type() == Event_Grab_Completed) {
+ // JS callback
+ if (d->qmlEngine && d->callback.isCallable())
+ d->callback.call(QJSValueList() << d->qmlEngine->newQObject(this));
+ else
+ Q_EMIT ready();
+ return true;
+ }
+ return QObject::event(e);
+}
+
+void QQuickItemGrabResult::setup()
+{
+ Q_D(QQuickItemGrabResult);
+ if (!d->item) {
+ disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup);
+ disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render);
+ QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed));
+ return;
+ }
+
+ d->texture = new QQuickShaderEffectTexture(d->item);
+ d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode());
+ d->itemSize = QSizeF(d->item->width(), d->item->height());
+}
+
+void QQuickItemGrabResult::render()
+{
+ Q_D(QQuickItemGrabResult);
+ if (!d->texture)
+ return;
+
+ d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
+ QSGContext *sg = QSGRenderContext::from(QOpenGLContext::currentContext())->sceneGraphContext();
+ const QSize minSize = sg->minimumFBOSize();
+ d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()),
+ qMax(minSize.height(), d->textureSize.height())));
+ d->texture->scheduleUpdate();
+ d->texture->updateTexture();
+ d->image = d->texture->toImage();
+
+ delete d->texture;
+ d->texture = 0;
+
+ disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup);
+ disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render);
+ QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed));
+}
+
+QQuickItemGrabResult *QQuickItemGrabResultPrivate::create(QQuickItem *item, const QSize &targetSize)
+{
+ QSize size = targetSize;
+ if (size.isEmpty())
+ size = QSize(item->width(), item->height());
+
+ if (size.width() < 1 || size.height() < 1) {
+ qWarning("Item::grabToImage: item has invalid dimensions");
+ return 0;
+ }
+
+ if (!item->window()) {
+ qWarning("Item::grabToImage: item is not attached to a window");
+ return 0;
+ }
+
+ if (!item->window()->isVisible()) {
+ qWarning("Item::grabToImage: item's window is not visible");
+ return 0;
+ }
+
+ QQuickItemGrabResult *result = new QQuickItemGrabResult();
+ QQuickItemGrabResultPrivate *d = result->d_func();
+ d->item = item;
+ d->window = item->window();
+ d->textureSize = size;
+
+ QQuickItemPrivate::get(item)->refFromEffectItem(false);
+
+ // trigger sync & render
+ item->window()->update();
+
+ return result;
+}
+
+/*!
+ * Grabs the item into an in-memory image.
+ *
+ * The grab happens asynchronously and the signal QQuickItemGrabResult::ready()
+ * is emitted when the grab has been completed.
+ *
+ * Use \a targetSize to specify the size of the target image. By default, the
+ * result will have the same size as item.
+ *
+ * If the grab could not be initiated, the function returns a \c null.
+ *
+ * \note This function will render the item to an offscreen surface and
+ * copy that surface from the GPU's memory into the CPU's memory, which can
+ * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers}
+ * or ShaderEffectSource.
+ *
+ * \sa QQuickWindow::grabWindow()
+ */
+QSharedPointer<QQuickItemGrabResult> QQuickItem::grabToImage(const QSize &targetSize)
+{
+ QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, targetSize);
+ if (!result)
+ return QSharedPointer<QQuickItemGrabResult>();
+
+ connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection);
+ connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection);
+
+ return QSharedPointer<QQuickItemGrabResult>(result);
+}
+
+/*!
+ * \qmlmethod bool QtQuick::Item::grabToImage(callback, targetSize)
+ *
+ * Grabs the item into an in-memory image.
+ *
+ * The grab happens asynchronously and the JavaScript function \a callback is
+ * invoked when the grab is completed.
+ *
+ * Use \a targetSize to specify the size of the target image. By default, the result
+ * will have the same size as the item.
+ *
+ * If the grab could not be initiated, the function returns \c false.
+ *
+ * The following snippet shows how to grab an item and store the results to
+ * a file.
+ *
+ * \snippet qml/itemGrab.qml grab-source
+ * \snippet qml/itemGrab.qml grab-to-file
+ *
+ * The following snippet shows how to grab an item and use the results in
+ * another image element.
+ *
+ * \snippet qml/itemGrab.qml grab-image-target
+ * \snippet qml/itemGrab.qml grab-to-cache
+ *
+ * \note This function will render the item to an offscreen surface and
+ * copy that surface from the GPU's memory into the CPU's memory, which can
+ * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers}
+ * or ShaderEffectSource.
+ */
+
+/*!
+ * \internal
+ * Only visible from QML.
+ */
+bool QQuickItem::grabToImage(const QJSValue &callback, const QSize &targetSize)
+{
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning("Item::grabToImage: no QML Engine");
+ return false;
+ }
+
+ if (!callback.isCallable()) {
+ qWarning("Item::grabToImage: 'callback' is not a function");
+ return false;
+ }
+
+ QSize size = targetSize;
+ if (size.isEmpty())
+ size = QSize(width(), height());
+
+ if (size.width() < 1 || size.height() < 1) {
+ qWarning("Item::grabToImage: item has invalid dimensions");
+ return false;
+ }
+
+ if (!window()) {
+ qWarning("Item::grabToImage: item is not attached to a window");
+ return false;
+ }
+
+ QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, size);
+ if (!result)
+ return false;
+
+ connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection);
+ connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection);
+
+ QQuickItemGrabResultPrivate *d = result->d_func();
+ d->qmlEngine = engine;
+ d->callback = callback;
+ return true;
+}
+
+QT_END_NAMESPACE