1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*!
43 \class QGLPixelBuffer
44 \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer.
45 \since 4.1
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 \o \bold{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 \o \bold{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 \o \bold{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 Mac OS X 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
80 \section1 Threading
81
82 As of Qt 4.8, it's possible to render into a QGLPixelBuffer using
83 a QPainter in a separate thread. Note that OpenGL 2.0 or OpenGL ES
84 2.0 is required for this to work. Also, under X11, it's necessary
85 to set the Qt::AA_X11InitThreads application attribute.
86
87 Pbuffers are provided by the OpenGL \c pbuffer extension; call
88 hasOpenGLPbuffer() to find out if the system provides pbuffers.
89
90 \sa {opengl/pbuffers}{Pbuffers Example}
91*/
92
93#include <QtCore/qglobal.h>
94
95#if !defined(QT_OPENGL_ES_1)
96#include <private/qpaintengineex_opengl2_p.h>
97#endif
98
99#include <qglpixelbuffer.h>
100#include <private/qglpixelbuffer_p.h>
101#include <private/qfont_p.h>
102#include <qimage.h>
103
104#ifndef QT_OPENGL_ES_2
105#include <private/qpaintengine_opengl_p.h>
106#endif
107
108QT_BEGIN_NAMESPACE
109
110#if !defined(QT_OPENGL_ES_2)
111extern void qgl_cleanup_glyph_cache(QGLContext *);
112#else
113void qgl_cleanup_glyph_cache(QGLContext *) {}
114#endif
115
116extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
117
118
119QGLContext* QGLPBufferGLPaintDevice::context() const
120{
121 return pbuf->d_func()->qctx;
122}
123
124void QGLPBufferGLPaintDevice::endPaint() {
125 glFlush();
126 QGLPaintDevice::endPaint();
127}
128
129void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
130{
131 pbuf = pb;
132}
133
134void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
135{
136 Q_Q(QGLPixelBuffer);
137 if(init(size, format, shareWidget)) {
138 req_size = size;
139 req_format = format;
140 req_shareWidget = shareWidget;
141 invalid = false;
142 qctx = new QGLContext(format);
143 qctx->d_func()->sharing = (shareWidget != 0);
144 if (shareWidget != 0 && shareWidget->d_func()->glcx) {
145 QGLContextGroup::addShare(qctx, shareWidget->d_func()->glcx);
146 shareWidget->d_func()->glcx->d_func()->sharing = true;
147 }
148
149 glDevice.setPBuffer(q);
150 qctx->d_func()->paintDevice = q;
151 qctx->d_func()->valid = true;
152#if defined(Q_WS_WIN) && !defined(QT_OPENGL_ES)
153 qctx->d_func()->dc = dc;
154 qctx->d_func()->rc = ctx;
155#elif (defined(Q_WS_X11) && defined(QT_NO_EGL))
156 qctx->d_func()->cx = ctx;
157 qctx->d_func()->pbuf = (void *) pbuf;
158 qctx->d_func()->vi = 0;
159#elif defined(Q_WS_MAC)
160 qctx->d_func()->cx = ctx;
161 qctx->d_func()->vi = 0;
162#elif !defined(QT_NO_EGL)
163 qctx->d_func()->eglContext = ctx;
164 qctx->d_func()->eglSurface = pbuf;
165#endif
166 }
167}
168
169/*!
170 Constructs an OpenGL pbuffer of the given \a size. If no \a
171 format is specified, the \l{QGLFormat::defaultFormat()}{default
172 format} is used. If the \a shareWidget parameter points to a
173 valid QGLWidget, the pbuffer will share its context with \a
174 shareWidget.
175
176 If you intend to bind this pbuffer as a dynamic texture, the width
177 and height components of \c size must be powers of two (e.g., 512
178 x 128).
179
180 \sa size(), format()
181*/
182QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget)
183 : d_ptr(new QGLPixelBufferPrivate(this))
184{
185 Q_D(QGLPixelBuffer);
186 d->common_init(size, format, shareWidget);
187}
188
189
190/*! \overload
191
192 Constructs an OpenGL pbuffer with the \a width and \a height. If
193 no \a format is specified, the
194 \l{QGLFormat::defaultFormat()}{default format} is used. If the \a
195 shareWidget parameter points to a valid QGLWidget, the pbuffer
196 will share its context with \a shareWidget.
197
198 If you intend to bind this pbuffer as a dynamic texture, the width
199 and height components of \c size must be powers of two (e.g., 512
200 x 128).
201
202 \sa size(), format()
203*/
204QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget)
205 : d_ptr(new QGLPixelBufferPrivate(this))
206{
207 Q_D(QGLPixelBuffer);
208 d->common_init(QSize(width, height), format, shareWidget);
209}
210
211
212/*! \fn QGLPixelBuffer::~QGLPixelBuffer()
213
214 Destroys the pbuffer and frees any allocated resources.
215*/
216QGLPixelBuffer::~QGLPixelBuffer()
217{
218 Q_D(QGLPixelBuffer);
219
220 // defined in qpaintengine_opengl.cpp
221 QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
222 if (current != d->qctx)
223 makeCurrent();
224 qgl_cleanup_glyph_cache(d->qctx);
225 d->cleanup();
226 delete d->qctx;
227 if (current && current != d->qctx)
228 current->makeCurrent();
229}
230
231/*! \fn bool QGLPixelBuffer::makeCurrent()
232
233 Makes this pbuffer the current OpenGL rendering context. Returns
234 true on success; otherwise returns false.
235
236 \sa QGLContext::makeCurrent(), doneCurrent()
237*/
238
239bool QGLPixelBuffer::makeCurrent()
240{
241 Q_D(QGLPixelBuffer);
242 if (d->invalid)
243 return false;
244 d->qctx->makeCurrent();
245 return true;
246}
247
248/*! \fn bool QGLPixelBuffer::doneCurrent()
249
250 Makes no context the current OpenGL context. Returns true on
251 success; otherwise returns false.
252*/
253
254bool QGLPixelBuffer::doneCurrent()
255{
256 Q_D(QGLPixelBuffer);
257 if (d->invalid)
258 return false;
259 d->qctx->doneCurrent();
260 return true;
261}
262
263/*!
264 Generates and binds a 2D GL texture that is the same size as the
265 pbuffer, and returns the texture's ID. This can be used in
266 conjunction with bindToDynamicTexture() and
267 updateDynamicTexture().
268
269 \sa size()
270*/
271
272#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && defined(QT_NO_EGL)
273GLuint QGLPixelBuffer::generateDynamicTexture() const
274{
275 Q_D(const QGLPixelBuffer);
276 GLuint texture;
277 glGenTextures(1, &texture);
278 glBindTexture(GL_TEXTURE_2D, texture);
279 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_FLOAT, 0);
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
282 return texture;
283}
284#endif
285
286/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id)
287
288 Binds the texture specified by \a texture_id to this pbuffer.
289 Returns true on success; otherwise returns false.
290
291 The texture must be of the same size and format as the pbuffer.
292
293 To unbind the texture, call releaseFromDynamicTexture(). While
294 the texture is bound, it is updated automatically when the
295 pbuffer contents change, eliminating the need for additional copy
296 operations.
297
298 Example:
299
300 \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 0
301
302 \warning This function uses the \c {render_texture} extension,
303 which is currently not supported under X11. An alternative that
304 works on all systems (including X11) is to manually copy the
305 pbuffer contents to a texture using updateDynamicTexture().
306
307 \warning For the bindToDynamicTexture() call to succeed on the
308 Mac OS X, the pbuffer needs a shared context, i.e. the
309 QGLPixelBuffer must be created with a share widget.
310
311 \sa generateDynamicTexture(), releaseFromDynamicTexture()
312*/
313
314/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture()
315
316 Releases the pbuffer from any previously bound texture.
317
318 \sa bindToDynamicTexture()
319*/
320
321/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers()
322
323 Returns true if the OpenGL \c pbuffer extension is present on
324 this system; otherwise returns false.
325*/
326
327/*!
328 Copies the pbuffer contents into the texture specified with \a
329 texture_id.
330
331 The texture must be of the same size and format as the pbuffer.
332
333 Example:
334
335 \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 1
336
337 An alternative on Windows and Mac OS X systems that support the
338 \c render_texture extension is to use bindToDynamicTexture() to
339 get dynamic updates of the texture.
340
341 \sa generateDynamicTexture(), bindToDynamicTexture()
342*/
343void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const
344{
345 Q_D(const QGLPixelBuffer);
346 if (d->invalid)
347 return;
348 glBindTexture(GL_TEXTURE_2D, texture_id);
349#ifndef QT_OPENGL_ES
350 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0);
351#else
352 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0);
353#endif
354}
355
356#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
357void QGLPixelBuffer::updateDynamicTexture(QMacCompatGLuint texture_id) const
358{
359 updateDynamicTexture(GLuint(texture_id));
360}
361#endif
362
363/*!
364 Returns the size of the pbuffer.
365*/
366QSize QGLPixelBuffer::size() const
367{
368 Q_D(const QGLPixelBuffer);
369 return d->req_size;
370}
371
372/*!
373 Returns the contents of the pbuffer as a QImage.
374*/
375QImage QGLPixelBuffer::toImage() const
376{
377 Q_D(const QGLPixelBuffer);
378 if (d->invalid)
379 return QImage();
380
381 const_cast<QGLPixelBuffer *>(this)->makeCurrent();
382 return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
383}
384
385/*!
386 Returns the native pbuffer handle.
387*/
388Qt::HANDLE QGLPixelBuffer::handle() const
389{
390 Q_D(const QGLPixelBuffer);
391 if (d->invalid)
392 return 0;
393 return (Qt::HANDLE) d->pbuf;
394}
395
396/*!
397 Returns true if this pbuffer is valid; otherwise returns false.
398*/
399bool QGLPixelBuffer::isValid() const
400{
401 Q_D(const QGLPixelBuffer);
402 return !d->invalid;
403}
404
405#if !defined(QT_OPENGL_ES_1)
406Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
407#endif
408
409#ifndef QT_OPENGL_ES_2
410Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine)
411#endif
412
413/*! \reimp */
414QPaintEngine *QGLPixelBuffer::paintEngine() const
415{
416#if defined(QT_OPENGL_ES_1)
417 return qt_buffer_engine()->engine();
418#elif defined(QT_OPENGL_ES_2)
419 return qt_buffer_2_engine()->engine();
420#else
421 if (qt_gl_preferGL2Engine())
422 return qt_buffer_2_engine()->engine();
423 else
424 return qt_buffer_engine()->engine();
425#endif
426}
427
428/*! \reimp */
429int QGLPixelBuffer::metric(PaintDeviceMetric metric) const
430{
431 Q_D(const QGLPixelBuffer);
432
433 float dpmx = qt_defaultDpiX()*100./2.54;
434 float dpmy = qt_defaultDpiY()*100./2.54;
435 int w = d->req_size.width();
436 int h = d->req_size.height();
437 switch (metric) {
438 case PdmWidth:
439 return w;
440
441 case PdmHeight:
442 return h;
443
444 case PdmWidthMM:
445 return qRound(w * 1000 / dpmx);
446
447 case PdmHeightMM:
448 return qRound(h * 1000 / dpmy);
449
450 case PdmNumColors:
451 return 0;
452
453 case PdmDepth:
454 return 32;//d->depth;
455
456 case PdmDpiX:
457 return qRound(dpmx * 0.0254);
458
459 case PdmDpiY:
460 return qRound(dpmy * 0.0254);
461
462 case PdmPhysicalDpiX:
463 return qRound(dpmx * 0.0254);
464
465 case PdmPhysicalDpiY:
466 return qRound(dpmy * 0.0254);
467
468 default:
469 qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric);
470 break;
471 }
472 return 0;
473}
474
475/*!
476 Generates and binds a 2D GL texture to the current context, based
477 on \a image. The generated texture id is returned and can be used
478 in later glBindTexture() calls.
479
480 The \a target parameter specifies the texture target.
481
482 Equivalent to calling QGLContext::bindTexture().
483
484 \sa deleteTexture()
485*/
486GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target)
487{
488 Q_D(QGLPixelBuffer);
489#ifndef QT_OPENGL_ES
490 return d->qctx->bindTexture(image, target, GLint(GL_RGBA8));
491#else
492 return d->qctx->bindTexture(image, target, GL_RGBA);
493#endif
494}
495
496#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
497/*! \internal */
498GLuint QGLPixelBuffer::bindTexture(const QImage &image, QMacCompatGLenum target)
499{
500 Q_D(QGLPixelBuffer);
501 return d->qctx->bindTexture(image, target, QMacCompatGLint(GL_RGBA8));
502}
503#endif
504
505/*! \overload
506
507 Generates and binds a 2D GL texture based on \a pixmap.
508
509 Equivalent to calling QGLContext::bindTexture().
510
511 \sa deleteTexture()
512*/
513GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target)
514{
515 Q_D(QGLPixelBuffer);
516#ifndef QT_OPENGL_ES
517 return d->qctx->bindTexture(pixmap, target, GLint(GL_RGBA8));
518#else
519 return d->qctx->bindTexture(pixmap, target, GL_RGBA);
520#endif
521}
522
523#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
524/*! \internal */
525GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target)
526{
527 Q_D(QGLPixelBuffer);
528 return d->qctx->bindTexture(pixmap, target, QMacCompatGLint(GL_RGBA8));
529}
530#endif
531
532/*! \overload
533
534 Reads the DirectDrawSurface (DDS) compressed file \a fileName and
535 generates a 2D GL texture from it.
536
537 Equivalent to calling QGLContext::bindTexture().
538
539 \sa deleteTexture()
540*/
541GLuint QGLPixelBuffer::bindTexture(const QString &fileName)
542{
543 Q_D(QGLPixelBuffer);
544 return d->qctx->bindTexture(fileName);
545}
546
547/*!
548 Removes the texture identified by \a texture_id from the texture cache.
549
550 Equivalent to calling QGLContext::deleteTexture().
551 */
552void QGLPixelBuffer::deleteTexture(GLuint texture_id)
553{
554 Q_D(QGLPixelBuffer);
555 d->qctx->deleteTexture(texture_id);
556}
557
558#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
559/*! \internal */
560void QGLPixelBuffer::deleteTexture(QMacCompatGLuint texture_id)
561{
562 Q_D(QGLPixelBuffer);
563 d->qctx->deleteTexture(texture_id);
564}
565#endif
566
567/*!
568 \since 4.4
569
570 Draws the given texture, \a textureId, to the given target rectangle,
571 \a target, in OpenGL model space. The \a textureTarget should be a 2D
572 texture target.
573
574 Equivalent to the corresponding QGLContext::drawTexture().
575*/
576void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
577{
578 Q_D(QGLPixelBuffer);
579 d->qctx->drawTexture(target, textureId, textureTarget);
580}
581
582#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
583/*! \internal */
584void QGLPixelBuffer::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
585{
586 Q_D(QGLPixelBuffer);
587 d->qctx->drawTexture(target, textureId, textureTarget);
588}
589#endif
590
591/*!
592 \since 4.4
593
594 Draws the given texture, \a textureId, at the given \a point in OpenGL model
595 space. The textureTarget parameter should be a 2D texture target.
596
597 Equivalent to the corresponding QGLContext::drawTexture().
598*/
599void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
600{
601 Q_D(QGLPixelBuffer);
602 d->qctx->drawTexture(point, textureId, textureTarget);
603}
604
605#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
606/*! \internal */
607void QGLPixelBuffer::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
608{
609 Q_D(QGLPixelBuffer);
610 d->qctx->drawTexture(point, textureId, textureTarget);
611}
612#endif
613
614/*!
615 Returns the format of the pbuffer. The format may be different
616 from the one that was requested.
617*/
618QGLFormat QGLPixelBuffer::format() const
619{
620 Q_D(const QGLPixelBuffer);
621 return d->format;
622}
623
624/*! \fn int QGLPixelBuffer::devType() const
625 \internal
626*/
627
628QT_END_NAMESPACE
629