1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include <Qt3DRender/qrendercapture.h>
38#include <Qt3DRender/private/qrendercapture_p.h>
39#include <Qt3DCore/qscenechange.h>
40#include <Qt3DCore/qpropertyupdatedchange.h>
41#include <Qt3DRender/qframegraphnodecreatedchange.h>
42
43#include <QPointer>
44#include <QMutexLocker>
45
46QT_BEGIN_NAMESPACE
47
48namespace Qt3DRender {
49
50/*!
51 * \class Qt3DRender::QRenderCapture
52 * \inheaderfile Qt3DRender/QRenderCapture
53 * \inmodule Qt3DRender
54 *
55 * \brief Frame graph node for render capture.
56 *
57 * The QRenderCapture is used to capture rendering into an image at any render stage.
58 * Capturing must be initiated by the user and one image is returned per capture request.
59 * User can issue multiple render capture requests simultaniously, but only one request
60 * is served per QRenderCapture instance per frame.
61 *
62 * \since 5.8
63 */
64
65/*!
66 * \qmltype RenderCapture
67 * \instantiates Qt3DRender::QRenderCapture
68 * \inherits FrameGraphNode
69 * \inqmlmodule Qt3D.Render
70 * \since 5.8
71 * \brief Capture rendering.
72 */
73
74/*!
75 * \class Qt3DRender::QRenderCaptureReply
76 * \inheaderfile Qt3DRender/QRenderCaptureReply
77 * \inmodule Qt3DRender
78 *
79 * \brief Receives the result of render capture request.
80 *
81 * An object, which receives the image from QRenderCapture::requestCapture.
82 *
83 * \since 5.8
84 */
85
86/*!
87 * \qmltype RenderCaptureReply
88 * \instantiates Qt3DRender::QRenderCaptureReply
89 * \inherits QObject
90 * \inqmlmodule Qt3D.Render
91 * \since 5.8
92 * \brief Receives render capture result.
93 */
94
95/*!
96 * \qmlproperty variant Qt3D.Render::RenderCaptureReply::image
97 *
98 * Holds the image, which was produced as a result of render capture.
99 */
100
101/*!
102 * \qmlproperty int Qt3D.Render::RenderCaptureReply::captureId
103 *
104 * Holds the captureId, which was passed to the renderCapture.
105 */
106
107/*!
108 * \qmlproperty bool Qt3D.Render::RenderCaptureReply::complete
109 *
110 * Holds the complete state of the render capture.
111 */
112
113/*!
114 * \qmlmethod bool Qt3D.Render::RenderCaptureReply::saveImage(fileName)
115 *
116 * Saves the render capture result as an image to \a fileName.
117 * Returns true if the image was successfully saved; otherwise returns false.
118 *
119 * \since 5.9
120 */
121
122/*!
123 * \qmlmethod void Qt3D.Render::RenderCaptureReply::saveToFile(fileName)
124 * \deprecated
125 *
126 * Saves the render capture result as an image to \a fileName.
127 *
128 * Deprecated in 5.9. Use saveImage().
129 */
130
131/*!
132 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture(int captureId)
133 * \deprecated
134 *
135 * Used to request render capture. User can specify a \a captureId to identify
136 * the request. The requestId does not have to be unique. Only one render capture result
137 * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
138 * The function returns a QRenderCaptureReply object, which receives the captured image
139 * when it is done. The user is responsible for deallocating the returned object.
140 */
141
142/*!
143 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture()
144 *
145 * Used to request render capture. Only one render capture result is produced per
146 * requestCapture call even if the frame graph has multiple leaf nodes.
147 * The function returns a QRenderCaptureReply object, which receives the captured image
148 * when it is done. The user is responsible for deallocating the returned object.
149 */
150
151/*!
152 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture(Rect rect)
153 *
154 * Used to request render capture from a specified \a rect. Only one render capture
155 * result is produced per requestCapture call even if the frame graph has multiple leaf nodes.
156 * The function returns a QRenderCaptureReply object, which receives the captured image
157 * when it is done. The user is responsible for deallocating the returned object.
158 */
159
160/*!
161 * \internal
162 */
163QRenderCaptureReplyPrivate::QRenderCaptureReplyPrivate()
164 : QObjectPrivate()
165 , m_captureId(0)
166 , m_complete(false)
167{
168
169}
170
171/*!
172 * The constructor creates an instance with the specified \a parent.
173 */
174QRenderCaptureReply::QRenderCaptureReply(QObject *parent)
175 : QObject(* new QRenderCaptureReplyPrivate, parent)
176{
177
178}
179
180/*!
181 * \property QRenderCaptureReply::image
182 *
183 * Holds the image, which was produced as a result of render capture.
184 */
185QImage QRenderCaptureReply::image() const
186{
187 Q_D(const QRenderCaptureReply);
188 return d->m_image;
189}
190
191/*!
192 * \property QRenderCaptureReply::captureId
193 *
194 * Holds the captureId, which was passed to the renderCapture.
195 */
196int QRenderCaptureReply::captureId() const
197{
198 Q_D(const QRenderCaptureReply);
199 return d->m_captureId;
200}
201
202/*!
203 * \property QRenderCaptureReply::complete
204 *
205 * Holds the complete state of the render capture.
206 */
207bool QRenderCaptureReply::isComplete() const
208{
209 Q_D(const QRenderCaptureReply);
210 return d->m_complete;
211}
212
213/*!
214 * Saves the render capture result as an image to \a fileName.
215 *
216 * Returns true if the image was successfully saved; otherwise returns false.
217 * \since 5.9
218 */
219bool QRenderCaptureReply::saveImage(const QString &fileName) const
220{
221 Q_D(const QRenderCaptureReply);
222 if (d->m_complete)
223 {
224 return d->m_image.save(fileName);
225 }
226 return false;
227}
228
229/*!
230 * \deprecated
231 * Saves the render capture result as an image to \a fileName.
232 *
233 * Deprecated in 5.9. Use saveImage().
234 */
235void QRenderCaptureReply::saveToFile(const QString &fileName) const
236{
237 Q_D(const QRenderCaptureReply);
238 if (d->m_complete)
239 d->m_image.save(fileName);
240}
241
242/*!
243 * \internal
244 */
245QRenderCapturePrivate::QRenderCapturePrivate()
246 : QFrameGraphNodePrivate()
247{
248}
249
250/*!
251 * \internal
252 */
253QRenderCapturePrivate::~QRenderCapturePrivate()
254{
255}
256
257/*!
258 * \internal
259 */
260QRenderCaptureReply *QRenderCapturePrivate::createReply(int captureId)
261{
262 QMutexLocker lock(&m_mutex);
263 QRenderCaptureReply *reply = new QRenderCaptureReply();
264 reply->d_func()->m_captureId = captureId;
265 m_waitingReplies.push_back(t: reply);
266 return reply;
267}
268
269/*!
270 * \internal
271 */
272QRenderCaptureReply *QRenderCapturePrivate::takeReply(int captureId)
273{
274 QRenderCaptureReply *reply = nullptr;
275 QMutexLocker lock(&m_mutex);
276 for (int i = 0; i < m_waitingReplies.size(); ++i) {
277 if (m_waitingReplies[i]->d_func()->m_captureId == captureId) {
278 reply = m_waitingReplies.takeAt(i);
279 break;
280 }
281 }
282 return reply;
283}
284
285/*!
286 * \internal
287 */
288void QRenderCapturePrivate::setImage(QRenderCaptureReply *reply, const QImage &image)
289{
290 reply->d_func()->m_complete = true;
291 reply->d_func()->m_image = image;
292}
293
294/*!
295 * \internal
296 */
297void QRenderCapturePrivate::replyDestroyed(QRenderCaptureReply *reply)
298{
299 QMutexLocker lock(&m_mutex);
300 m_waitingReplies.removeAll(t: reply);
301}
302
303/*!
304 * The constructor creates an instance with the specified \a parent.
305 */
306QRenderCapture::QRenderCapture(Qt3DCore::QNode *parent)
307 : QFrameGraphNode(*new QRenderCapturePrivate, parent)
308{
309}
310
311/*!
312 * \deprecated Used to request render capture. User can specify a \a captureId
313 * to identify the request. The requestId does not have to be unique. Only one
314 * render capture result is produced per requestCapture call even if the frame
315 * graph has multiple leaf nodes. The function returns a QRenderCaptureReply
316 * object, which receives the captured image when it is done. The user is
317 * responsible for deallocating the returned object by calling deleteLater().
318 */
319QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
320{
321 Q_D(QRenderCapture);
322 QRenderCaptureReply *reply = d->createReply(captureId);
323 reply->setParent(this);
324 QObject::connect(sender: reply, signal: &QObject::destroyed, context: this, slot: [&, reply, d] (QObject *) {
325 d->replyDestroyed(reply);
326 });
327
328 const QRenderCaptureRequest request = { .captureId: captureId, .rect: QRect() };
329 d->m_pendingRequests.push_back(t: request);
330 d->update();
331
332 return reply;
333}
334
335/*!
336 * Used to request render capture from a specified \a rect. Only one render
337 * capture result is produced per requestCapture call even if the frame graph
338 * has multiple leaf nodes. The function returns a QRenderCaptureReply object,
339 * which receives the captured image when it is done. The user is responsible
340 * for deallocating the returned object by calling deleteLater().
341 */
342QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
343{
344 Q_D(QRenderCapture);
345 static int captureId = 1;
346 QRenderCaptureReply *reply = d->createReply(captureId);
347 reply->setParent(this);
348 QObject::connect(sender: reply, signal: &QObject::destroyed, context: this, slot: [&, reply, d] (QObject *) {
349 d->replyDestroyed(reply);
350 });
351
352 const QRenderCaptureRequest request = { .captureId: captureId, .rect: rect };
353 d->m_pendingRequests.push_back(t: request);
354 d->update();
355
356 captureId++;
357
358 return reply;
359}
360
361/*!
362 * Used to request render capture. Only one render capture result is produced
363 * per requestCapture call even if the frame graph has multiple leaf nodes. The
364 * function returns a QRenderCaptureReply object, which receives the captured
365 * image when it is done. The user is responsible for deallocating the returned
366 * object by calling deleterLater().
367 */
368Qt3DRender::QRenderCaptureReply *QRenderCapture::requestCapture()
369{
370 return requestCapture(rect: QRect());
371}
372
373/*!
374 * \internal
375 */
376void QRenderCapture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
377{
378 Q_D(QRenderCapture);
379 Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(src: change);
380 if (propertyChange->type() == Qt3DCore::PropertyUpdated) {
381 if (propertyChange->propertyName() == QByteArrayLiteral("renderCaptureData")) {
382 RenderCaptureDataPtr data = propertyChange->value().value<RenderCaptureDataPtr>();
383 QPointer<QRenderCaptureReply> reply = d->takeReply(captureId: data.data()->captureId);
384 if (!reply.isNull()) {
385 d->setImage(reply, image: data.data()->image);
386 emit reply->completed();
387QT_WARNING_PUSH
388QT_WARNING_DISABLE_DEPRECATED
389 if (reply)
390 emit reply->completeChanged(isComplete: true);
391QT_WARNING_POP
392 }
393 }
394 }
395}
396
397/*!
398 * \internal
399 */
400Qt3DCore::QNodeCreatedChangeBasePtr QRenderCapture::createNodeCreationChange() const
401{
402 auto creationChange = QFrameGraphNodeCreatedChangePtr<QRenderCaptureInitData>::create(arguments: this);
403 QRenderCaptureInitData &data = creationChange->data;
404 data.captureId = 0;
405 return creationChange;
406}
407
408} // Qt3DRender
409
410QT_END_NAMESPACE
411

source code of qt3d/src/render/framegraph/qrendercapture.cpp