1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtOpenGL module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*!
41 \class QGLPixelBuffer
42 \inmodule QtOpenGL
43 \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer.
44 \since 4.1
45 \obsolete
46
47 \ingroup painting-3D
48
49 Rendering into a pbuffer is normally done using full hardware
50 acceleration. This can be significantly faster than rendering
51 into a QPixmap.
52
53 There are three approaches to using this class:
54
55 \list 1
56 \li \b{We can draw into the pbuffer and convert it to a QImage
57 using toImage().} This is normally much faster than calling
58 QGLWidget::renderPixmap().
59
60 \li \b{We can draw into the pbuffer and copy the contents into
61 an OpenGL texture using updateDynamicTexture().} This allows
62 us to create dynamic textures and works on all systems
63 with pbuffer support.
64
65 \li \b{On systems that support it, we can bind the pbuffer to
66 an OpenGL texture.} The texture is then updated automatically
67 when the pbuffer contents change, eliminating the need for
68 additional copy operations. This is supported only on Windows
69 and \macos systems that provide the \c render_texture
70 extension. Note that under Windows, a multi-sampled pbuffer
71 can't be used in conjunction with the \c render_texture
72 extension. If a multi-sampled pbuffer is requested under
73 Windows, the \c render_texture extension is turned off for that
74 pbuffer.
75
76
77 \endlist
78
79 \note This class has been deprecated, use QOpenGLFramebufferObject
80 for offscreen rendering.
81
82 \section1 Threading
83
84 As of Qt 4.8, it's possible to render into a QGLPixelBuffer using
85 a QPainter in a separate thread. Note that OpenGL 2.0 or OpenGL ES
86 2.0 is required for this to work.
87
88 Pbuffers are provided by the OpenGL \c pbuffer extension; call
89 hasOpenGLPbuffer() to find out if the system provides pbuffers.
90*/
91
92#include <private/qopenglextensions_p.h>
93
94#include <QtCore/qglobal.h>
95#include <QtGui/qopenglframebufferobject.h>
96
97#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
98
99#include <qglframebufferobject.h>
100#include <qglpixelbuffer.h>
101#include <private/qglpixelbuffer_p.h>
102#include <private/qfont_p.h>
103#include <qimage.h>
104
105QT_BEGIN_NAMESPACE
106
107extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool);
108
109
110QGLContext* QGLPBufferGLPaintDevice::context() const
111{
112 return pbuf->d_func()->qctx;
113}
114
115void QGLPBufferGLPaintDevice::beginPaint()
116{
117 pbuf->makeCurrent();
118 QGLPaintDevice::beginPaint();
119}
120
121void QGLPBufferGLPaintDevice::endPaint()
122{
123 QOpenGLContext::currentContext()->functions()->glFlush();
124 QGLPaintDevice::endPaint();
125}
126
127void QGLPBufferGLPaintDevice::setFbo(GLuint fbo)
128{
129 m_thisFBO = fbo;
130}
131
132void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
133{
134 pbuf = pb;
135}
136
137void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
138{
139 Q_Q(QGLPixelBuffer);
140 if(init(size, format, shareWidget)) {
141 req_size = size;
142 req_format = format;
143 req_shareWidget = shareWidget;
144 invalid = false;
145 glDevice.setPBuffer(q);
146 }
147}
148
149/*!
150 Constructs an OpenGL pbuffer of the given \a size. If no \a
151 format is specified, the \l{QGLFormat::defaultFormat()}{default
152 format} is used. If the \a shareWidget parameter points to a
153 valid QGLWidget, the pbuffer will share its context with \a
154 shareWidget.
155
156 If you intend to bind this pbuffer as a dynamic texture, the width
157 and height components of \c size must be powers of two (e.g., 512
158 x 128).
159
160 \sa size(), format()
161*/
162QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
163 : d_ptr(new QGLPixelBufferPrivate(this))
164{
165 Q_D(QGLPixelBuffer);
166 d->common_init(size, format, shareWidget);
167}
168
169
170/*! \overload
171
172 Constructs an OpenGL pbuffer with the \a width and \a height. If
173 no \a format is specified, the
174 \l{QGLFormat::defaultFormat()}{default format} is used. If the \a
175 shareWidget parameter points to a valid QGLWidget, the pbuffer
176 will share its context with \a shareWidget.
177
178 If you intend to bind this pbuffer as a dynamic texture, the width
179 and height components of \c size must be powers of two (e.g., 512
180 x 128).
181
182 \sa size(), format()
183*/
184QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget)
185 : d_ptr(new QGLPixelBufferPrivate(this))
186{
187 Q_D(QGLPixelBuffer);
188 d->common_init(QSize(width, height), format, shareWidget);
189}
190
191
192/*! \fn QGLPixelBuffer::~QGLPixelBuffer()
193
194 Destroys the pbuffer and frees any allocated resources.
195*/
196QGLPixelBuffer::~QGLPixelBuffer()
197{
198 Q_D(QGLPixelBuffer);
199
200 // defined in qpaintengine_opengl.cpp
201 QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
202 if (current != d->qctx)
203 makeCurrent();
204 d->cleanup();
205 if (current && current != d->qctx)
206 current->makeCurrent();
207}
208
209/*! \fn bool QGLPixelBuffer::makeCurrent()
210
211 Makes this pbuffer the current OpenGL rendering context. Returns
212 true on success; otherwise returns \c false.
213
214 \sa QGLContext::makeCurrent(), doneCurrent()
215*/
216
217bool QGLPixelBuffer::makeCurrent()
218{
219 Q_D(QGLPixelBuffer);
220 if (d->invalid)
221 return false;
222 d->qctx->makeCurrent();
223 if (!d->fbo) {
224 QOpenGLFramebufferObjectFormat format;
225 if (d->req_format.stencil())
226 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
227 else if (d->req_format.depth())
228 format.setAttachment(QOpenGLFramebufferObject::Depth);
229 if (d->req_format.sampleBuffers())
230 format.setSamples(d->req_format.samples());
231 d->fbo = new QOpenGLFramebufferObject(d->req_size, format);
232 d->fbo->bind();
233 d->glDevice.setFbo(d->fbo->handle());
234 QOpenGLContext::currentContext()->functions()->glViewport(0, 0, d->req_size.width(), d->req_size.height());
235 }
236 return true;
237}
238
239/*! \fn bool QGLPixelBuffer::doneCurrent()
240
241 Makes no context the current OpenGL context. Returns \c true on
242 success; otherwise returns \c false.
243*/
244
245bool QGLPixelBuffer::doneCurrent()
246{
247 Q_D(QGLPixelBuffer);
248 if (d->invalid)
249 return false;
250 d->qctx->doneCurrent();
251 return true;
252}
253
254/*!
255 Returns the context of this pixelbuffer.
256*/
257QGLContext *QGLPixelBuffer::context() const
258{
259 Q_D(const QGLPixelBuffer);
260 return d->qctx;
261}
262
263/*!
264 \fn GLuint QGLPixelBuffer::generateDynamicTexture() const
265
266 Generates and binds a 2D GL texture that is the same size as the
267 pbuffer, and returns the texture's ID. This can be used in
268 conjunction with bindToDynamicTexture() and
269 updateDynamicTexture().
270
271 \sa size()
272*/
273
274/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
275
276 Binds the texture specified by \a texture_id to this pbuffer.
277 Returns \c true on success; otherwise returns \c false.
278
279 The texture must be of the same size and format as the pbuffer.
280
281 To unbind the texture, call releaseFromDynamicTexture(). While
282 the texture is bound, it is updated automatically when the
283 pbuffer contents change, eliminating the need for additional copy
284 operations.
285
286 Example:
287
288 \snippet code/src_opengl_qglpixelbuffer.cpp 0
289
290 \warning This function uses the \c {render_texture} extension,
291 which is currently not supported under X11. An alternative that
292 works on all systems (including X11) is to manually copy the
293 pbuffer contents to a texture using updateDynamicTexture().
294
295 \warning For the bindToDynamicTexture() call to succeed on the
296 \macos, the pbuffer needs a shared context, i.e. the
297 QGLPixelBuffer must be created with a share widget.
298
299 \sa generateDynamicTexture(), releaseFromDynamicTexture()
300*/
301
302/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture()
303
304 Releases the pbuffer from any previously bound texture.
305
306 \sa bindToDynamicTexture()
307*/
308
309/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers()
310
311 Returns \c true if the OpenGL \c pbuffer extension is present on
312 this system; otherwise returns \c false.
313*/
314
315/*!
316 Copies the pbuffer contents into the texture specified with \a
317 texture_id.
318
319 The texture must be of the same size and format as the pbuffer.
320
321 Example:
322
323 \snippet code/src_opengl_qglpixelbuffer.cpp 1
324
325 An alternative on Windows and \macos systems that support the
326 \c render_texture extension is to use bindToDynamicTexture() to
327 get dynamic updates of the texture.
328
329 \sa generateDynamicTexture(), bindToDynamicTexture()
330*/
331void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const
332{
333 Q_D(const QGLPixelBuffer);
334 if (d->invalid || !d->fbo)
335 return;
336
337 const QGLContext *ctx = QGLContext::currentContext();
338 if (!ctx)
339 return;
340
341#undef glBindFramebuffer
342
343#ifndef GL_READ_FRAMEBUFFER
344#define GL_READ_FRAMEBUFFER 0x8CA8
345#endif
346
347#ifndef GL_DRAW_FRAMEBUFFER
348#define GL_DRAW_FRAMEBUFFER 0x8CA9
349#endif
350
351 QOpenGLExtensions extensions(ctx->contextHandle());
352
353 ctx->d_ptr->refreshCurrentFbo();
354
355 if (d->blit_fbo) {
356 QOpenGLFramebufferObject::blitFramebuffer(d->blit_fbo, d->fbo);
357 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, d->blit_fbo->handle());
358 }
359
360 extensions.glBindTexture(GL_TEXTURE_2D, texture_id);
361#ifndef QT_OPENGL_ES
362 GLenum format = ctx->contextHandle()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
363 extensions.glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, d->req_size.width(), d->req_size.height(), 0);
364#else
365 extensions.glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0);
366#endif
367
368 if (d->blit_fbo)
369 extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->d_func()->current_fbo);
370}
371
372/*!
373 Returns the size of the pbuffer.
374*/
375QSize QGLPixelBuffer::size() const
376{
377 Q_D(const QGLPixelBuffer);
378 return d->req_size;
379}
380
381/*!
382 Returns the contents of the pbuffer as a QImage.
383*/
384QImage QGLPixelBuffer::toImage() const
385{
386 Q_D(const QGLPixelBuffer);
387 if (d->invalid)
388 return QImage();
389
390 const_cast<QGLPixelBuffer *>(this)->makeCurrent();
391 if (d->fbo)
392 d->fbo->bind();
393 return qt_gl_read_frame_buffer(d->req_size, d->format.alpha(), true);
394}
395
396/*!
397 Returns the native pbuffer handle.
398*/
399Qt::HANDLE QGLPixelBuffer::handle() const
400{
401 Q_D(const QGLPixelBuffer);
402 if (d->invalid)
403 return 0;
404 return (Qt::HANDLE) d->pbuf;
405}
406
407/*!
408 Returns \c true if this pbuffer is valid; otherwise returns \c false.
409*/
410bool QGLPixelBuffer::isValid() const
411{
412 Q_D(const QGLPixelBuffer);
413 return !d->invalid;
414}
415
416Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
417
418/*! \reimp */
419QPaintEngine *QGLPixelBuffer::paintEngine() const
420{
421 return qt_buffer_2_engine()->engine();
422}
423
424/*! \reimp */
425int QGLPixelBuffer::metric(PaintDeviceMetric metric) const
426{
427 Q_D(const QGLPixelBuffer);
428
429 float dpmx = qt_defaultDpiX()*100./2.54;
430 float dpmy = qt_defaultDpiY()*100./2.54;
431 int w = d->req_size.width();
432 int h = d->req_size.height();
433 switch (metric) {
434 case PdmWidth:
435 return w;
436
437 case PdmHeight:
438 return h;
439
440 case PdmWidthMM:
441 return qRound(w * 1000 / dpmx);
442
443 case PdmHeightMM:
444 return qRound(h * 1000 / dpmy);
445
446 case PdmNumColors:
447 return 0;
448
449 case PdmDepth:
450 return 32;//d->depth;
451
452 case PdmDpiX:
453 return qRound(dpmx * 0.0254);
454
455 case PdmDpiY:
456 return qRound(dpmy * 0.0254);
457
458 case PdmPhysicalDpiX:
459 return qRound(dpmx * 0.0254);
460
461 case PdmPhysicalDpiY:
462 return qRound(dpmy * 0.0254);
463
464 case QPaintDevice::PdmDevicePixelRatio:
465 return 1;
466
467 case QPaintDevice::PdmDevicePixelRatioScaled:
468 return QPaintDevice::devicePixelRatioFScale();
469
470 default:
471 qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric);
472 break;
473 }
474 return 0;
475}
476
477/*!
478 Generates and binds a 2D GL texture to the current context, based
479 on \a image. The generated texture id is returned and can be used
480 in later glBindTexture() calls.
481
482 The \a target parameter specifies the texture target.
483
484 Equivalent to calling QGLContext::bindTexture().
485
486 \sa deleteTexture()
487*/
488GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target)
489{
490 Q_D(QGLPixelBuffer);
491#ifndef QT_OPENGL_ES
492 GLenum format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
493 return d->qctx->bindTexture(image, target, GLint(format));
494#else
495 return d->qctx->bindTexture(image, target, GL_RGBA);
496#endif
497}
498
499/*! \overload
500
501 Generates and binds a 2D GL texture based on \a pixmap.
502
503 Equivalent to calling QGLContext::bindTexture().
504
505 \sa deleteTexture()
506*/
507GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target)
508{
509 Q_D(QGLPixelBuffer);
510#ifndef QT_OPENGL_ES
511 GLenum format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8;
512 return d->qctx->bindTexture(pixmap, target, GLint(format));
513#else
514 return d->qctx->bindTexture(pixmap, target, GL_RGBA);
515#endif
516}
517
518/*! \overload
519
520 Reads the DirectDrawSurface (DDS) compressed file \a fileName and
521 generates a 2D GL texture from it.
522
523 Equivalent to calling QGLContext::bindTexture().
524
525 \sa deleteTexture()
526*/
527GLuint QGLPixelBuffer::bindTexture(const QString &fileName)
528{
529 Q_D(QGLPixelBuffer);
530 return d->qctx->bindTexture(fileName);
531}
532
533/*!
534 Removes the texture identified by \a texture_id from the texture cache.
535
536 Equivalent to calling QGLContext::deleteTexture().
537 */
538void QGLPixelBuffer::deleteTexture(GLuint texture_id)
539{
540 Q_D(QGLPixelBuffer);
541 d->qctx->deleteTexture(texture_id);
542}
543
544/*!
545 \since 4.4
546
547 Draws the given texture, \a textureId, to the given target rectangle,
548 \a target, in OpenGL model space. The \a textureTarget should be a 2D
549 texture target.
550
551 Equivalent to the corresponding QGLContext::drawTexture().
552*/
553void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
554{
555 Q_D(QGLPixelBuffer);
556 d->qctx->drawTexture(target, textureId, textureTarget);
557}
558
559/*!
560 \since 4.4
561
562 Draws the given texture, \a textureId, at the given \a point in OpenGL model
563 space. The textureTarget parameter should be a 2D texture target.
564
565 Equivalent to the corresponding QGLContext::drawTexture().
566*/
567void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
568{
569 Q_D(QGLPixelBuffer);
570 d->qctx->drawTexture(point, textureId, textureTarget);
571}
572
573/*!
574 Returns the format of the pbuffer. The format may be different
575 from the one that was requested.
576*/
577QGLFormat QGLPixelBuffer::format() const
578{
579 Q_D(const QGLPixelBuffer);
580 return d->format;
581}
582
583/*! \fn int QGLPixelBuffer::devType() const
584 \internal
585*/
586
587bool QGLPixelBufferPrivate::init(const QSize &, const QGLFormat &f, QGLWidget *shareWidget)
588{
589 widget = new QGLWidget(f, 0, shareWidget);
590 widget->resize(1, 1);
591 qctx = const_cast<QGLContext *>(widget->context());
592 return widget->isValid();
593}
594
595bool QGLPixelBufferPrivate::cleanup()
596{
597 delete fbo;
598 fbo = 0;
599 delete blit_fbo;
600 blit_fbo = 0;
601 delete widget;
602 widget = 0;
603 return true;
604}
605
606bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
607{
608 Q_UNUSED(texture_id);
609 return false;
610}
611
612void QGLPixelBuffer::releaseFromDynamicTexture()
613{
614}
615
616GLuint QGLPixelBuffer::generateDynamicTexture() const
617{
618 Q_D(const QGLPixelBuffer);
619 if (!d->fbo)
620 return 0;
621
622 if (d->fbo->format().samples() > 0
623 && QOpenGLExtensions(QOpenGLContext::currentContext())
624 .hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit))
625 {
626 if (!d->blit_fbo)
627 const_cast<QOpenGLFramebufferObject *&>(d->blit_fbo) = new QOpenGLFramebufferObject(d->req_size);
628 } else {
629 return d->fbo->texture();
630 }
631
632 GLuint texture;
633 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
634
635 funcs->glGenTextures(1, &texture);
636 funcs->glBindTexture(GL_TEXTURE_2D, texture);
637
638 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
639 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
640 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
641 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
642
643 funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->req_size.width(), d->req_size.height(), 0,
644 GL_RGBA, GL_UNSIGNED_BYTE, 0);
645
646 return texture;
647}
648
649bool QGLPixelBuffer::hasOpenGLPbuffers()
650{
651 return QGLFramebufferObject::hasOpenGLFramebufferObjects();
652}
653
654QT_END_NAMESPACE
655