Say hello to QQuickItem::grabToImage().
authorGunnar Sletta <gunnar.sletta@jollamobile.com>
Mon, 28 Apr 2014 11:30:04 +0000 (13:30 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 1 May 2014 20:13:11 +0000 (22:13 +0200)
[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>
src/quick/doc/snippets/qml/itemGrab.qml [new file with mode: 0644]
src/quick/items/items.pri
src/quick/items/qquickitem.h
src/quick/items/qquickitemgrabresult.cpp [new file with mode: 0644]
src/quick/items/qquickitemgrabresult.h [new file with mode: 0644]
src/quick/items/qquickitemsmodule.cpp
src/quick/util/qquickpixmapcache.cpp
src/quick/util/qquickpixmapcache_p.h
tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml [new file with mode: 0644]
tests/auto/quick/qquickitem2/data/grabToImage.qml [new file with mode: 0644]
tests/auto/quick/qquickitem2/tst_qquickitem.cpp

diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/itemGrab.qml
new file mode 100644 (file)
index 0000000..4ceaea6
--- /dev/null
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.4
+
+Item {
+    width: 320
+    height: 480
+
+//! [grab-source]
+Rectangle {
+    id: source
+    width: 100
+    height: 100
+    gradient: Gradient {
+        GradientStop { position: 0; color: "steelblue" }
+        GradientStop { position: 1; color: "black" }
+    }
+}
+//! [grab-source]
+
+//! [grab-image-target]
+Image {
+    id: image
+}
+//! [grab-image-target]
+    Timer {
+        repeat: false
+        running: true
+        interval: 1000
+        onTriggered: {
+//! [grab-to-file]
+
+    // ...
+    source.grabToImage(function(result) {
+                           result.save("something.png");
+                       });
+//! [grab-to-file]
+
+//! [grab-to-cache]
+
+    // ...
+    source.grabToImage(function(result) {
+                           image.source = result.url;
+                       },
+                       Qt.size(50, 50));
+//! [grab-to-cache]
+        }
+    }
+}
index cb378b4..add909d 100644 (file)
@@ -75,7 +75,8 @@ HEADERS += \
     $$PWD/qquickitemviewtransition_p.h \
     $$PWD/qquickscreen_p.h \
     $$PWD/qquickwindowmodule_p.h \
-    $$PWD/qquickframebufferobject.h
+    $$PWD/qquickframebufferobject.h \
+    $$PWD/qquickitemgrabresult.h
 
 SOURCES += \
     $$PWD/qquickevents.cpp \
@@ -128,7 +129,8 @@ SOURCES += \
     $$PWD/qquickitemviewtransition.cpp \
     $$PWD/qquickwindowmodule.cpp \
     $$PWD/qquickscreen.cpp \
-    $$PWD/qquickframebufferobject.cpp
+    $$PWD/qquickframebufferobject.cpp \
+    $$PWD/qquickitemgrabresult.cpp
 
 SOURCES += \
     $$PWD/qquickshadereffect.cpp \
index 3858499..b5bf899 100644 (file)
@@ -43,6 +43,7 @@
 #define QQUICKITEM_H
 
 #include <QtQuick/qtquickglobal.h>
+#include <QtQuick/qquickitemgrabresult.h>
 #include <QtQml/qqml.h>
 #include <QtQml/qqmlcomponent.h>
 
@@ -52,6 +53,7 @@
 #include <QtGui/qfont.h>
 #include <QtGui/qaccessible.h>
 
+
 QT_BEGIN_NAMESPACE
 
 class QQuickItem;
@@ -92,6 +94,7 @@ class QTouchEvent;
 class QSGNode;
 class QSGTransformNode;
 class QSGTextureProvider;
+class QQuickItemGrabResult;
 
 class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
 {
@@ -310,6 +313,10 @@ public:
     bool keepTouchGrab() const;
     void setKeepTouchGrab(bool);
 
+    // implemented in qquickitemgrabresult.cpp
+    Q_REVISION(4) Q_INVOKABLE bool grabToImage(const QJSValue &callback, const QSize &targetSize = QSize());
+    QSharedPointer<QQuickItemGrabResult> grabToImage(const QSize &targetSize = QSize());
+
     Q_INVOKABLE virtual bool contains(const QPointF &point) const;
 
     QTransform itemTransform(QQuickItem *, bool *) const;
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
new file mode 100644 (file)
index 0000000..93b9261
--- /dev/null
@@ -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
diff --git a/src/quick/items/qquickitemgrabresult.h b/src/quick/items/qquickitemgrabresult.h
new file mode 100644 (file)
index 0000000..5297002
--- /dev/null
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QQUICKITEMGRABRESULT_H
+#define QQUICKITEMGRABRESULT_H
+
+#include <QtCore/QObject>
+#include <QtCore/QSize>
+#include <QtCore/QUrl>
+#include <QtGui/QImage>
+#include <QtQml/QJSValue>
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImage;
+
+class QQuickItemGrabResultPrivate;
+
+class Q_QUICK_EXPORT QQuickItemGrabResult : public QObject
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QQuickItemGrabResult)
+
+    Q_PROPERTY(QImage image READ image CONSTANT)
+    Q_PROPERTY(QUrl url READ url CONSTANT)
+public:
+    QImage image() const;
+    QUrl url() const;
+
+    Q_INVOKABLE bool saveToFile(const QString &fileName);
+
+protected:
+    bool event(QEvent *);
+
+Q_SIGNALS:
+    void ready();
+
+private Q_SLOTS:
+    void setup();
+    void render();
+
+private:
+    friend class QQuickItem;
+
+    QQuickItemGrabResult(QObject *parent = 0);
+};
+
+QT_END_NAMESPACE
+
+#endif
index 7d75c76..dc8230f 100644 (file)
@@ -193,6 +193,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
     qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
     qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
 
+    qmlRegisterType<QQuickItemGrabResult>();
     qmlRegisterType<QQuickItemLayer>();
     qmlRegisterType<QQuickAnchors>();
     qmlRegisterType<QQuickKeyEvent>();
index 747c07b..b90ca47 100644 (file)
@@ -1084,6 +1084,12 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QSize &siz
     load(engine, url, size);
 }
 
+QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image)
+{
+    d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QSize());
+    d->addToCache();
+}
+
 QQuickPixmap::~QQuickPixmap()
 {
     if (d) {
index aa1761e..6ab1ff6 100644 (file)
@@ -78,6 +78,7 @@ public:
     QQuickPixmap();
     QQuickPixmap(QQmlEngine *, const QUrl &);
     QQuickPixmap(QQmlEngine *, const QUrl &, const QSize &);
+    QQuickPixmap(const QUrl &, const QImage &image);
     ~QQuickPixmap();
 
     enum Status { Null, Ready, Error, Loading };
diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml
new file mode 100644 (file)
index 0000000..4f827bb
--- /dev/null
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.4
+import QtTest 1.0
+
+Item {
+    id: root;
+    width: 400
+    height: 400
+
+    TestCase {
+        id: testCase
+        name: "item-grabber"
+        when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready
+        function test_endresult() {
+            var image = grabImage(root);
+
+            // imageOnDisk at (0, 0) - (100x100)
+            compare(imageOnDisk.width, 100);
+            compare(imageOnDisk.height, 100);
+            verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878)
+            verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1));
+
+            // imageOnDiskSmall at (100, 0) - 50x50
+            compare(imageOnDiskSmall.width, 50);
+            compare(imageOnDiskSmall.height, 50);
+            verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1));
+            verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1));
+
+            // imageInCache at (0, 100) - 100x100
+            compare(imageInCache.width, 100);
+            compare(imageInCache.height, 100);
+            verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1));
+            verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1));
+
+            // imageInCacheSmall at (100, 100) - 50x50
+            compare(imageInCacheSmall.width, 50);
+            compare(imageInCacheSmall.height, 50);
+            verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1));
+            verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1));
+
+            // After all that has been going on, it should only have been called that one time..
+            compare(imageOnDisk.callCount, 1);
+        }
+
+        onWindowShownChanged: {
+            box.grabToImage(imageOnDisk.handleGrab);
+            box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50));
+            box.grabToImage(imageInCache.handleGrab);
+            box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50));
+        }
+
+    }
+
+    Rectangle {
+        id: box
+        width: 100
+        height: 100
+        color: "red";
+
+        visible: false
+
+        Rectangle {
+            anchors.bottom: parent.bottom;
+            anchors.right: parent.right;
+            width: 10
+            height: 10
+            color: "blue";
+        }
+    }
+
+    Image {
+        id: imageOnDisk
+        x: 0
+        y: 0
+        property int callCount: 0;
+        property bool ready: false;
+        function handleGrab(result) {
+            if (!result.saveToFile("image.png"))
+                print("Error: Failed to save image to disk...");
+            source = "image.png";
+            ready = true;
+            ++callCount;
+        }
+    }
+
+    Image {
+        id: imageOnDiskSmall
+        x: 100
+        y: 0
+        property bool ready: false;
+        function handleGrab(result) {
+            if (!result.saveToFile("image_small.png"))
+                print("Error: Failed to save image to disk...");
+            source = "image_small.png";
+            ready = true;
+        }
+    }
+
+    Image {
+        id: imageInCache
+        x: 0
+        y: 100
+        property bool ready: false;
+        function handleGrab(result) {
+            source = result.url;
+            ready = true;
+        }
+    }
+
+    Image {
+        id: imageInCacheSmall
+        x: 100
+        y: 100
+        property bool ready: false;
+        function handleGrab(result) {
+            source = result.url;
+            ready = true;
+        }
+    }
+}
diff --git a/tests/auto/quick/qquickitem2/data/grabToImage.qml b/tests/auto/quick/qquickitem2/data/grabToImage.qml
new file mode 100644 (file)
index 0000000..9f25210
--- /dev/null
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+    width: 320
+    height: 480
+    Rectangle {
+        objectName: "myItem";
+        width: 100
+        height: 100
+        color: "red"
+        Rectangle {
+            anchors.right: parent.right
+            anchors.bottom: parent.bottom
+            width: 10
+            height: 10
+            color: "blue"
+        }
+    }
+}
index 3ce1848..2de7fa1 100644 (file)
@@ -117,6 +117,8 @@ private slots:
     void contains();
     void childAt();
 
+    void grab();
+
 private:
     QQmlEngine engine;
     bool qt_tab_all_widgets() {
@@ -2528,6 +2530,41 @@ void tst_QQuickItem::childAt()
     QCOMPARE(parent.childAt(300, 300), static_cast<QQuickItem *>(0));
 }
 
+void tst_QQuickItem::grab()
+{
+    QQuickView view;
+    view.setSource(testFileUrl("grabToImage.qml"));
+    view.show();
+    QTest::qWaitForWindowExposed(&view);
+
+    QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject());
+    QVERIFY(root);
+    QQuickItem *item = root->findChild<QQuickItem *>("myItem");
+    QVERIFY(item);
+
+    { // Default size (item is 100x100)
+        QSharedPointer<QQuickItemGrabResult> result = item->grabToImage();
+        QSignalSpy spy(result.data(), SIGNAL(ready()));
+        QTRY_VERIFY(spy.size() > 0);
+        QVERIFY(!result->url().isEmpty());
+        QImage image = result->image();
+        QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0));
+        QCOMPARE(image.pixel(99, 99), qRgb(0, 0, 255));
+    }
+
+    { // Smaller size
+        QSharedPointer<QQuickItemGrabResult> result = item->grabToImage(QSize(50, 50));
+        QVERIFY(!result.isNull());
+        QSignalSpy spy(result.data(), SIGNAL(ready()));
+        QTRY_VERIFY(spy.size() > 0);
+        QVERIFY(!result->url().isEmpty());
+        QImage image = result->image();
+        QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0));
+        QCOMPARE(image.pixel(49, 49), qRgb(0, 0, 255));
+    }
+
+}
+
 
 QTEST_MAIN(tst_QQuickItem)