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