Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickitemgrabresult.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <private/qtquickglobal_p.h>
7
9#include "qquickwindow.h"
10#include "qquickitem.h"
11#if QT_CONFIG(quick_shadereffect)
12#include "qquickshadereffectsource_p.h"
13#endif
14
15#include <QtQml/QQmlEngine>
16#include <QtQml/QQmlInfo>
17
18#include <private/qquickpixmap_p.h>
19#include <private/qquickitem_p.h>
20#include <private/qsgcontext_p.h>
21#include <private/qsgadaptationlayer_p.h>
22
23#include <QtCore/qpointer.h>
24
26
27const QEvent::Type Event_Grab_Completed = static_cast<QEvent::Type>(QEvent::User + 1);
28
30{
31public:
33 : cacheEntry(nullptr)
34 , qmlEngine(nullptr)
35 , texture(nullptr)
36 , devicePixelRatio(1.0)
37 {
38 }
39
41 {
42 delete cacheEntry;
43 }
44
45 void ensureImageInCache() const {
46 if (url.isEmpty() && !image.isNull()) {
47 url.setScheme(QQuickPixmap::itemGrabberScheme);
48 url.setPath(QVariant::fromValue(item.data()).toString());
49 static uint counter = 0;
50 url.setFragment(QString::number(++counter));
51 cacheEntry = new QQuickPixmap(url, image);
52 }
53 }
54
55 static QQuickItemGrabResult *create(QQuickItem *item, const QSize &size);
56
58
59 mutable QUrl url;
61
64
71};
72
73/*!
74 * \qmlproperty url QtQuick::ItemGrabResult::url
75 *
76 * This property holds a URL which can be used in conjunction with
77 * URL based image consumers, such as the QtQuick::Image type.
78 *
79 * The URL is valid while there is a reference in QML or JavaScript
80 * to the ItemGrabResult or while the image the URL references is
81 * actively used.
82 *
83 * The URL does not represent a valid file or location to read it from, it
84 * is primarily a key to access images through Qt Quick's image-based types.
85 */
86
87/*!
88 * \property QQuickItemGrabResult::url
89 *
90 * This property holds a URL which can be used in conjunction with
91 * URL based image consumers, such as the QtQuick::Image type.
92 *
93 * The URL is valid until the QQuickItemGrabResult object is deleted.
94 *
95 * The URL does not represent a valid file or location to read it from, it
96 * is primarily a key to access images through Qt Quick's image-based types.
97 */
98
99/*!
100 * \qmlproperty variant QtQuick::ItemGrabResult::image
101 *
102 * This property holds the pixel results from a grab in the
103 * form of a QImage.
104 */
105
106/*!
107 * \property QQuickItemGrabResult::image
108 *
109 * This property holds the pixel results from a grab.
110 *
111 * If the grab is not yet complete or if it failed,
112 * a null image is returned (\c {image.isNull()} will return \c true).
113 */
114
115/*!
116 \class QQuickItemGrabResult
117 \inmodule QtQuick
118 \brief The QQuickItemGrabResult contains the result from QQuickItem::grabToImage().
119
120 \sa QQuickItem::grabToImage()
121 */
122
123/*!
124 * \fn void QQuickItemGrabResult::ready()
125 *
126 * This signal is emitted when the grab has completed.
127 */
128
129/*!
130 * \qmltype ItemGrabResult
131 * \nativetype QQuickItemGrabResult
132 * \inherits QtObject
133 * \inqmlmodule QtQuick
134 * \ingroup qtquick-visual
135 * \brief Contains the results from a call to Item::grabToImage().
136 *
137 * The ItemGrabResult is a small container used to encapsulate
138 * the results from Item::grabToImage().
139 *
140 * \sa Item::grabToImage()
141 */
142
143QQuickItemGrabResult::QQuickItemGrabResult(QObject *parent)
144 : QObject(*new QQuickItemGrabResultPrivate, parent)
145{
146}
147
148/*!
149 * \qmlmethod bool QtQuick::ItemGrabResult::saveToFile(fileName)
150 *
151 * Saves the grab result as an image to \a fileName. Returns \c true
152 * if successful; otherwise returns \c false.
153 */
154
155// ### Qt 7: remove and keep only QUrl overload
156/*!
157 * Saves the grab result as an image to \a fileName. Returns \c true
158 * if successful; otherwise returns \c false.
159 *
160 * \note In Qt versions prior to 5.9, this function is marked as non-\c{const}.
161 */
162bool QQuickItemGrabResult::saveToFile(const QString &fileName) const
163{
164 Q_D(const QQuickItemGrabResult);
165 if (fileName.startsWith(QLatin1String("file:/")))
166 return saveToFile(QUrl(fileName));
167 return d->image.save(fileName);
168}
169
170/*!
171 * \since 6.2
172 * Saves the grab result as an image to \a filePath, which must refer to a
173 * \l{QUrl::isLocalFile}{local file name} with a
174 * \l{QImageWriter::supportedImageFormats()}{supported image format} extension.
175 * Returns \c true if successful; otherwise returns \c false.
176 */
177bool QQuickItemGrabResult::saveToFile(const QUrl &filePath) const
178{
179 Q_D(const QQuickItemGrabResult);
180 if (!filePath.isLocalFile()) {
181 qWarning() << "saveToFile can only save to a file on the local filesystem";
182 return false;
183 }
184 return d->image.save(filePath.toLocalFile());
185}
186
187#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
188#if QT_DEPRECATED_SINCE(5, 15)
189/*!
190 * \overload
191 * \internal
192 */
193bool QQuickItemGrabResult::saveToFile(const QString &fileName)
194{
195 return std::as_const(*this).saveToFile(fileName);
196}
197#endif
198#endif // < Qt 6
199
200QUrl QQuickItemGrabResult::url() const
201{
202 Q_D(const QQuickItemGrabResult);
203 d->ensureImageInCache();
204 return d->url;
205}
206
207QImage QQuickItemGrabResult::image() const
208{
209 Q_D(const QQuickItemGrabResult);
210 return d->image;
211}
212
213/*!
214 * \internal
215 */
216bool QQuickItemGrabResult::event(QEvent *e)
217{
218 Q_D(QQuickItemGrabResult);
219 if (e->type() == Event_Grab_Completed) {
220 // JS callback
221 if (d->qmlEngine && d->callback.isCallable()) {
222 d->callback.call(QJSValueList() << d->qmlEngine->newQObject(this));
223 QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
224 } else {
225 Q_EMIT ready();
226 }
227 return true;
228 }
229 return QObject::event(e);
230}
231
232void QQuickItemGrabResult::setup()
233{
234 Q_D(QQuickItemGrabResult);
235 if (!d->item) {
236 disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup);
237 disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render);
238 QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed));
239 return;
240 }
241
242 QSGRenderContext *rc = QQuickWindowPrivate::get(d->window.data())->context;
243 d->devicePixelRatio = d->window->effectiveDevicePixelRatio();
244 d->texture = rc->sceneGraphContext()->createLayer(rc);
245 d->texture->setDevicePixelRatio(d->devicePixelRatio);
246 d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode());
247 d->itemSize = QSizeF(d->item->width(), d->item->height());
248}
249
250void QQuickItemGrabResult::render()
251{
252 Q_D(QQuickItemGrabResult);
253 if (!d->texture)
254 return;
255
256 d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
257 const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->minimumFBOSize();
258 const QSize effectiveTextureSize = d->textureSize * d->devicePixelRatio;
259 d->texture->setSize(QSize(qMax(minSize.width(), effectiveTextureSize.width()),
260 qMax(minSize.height(), effectiveTextureSize.height())));
261 d->texture->scheduleUpdate();
262 d->texture->updateTexture();
263 d->image = d->texture->toImage();
264 d->image.setDevicePixelRatio(d->devicePixelRatio);
265
266 delete d->texture;
267 d->texture = nullptr;
268
269 disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup);
270 disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render);
271 QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed));
272}
273
274QQuickItemGrabResult *QQuickItemGrabResultPrivate::create(QQuickItem *item, const QSize &targetSize)
275{
276 QSize size = targetSize;
277 if (size.isEmpty())
278 size = QSize(item->width(), item->height());
279
280 if (size.width() < 1 || size.height() < 1) {
281 qmlWarning(item) << "grabToImage: item has invalid dimensions";
282 return nullptr;
283 }
284
285 if (!item->window()) {
286 qmlWarning(item) << "grabToImage: item is not attached to a window";
287 return nullptr;
288 }
289
290 QWindow *effectiveWindow = item->window();
291 if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(item->window()))
292 effectiveWindow = renderWindow;
293
294 if (!effectiveWindow->isVisible()) {
295 qmlWarning(item) << "grabToImage: item's window is not visible";
296 return nullptr;
297 }
298
299 QQuickItemGrabResult *result = new QQuickItemGrabResult();
300 QQuickItemGrabResultPrivate *d = result->d_func();
301 d->item = item;
302 d->window = item->window();
303 d->textureSize = size;
304
305 QQuickItemPrivate::get(item)->refFromEffectItem(false);
306
307 // trigger sync & render
308 item->window()->update();
309
310 return result;
311}
312
313/*!
314 * Grabs the item into an in-memory image.
315 *
316 * The grab happens asynchronously and the signal QQuickItemGrabResult::ready()
317 * is emitted when the grab has been completed.
318 *
319 * Use \a targetSize to specify the size of the target image. By default, the
320 * result will have the same size as item.
321 *
322 * If the grab could not be initiated, the function returns \c null.
323 *
324 * \note This function will render the item to an offscreen surface and
325 * copy that surface from the GPU's memory into the CPU's memory, which can
326 * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers}
327 * or ShaderEffectSource.
328 *
329 * \sa QQuickWindow::grabWindow()
330 */
331QSharedPointer<QQuickItemGrabResult> QQuickItem::grabToImage(const QSize &targetSize)
332{
333 QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, targetSize);
334 if (!result)
335 return QSharedPointer<QQuickItemGrabResult>();
336
337 connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection);
338 connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection);
339
340 return QSharedPointer<QQuickItemGrabResult>(result);
341}
342
343/*!
344 * \qmlmethod bool QtQuick::Item::grabToImage(callback, targetSize)
345 *
346 * Grabs the item into an in-memory image.
347 *
348 * The grab happens asynchronously and the JavaScript function \a callback is
349 * invoked when the grab is completed. The callback takes one argument, which
350 * is the result of the grab operation; an \l ItemGrabResult object.
351 *
352 * Use \a targetSize to specify the size of the target image. By default, the result
353 * will have the same size as the item.
354 *
355 * If the grab could not be initiated, the function returns \c false.
356 *
357 * The following snippet shows how to grab an item and store the results in
358 * a file:
359 *
360 * \snippet qml/item/itemGrab.qml grab-to-file
361 *
362 * The following snippet shows how to grab an item and use the results in
363 * another image element:
364 *
365 * \snippet qml/item/itemGrab.qml grab-to-image
366 *
367 * \note This function will render the item to an offscreen surface and
368 * copy that surface from the GPU's memory into the CPU's memory, which can
369 * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers}
370 * or ShaderEffectSource.
371 */
372
373/*!
374 * \internal
375 * Only visible from QML.
376 */
377bool QQuickItem::grabToImage(const QJSValue &callback, const QSize &targetSize)
378{
379 QQmlEngine *engine = qmlEngine(this);
380 if (!engine) {
381 qmlWarning(this) << "grabToImage: item has no QML engine";
382 return false;
383 }
384
385 if (!callback.isCallable()) {
386 qmlWarning(this) << "grabToImage: 'callback' is not a function";
387 return false;
388 }
389
390 QSize size = targetSize;
391 if (size.isEmpty())
392 size = QSize(width(), height());
393
394 if (size.width() < 1 || size.height() < 1) {
395 qmlWarning(this) << "grabToImage: item has invalid dimensions";
396 return false;
397 }
398
399 if (!window()) {
400 qmlWarning(this) << "grabToImage: item is not attached to a window";
401 return false;
402 }
403
404 QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, size);
405 if (!result)
406 return false;
407
408 connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection);
409 connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection);
410
411 QQuickItemGrabResultPrivate *d = result->d_func();
412 d->qmlEngine = engine;
413 d->callback = callback;
414 return true;
415}
416
417QT_END_NAMESPACE
418
419#include "moc_qquickitemgrabresult.cpp"
QPointer< QQuickWindow > window
static QQuickItemGrabResult * create(QQuickItem *item, const QSize &size)
QT_BEGIN_NAMESPACE const QEvent::Type Event_Grab_Completed