/**************************************************************************** ** ** Copyright (C) 2016 Jolla Ltd, author: ** Contact: https://www.qt.io/licensing/ ** ** 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 The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qquickitemgrabresult.h" #include "qquickrendercontrol.h" #include "qquickwindow.h" #include "qquickitem.h" #if QT_CONFIG(quick_shadereffect) #include "qquickshadereffectsource_p.h" #endif #include #include #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(nullptr) , qmlEngine(nullptr) , texture(nullptr) { } ~QQuickItemGrabResultPrivate() { delete cacheEntry; } void ensureImageInCache() const { if (url.isEmpty() && !image.isNull()) { url.setScheme(QQuickPixmap::itemGrabberScheme); 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; QSGLayer *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, * a null image is returned (\c {image.isNull()} will return \c true). */ /*! \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. * * \note In Qt versions prior to 5.9, this function is marked as non-\c{const}. */ bool QQuickItemGrabResult::saveToFile(const QString &fileName) const { Q_D(const QQuickItemGrabResult); return d->image.save(fileName); } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #if QT_DEPRECATED_SINCE(5, 15) /*! * \overload * \internal */ bool QQuickItemGrabResult::saveToFile(const QString &fileName) { return qAsConst(*this).saveToFile(fileName); } #endif #endif // < Qt 6 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)); deleteLater(); } 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; } QSGRenderContext *rc = QQuickWindowPrivate::get(d->window.data())->context; d->texture = rc->sceneGraphContext()->createLayer(rc); 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())); const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->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 = nullptr; 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) { qmlWarning(item) << "grabToImage: item has invalid dimensions"; return nullptr; } if (!item->window()) { qmlWarning(item) << "grabToImage: item is not attached to a window"; return nullptr; } QWindow *effectiveWindow = item->window(); if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(item->window())) effectiveWindow = renderWindow; if (!effectiveWindow->isVisible()) { qmlWarning(item) << "grabToImage: item's window is not visible"; return nullptr; } 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 \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. The callback takes one argument, which * is the result of the grab operation; an \l ItemGrabResult object. * * 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) { qmlWarning(this) << "grabToImage: item has no QML engine"; return false; } if (!callback.isCallable()) { qmlWarning(this) << "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) { qmlWarning(this) << "grabToImage: item has invalid dimensions"; return false; } if (!window()) { qmlWarning(this) << "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 #include "moc_qquickitemgrabresult.cpp"