diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/itemGrab.qml new file mode 100644 index 0000000000..4ceaea6133 --- /dev/null +++ b/src/quick/doc/snippets/qml/itemGrab.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** 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] + } + } +} diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index cb378b424b..add909d0cb 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -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 \ diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 3858499c9e..b5bf8999a5 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -43,6 +43,7 @@ #define QQUICKITEM_H #include +#include #include #include @@ -52,6 +53,7 @@ #include #include + 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 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus 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 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 index 0000000000..93b9261c44 --- /dev/null +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** 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 + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +const QEvent::Type Event_Grab_Completed = static_cast(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 item; + QPointer 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 QQuickItem::grabToImage(const QSize &targetSize) +{ + QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, targetSize); + if (!result) + return QSharedPointer(); + + connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection); + connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection); + + return QSharedPointer(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 index 0000000000..5297002bb8 --- /dev/null +++ b/src/quick/items/qquickitemgrabresult.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** 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 +#include +#include +#include +#include +#include + +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 diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 7d75c7693d..dc8230f6be 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -193,6 +193,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri,2,4,"TextInput"); qmlRegisterType(uri,major,minor,"ViewSection"); + qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 747c07b942..b90ca47fd1 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -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) { diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index aa1761e896..6ab1ff6482 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -78,6 +78,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPixmap 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 index 0000000000..4f827bbf33 --- /dev/null +++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** 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 index 0000000000..9f25210ee2 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/grabToImage.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** 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" + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 3ce18488df..2de7fa1a83 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -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(0)); } +void tst_QQuickItem::grab() +{ + QQuickView view; + view.setSource(testFileUrl("grabToImage.qml")); + view.show(); + QTest::qWaitForWindowExposed(&view); + + QQuickItem *root = qobject_cast(view.rootObject()); + QVERIFY(root); + QQuickItem *item = root->findChild("myItem"); + QVERIFY(item); + + { // Default size (item is 100x100) + QSharedPointer 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 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)