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#include "qglframebufferobject.h"
43#include "qglframebufferobject_p.h"
44
45#include <qdebug.h>
46#include <private/qgl_p.h>
47#include <private/qfont_p.h>
48#if !defined(QT_OPENGL_ES_1)
49#include <private/qpaintengineex_opengl2_p.h>
50#endif
51
52#ifndef QT_OPENGL_ES_2
53#include <private/qpaintengine_opengl_p.h>
54#endif
55
56#include <qglframebufferobject.h>
57#include <qlibrary.h>
58#include <qimage.h>
59
60QT_BEGIN_NAMESPACE
61
62extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool);
63
64#define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context();
65#define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context();
66
67#ifndef QT_NO_DEBUG
68#define QT_RESET_GLERROR() \
69{ \
70 while (glGetError() != GL_NO_ERROR) {} \
71}
72#define QT_CHECK_GLERROR() \
73{ \
74 GLenum err = glGetError(); \
75 if (err != GL_NO_ERROR) { \
76 qDebug("[%s line %d] GL Error: %d", \
77 __FILE__, __LINE__, (int)err); \
78 } \
79}
80#else
81#define QT_RESET_GLERROR() {}
82#define QT_CHECK_GLERROR() {}
83#endif
84
85/*!
86 \class QGLFramebufferObjectFormat
87 \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL
88 framebuffer object.
89
90 \since 4.6
91
92 \ingroup painting-3D
93
94 A framebuffer object has several characteristics:
95 \list
96 \i \link setSamples() Number of samples per pixels.\endlink
97 \i \link setAttachment() Depth and/or stencil attachments.\endlink
98 \i \link setTextureTarget() Texture target.\endlink
99 \i \link setInternalTextureFormat() Internal texture format.\endlink
100 \endlist
101
102 Note that the desired attachments or number of samples per pixels might not
103 be supported by the hardware driver. Call QGLFramebufferObject::format()
104 after creating a QGLFramebufferObject to find the exact format that was
105 used to create the frame buffer object.
106
107 \sa QGLFramebufferObject
108*/
109
110/*!
111 \internal
112*/
113void QGLFramebufferObjectFormat::detach()
114{
115 if (d->ref != 1) {
116 QGLFramebufferObjectFormatPrivate *newd
117 = new QGLFramebufferObjectFormatPrivate(d);
118 if (!d->ref.deref())
119 delete d;
120 d = newd;
121 }
122}
123
124/*!
125 Creates a QGLFramebufferObjectFormat object for specifying
126 the format of an OpenGL framebuffer object.
127
128 By default the format specifies a non-multisample framebuffer object with no
129 attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8.
130 On OpenGL/ES systems, the default internal format is \c GL_RGBA.
131
132 \sa samples(), attachment(), internalTextureFormat()
133*/
134
135QGLFramebufferObjectFormat::QGLFramebufferObjectFormat()
136{
137 d = new QGLFramebufferObjectFormatPrivate;
138}
139
140/*!
141 Constructs a copy of \a other.
142*/
143
144QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other)
145{
146 d = other.d;
147 d->ref.ref();
148}
149
150/*!
151 Assigns \a other to this object.
152*/
153
154QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other)
155{
156 if (d != other.d) {
157 other.d->ref.ref();
158 if (!d->ref.deref())
159 delete d;
160 d = other.d;
161 }
162 return *this;
163}
164
165/*!
166 Destroys the QGLFramebufferObjectFormat.
167*/
168QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat()
169{
170 if (!d->ref.deref())
171 delete d;
172}
173
174/*!
175 Sets the number of samples per pixel for a multisample framebuffer object
176 to \a samples. The default sample count of 0 represents a regular
177 non-multisample framebuffer object.
178
179 If the desired amount of samples per pixel is not supported by the hardware
180 then the maximum number of samples per pixel will be used. Note that
181 multisample framebuffer objects can not be bound as textures. Also, the
182 \c{GL_EXT_framebuffer_multisample} extension is required to create a
183 framebuffer with more than one sample per pixel.
184
185 \sa samples()
186*/
187void QGLFramebufferObjectFormat::setSamples(int samples)
188{
189 detach();
190 d->samples = samples;
191}
192
193/*!
194 Returns the number of samples per pixel if a framebuffer object
195 is a multisample framebuffer object. Otherwise, returns 0.
196 The default value is 0.
197
198 \sa setSamples()
199*/
200int QGLFramebufferObjectFormat::samples() const
201{
202 return d->samples;
203}
204
205/*!
206 \since 4.8
207
208 Enables mipmapping if \a enabled is true; otherwise disables it.
209
210 Mipmapping is disabled by default.
211
212 If mipmapping is enabled, additional memory will be allocated for
213 the mipmap levels. The mipmap levels can be updated by binding the
214 texture and calling glGenerateMipmap(). Mipmapping cannot be enabled
215 for multisampled framebuffer objects.
216
217 \sa mipmap(), QGLFramebufferObject::texture()
218*/
219void QGLFramebufferObjectFormat::setMipmap(bool enabled)
220{
221 detach();
222 d->mipmap = enabled;
223}
224
225/*!
226 \since 4.8
227
228 Returns true if mipmapping is enabled.
229
230 \sa setMipmap()
231*/
232bool QGLFramebufferObjectFormat::mipmap() const
233{
234 return d->mipmap;
235}
236
237/*!
238 Sets the attachment configuration of a framebuffer object to \a attachment.
239
240 \sa attachment()
241*/
242void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment)
243{
244 detach();
245 d->attachment = attachment;
246}
247
248/*!
249 Returns the configuration of the depth and stencil buffers attached to
250 a framebuffer object. The default is QGLFramebufferObject::NoAttachment.
251
252 \sa setAttachment()
253*/
254QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const
255{
256 return d->attachment;
257}
258
259/*!
260 Sets the texture target of the texture attached to a framebuffer object to
261 \a target. Ignored for multisample framebuffer objects.
262
263 \sa textureTarget(), samples()
264*/
265void QGLFramebufferObjectFormat::setTextureTarget(GLenum target)
266{
267 detach();
268 d->target = target;
269}
270
271/*!
272 Returns the texture target of the texture attached to a framebuffer object.
273 Ignored for multisample framebuffer objects. The default is
274 \c GL_TEXTURE_2D.
275
276 \sa setTextureTarget(), samples()
277*/
278GLenum QGLFramebufferObjectFormat::textureTarget() const
279{
280 return d->target;
281}
282
283/*!
284 Sets the internal format of a framebuffer object's texture or
285 multisample framebuffer object's color buffer to
286 \a internalTextureFormat.
287
288 \sa internalTextureFormat()
289*/
290void QGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat)
291{
292 detach();
293 d->internal_format = internalTextureFormat;
294}
295
296/*!
297 Returns the internal format of a framebuffer object's texture or
298 multisample framebuffer object's color buffer. The default is
299 \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on
300 OpenGL/ES systems.
301
302 \sa setInternalTextureFormat()
303*/
304GLenum QGLFramebufferObjectFormat::internalTextureFormat() const
305{
306 return d->internal_format;
307}
308
309#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
310/*! \internal */
311void QGLFramebufferObjectFormat::setTextureTarget(QMacCompatGLenum target)
312{
313 detach();
314 d->target = target;
315}
316
317/*! \internal */
318void QGLFramebufferObjectFormat::setInternalTextureFormat(QMacCompatGLenum internalTextureFormat)
319{
320 detach();
321 d->internal_format = internalTextureFormat;
322}
323#endif
324
325/*!
326 Returns true if all the options of this framebuffer object format
327 are the same as \a other; otherwise returns false.
328*/
329bool QGLFramebufferObjectFormat::operator==(const QGLFramebufferObjectFormat& other) const
330{
331 if (d == other.d)
332 return true;
333 else
334 return d->equals(other.d);
335}
336
337/*!
338 Returns false if all the options of this framebuffer object format
339 are the same as \a other; otherwise returns true.
340*/
341bool QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const
342{
343 return !(*this == other);
344}
345
346void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f,
347 QGLFramebufferObject::Attachment attachment)
348{
349 fbo = f;
350 m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed
351
352 // The context that the fbo was created in may not have depth
353 // and stencil buffers, but the fbo itself might.
354 fboFormat = QGLContext::currentContext()->format();
355 if (attachment == QGLFramebufferObject::CombinedDepthStencil) {
356 fboFormat.setDepth(true);
357 fboFormat.setStencil(true);
358 } else if (attachment == QGLFramebufferObject::Depth) {
359 fboFormat.setDepth(true);
360 fboFormat.setStencil(false);
361 } else {
362 fboFormat.setDepth(false);
363 fboFormat.setStencil(false);
364 }
365
366 GLenum format = f->format().internalTextureFormat();
367 reqAlpha = (format != GL_RGB
368#ifndef QT_OPENGL_ES
369 && format != GL_RGB5 && format != GL_RGB8
370#endif
371 );
372}
373
374QGLContext *QGLFBOGLPaintDevice::context() const
375{
376 QGLContext *fboContext = const_cast<QGLContext *>(fbo->d_ptr->fbo_guard.context());
377 QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
378
379 if (QGLContextPrivate::contextGroup(fboContext) == QGLContextPrivate::contextGroup(currentContext))
380 return currentContext;
381 else
382 return fboContext;
383}
384
385bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const
386{
387 QGL_FUNCP_CONTEXT;
388 if (!ctx)
389 return false; // Context no longer exists.
390 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
391 switch(status) {
392 case GL_NO_ERROR:
393 case GL_FRAMEBUFFER_COMPLETE_EXT:
394 return true;
395 break;
396 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
397 qDebug("QGLFramebufferObject: Unsupported framebuffer format.");
398 break;
399 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
400 qDebug("QGLFramebufferObject: Framebuffer incomplete attachment.");
401 break;
402 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
403 qDebug("QGLFramebufferObject: Framebuffer incomplete, missing attachment.");
404 break;
405#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
406 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
407 qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment.");
408 break;
409#endif
410 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
411 qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions.");
412 break;
413 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
414 qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format.");
415 break;
416 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
417 qDebug("QGLFramebufferObject: Framebuffer incomplete, missing draw buffer.");
418 break;
419 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
420 qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer.");
421 break;
422 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
423 qDebug("QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel.");
424 break;
425 default:
426 qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status;
427 break;
428 }
429 return false;
430}
431
432void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
433 QGLFramebufferObject::Attachment attachment,
434 GLenum texture_target, GLenum internal_format,
435 GLint samples, bool mipmap)
436{
437 QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
438 fbo_guard.setContext(ctx);
439
440 bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
441 if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
442 return;
443
444 size = sz;
445 target = texture_target;
446 // texture dimensions
447
448 QT_RESET_GLERROR(); // reset error state
449 GLuint fbo = 0;
450 glGenFramebuffers(1, &fbo);
451 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
452 fbo_guard.setId(fbo);
453
454 glDevice.setFBO(q, attachment);
455
456 QT_CHECK_GLERROR();
457 // init texture
458 if (samples == 0) {
459 glGenTextures(1, &texture);
460 glBindTexture(target, texture);
461 glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0,
462 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
463 if (mipmap)
464 glGenerateMipmap(GL_TEXTURE_2D);
465#ifndef QT_OPENGL_ES
466 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
467 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
468 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
469 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
470#else
471 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
472 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
473 glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
474 glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
475#endif
476 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
477 target, texture, 0);
478
479 QT_CHECK_GLERROR();
480 valid = checkFramebufferStatus();
481 glBindTexture(target, 0);
482
483 color_buffer = 0;
484 } else {
485 mipmap = false;
486 GLint maxSamples;
487 glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);
488
489 samples = qBound(0, int(samples), int(maxSamples));
490
491 glGenRenderbuffers(1, &color_buffer);
492 glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer);
493 if (glRenderbufferStorageMultisampleEXT && samples > 0) {
494 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
495 internal_format, size.width(), size.height());
496 } else {
497 samples = 0;
498 glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format,
499 size.width(), size.height());
500 }
501
502 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
503 GL_RENDERBUFFER_EXT, color_buffer);
504
505 QT_CHECK_GLERROR();
506 valid = checkFramebufferStatus();
507
508 if (valid)
509 glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &samples);
510 }
511
512 // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
513 // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
514 // might not be supported while separate buffers are, according to QTBUG-12861.
515
516 if (attachment == QGLFramebufferObject::CombinedDepthStencil
517 && (QGLExtensions::glExtensions() & QGLExtensions::PackedDepthStencil)) {
518 // depth and stencil buffer needs another extension
519 glGenRenderbuffers(1, &depth_buffer);
520 Q_ASSERT(!glIsRenderbuffer(depth_buffer));
521 glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
522 Q_ASSERT(glIsRenderbuffer(depth_buffer));
523 if (samples != 0 && glRenderbufferStorageMultisampleEXT)
524 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
525 GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
526 else
527 glRenderbufferStorage(GL_RENDERBUFFER_EXT,
528 GL_DEPTH24_STENCIL8_EXT, size.width(), size.height());
529
530 stencil_buffer = depth_buffer;
531 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
532 GL_RENDERBUFFER_EXT, depth_buffer);
533 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
534 GL_RENDERBUFFER_EXT, stencil_buffer);
535
536 valid = checkFramebufferStatus();
537 if (!valid) {
538 glDeleteRenderbuffers(1, &depth_buffer);
539 stencil_buffer = depth_buffer = 0;
540 }
541 }
542
543 if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil
544 || (attachment == QGLFramebufferObject::Depth)))
545 {
546 glGenRenderbuffers(1, &depth_buffer);
547 Q_ASSERT(!glIsRenderbuffer(depth_buffer));
548 glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer);
549 Q_ASSERT(glIsRenderbuffer(depth_buffer));
550 if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
551#ifdef QT_OPENGL_ES
552 if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
553 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
554 GL_DEPTH_COMPONENT24_OES, size.width(), size.height());
555 } else {
556 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
557 GL_DEPTH_COMPONENT16, size.width(), size.height());
558 }
559#else
560 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
561 GL_DEPTH_COMPONENT, size.width(), size.height());
562#endif
563 } else {
564#ifdef QT_OPENGL_ES
565 if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) {
566 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_OES,
567 size.width(), size.height());
568 } else {
569 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16,
570 size.width(), size.height());
571 }
572#else
573 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height());
574#endif
575 }
576 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
577 GL_RENDERBUFFER_EXT, depth_buffer);
578 valid = checkFramebufferStatus();
579 if (!valid) {
580 glDeleteRenderbuffers(1, &depth_buffer);
581 depth_buffer = 0;
582 }
583 }
584
585 if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) {
586 glGenRenderbuffers(1, &stencil_buffer);
587 Q_ASSERT(!glIsRenderbuffer(stencil_buffer));
588 glBindRenderbuffer(GL_RENDERBUFFER_EXT, stencil_buffer);
589 Q_ASSERT(glIsRenderbuffer(stencil_buffer));
590 if (samples != 0 && glRenderbufferStorageMultisampleEXT) {
591#ifdef QT_OPENGL_ES
592 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
593 GL_STENCIL_INDEX8_EXT, size.width(), size.height());
594#else
595 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples,
596 GL_STENCIL_INDEX, size.width(), size.height());
597#endif
598 } else {
599#ifdef QT_OPENGL_ES
600 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT,
601 size.width(), size.height());
602#else
603 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
604 size.width(), size.height());
605#endif
606 }
607 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
608 GL_RENDERBUFFER_EXT, stencil_buffer);
609 valid = checkFramebufferStatus();
610 if (!valid) {
611 glDeleteRenderbuffers(1, &stencil_buffer);
612 stencil_buffer = 0;
613 }
614 }
615
616 // The FBO might have become valid after removing the depth or stencil buffer.
617 valid = checkFramebufferStatus();
618
619 if (depth_buffer && stencil_buffer) {
620 fbo_attachment = QGLFramebufferObject::CombinedDepthStencil;
621 } else if (depth_buffer) {
622 fbo_attachment = QGLFramebufferObject::Depth;
623 } else {
624 fbo_attachment = QGLFramebufferObject::NoAttachment;
625 }
626
627 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
628 if (!valid) {
629 if (color_buffer)
630 glDeleteRenderbuffers(1, &color_buffer);
631 else
632 glDeleteTextures(1, &texture);
633 if (depth_buffer)
634 glDeleteRenderbuffers(1, &depth_buffer);
635 if (stencil_buffer && depth_buffer != stencil_buffer)
636 glDeleteRenderbuffers(1, &stencil_buffer);
637 glDeleteFramebuffers(1, &fbo);
638 fbo_guard.setId(0);
639 }
640 QT_CHECK_GLERROR();
641
642 format.setTextureTarget(target);
643 format.setSamples(int(samples));
644 format.setAttachment(fbo_attachment);
645 format.setInternalTextureFormat(internal_format);
646 format.setMipmap(mipmap);
647}
648
649/*!
650 \class QGLFramebufferObject
651 \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object.
652 \since 4.2
653
654 \ingroup painting-3D
655
656 The QGLFramebufferObject class encapsulates an OpenGL framebuffer
657 object, defined by the \c{GL_EXT_framebuffer_object} extension. In
658 addition it provides a rendering surface that can be painted on
659 with a QPainter, rendered to using native GL calls, or both. This
660 surface can be bound and used as a regular texture in your own GL
661 drawing code. By default, the QGLFramebufferObject class
662 generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target),
663 which is used as the internal rendering target.
664
665 \bold{It is important to have a current GL context when creating a
666 QGLFramebufferObject, otherwise initialization will fail.}
667
668 OpenGL framebuffer objects and pbuffers (see
669 \l{QGLPixelBuffer}{QGLPixelBuffer}) can both be used to render to
670 offscreen surfaces, but there are a number of advantages with
671 using framebuffer objects instead of pbuffers:
672
673 \list 1
674 \o A framebuffer object does not require a separate rendering
675 context, so no context switching will occur when switching
676 rendering targets. There is an overhead involved in switching
677 targets, but in general it is cheaper than a context switch to a
678 pbuffer.
679
680 \o Rendering to dynamic textures (i.e. render-to-texture
681 functionality) works on all platforms. No need to do explicit copy
682 calls from a render buffer into a texture, as was necessary on
683 systems that did not support the \c{render_texture} extension.
684
685 \o It is possible to attach several rendering buffers (or texture
686 objects) to the same framebuffer object, and render to all of them
687 without doing a context switch.
688
689 \o The OpenGL framebuffer extension is a pure GL extension with no
690 system dependant WGL, CGL, or GLX parts. This makes using
691 framebuffer objects more portable.
692 \endlist
693
694 When using a QPainter to paint to a QGLFramebufferObject you should take
695 care that the QGLFramebufferObject is created with the CombinedDepthStencil
696 attachment for QPainter to be able to render correctly.
697 Note that you need to create a QGLFramebufferObject with more than one
698 sample per pixel for primitives to be antialiased when drawing using a
699 QPainter. To create a multisample framebuffer object you should use one of
700 the constructors that take a QGLFramebufferObject parameter, and set the
701 QGLFramebufferObject::samples() property to a non-zero value.
702
703 When painting to a QGLFramebufferObject using QPainter, the state of
704 the current GL context will be altered by the paint engine to reflect
705 its needs. Applications should not rely upon the GL state being reset
706 to its original conditions, particularly the current shader program,
707 GL viewport, texture units, and drawing modes.
708
709 For multisample framebuffer objects a color render buffer is created,
710 otherwise a texture with the specified texture target is created.
711 The color render buffer or texture will have the specified internal
712 format, and will be bound to the \c GL_COLOR_ATTACHMENT0
713 attachment in the framebuffer object.
714
715 If you want to use a framebuffer object with multisampling enabled
716 as a texture, you first need to copy from it to a regular framebuffer
717 object using QGLContext::blitFramebuffer().
718
719 \section1 Threading
720
721 As of Qt 4.8, it's possible to draw into a QGLFramebufferObject
722 using a QPainter in a separate thread. Note that OpenGL 2.0 or
723 OpenGL ES 2.0 is required for this to work. Also, under X11, it's
724 necessary to set the Qt::AA_X11InitThreads application attribute.
725
726 \sa {Framebuffer Object Example}
727*/
728
729
730/*!
731 \enum QGLFramebufferObject::Attachment
732 \since 4.3
733
734 This enum type is used to configure the depth and stencil buffers
735 attached to the framebuffer object when it is created.
736
737 \value NoAttachment No attachment is added to the framebuffer object. Note that the
738 OpenGL depth and stencil tests won't work when rendering to a
739 framebuffer object without any depth or stencil buffers.
740 This is the default value.
741
742 \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present,
743 a combined depth and stencil buffer is attached.
744 If the extension is not present, only a depth buffer is attached.
745
746 \value Depth A depth buffer is attached to the framebuffer object.
747
748 \sa attachment()
749*/
750
751
752/*! \fn QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target)
753
754 Constructs an OpenGL framebuffer object and binds a 2D GL texture
755 to the buffer of the size \a size. The texture is bound to the
756 \c GL_COLOR_ATTACHMENT0 target in the framebuffer object.
757
758 The \a target parameter is used to specify the GL texture
759 target. The default target is \c GL_TEXTURE_2D. Keep in mind that
760 \c GL_TEXTURE_2D textures must have a power of 2 width and height
761 (e.g. 256x512), unless you are using OpenGL 2.0 or higher.
762
763 By default, no depth and stencil buffers are attached. This behavior
764 can be toggled using one of the overloaded constructors.
765
766 The default internal texture format is \c GL_RGBA8 for desktop
767 OpenGL, and \c GL_RGBA for OpenGL/ES.
768
769 It is important that you have a current GL context set when
770 creating the QGLFramebufferObject, otherwise the initialization
771 will fail.
772
773 \sa size(), texture(), attachment()
774*/
775
776QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target)
777 : d_ptr(new QGLFramebufferObjectPrivate)
778{
779 Q_D(QGLFramebufferObject);
780 d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
781}
782
783#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
784/*! \internal */
785QGLFramebufferObject::QGLFramebufferObject(const QSize &size, QMacCompatGLenum target)
786 : d_ptr(new QGLFramebufferObjectPrivate)
787{
788 Q_D(QGLFramebufferObject);
789 d->init(this, size, NoAttachment, target, DEFAULT_FORMAT);
790}
791#endif
792
793/*! \overload
794
795 Constructs an OpenGL framebuffer object and binds a 2D GL texture
796 to the buffer of the given \a width and \a height.
797
798 \sa size(), texture()
799*/
800QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target)
801 : d_ptr(new QGLFramebufferObjectPrivate)
802{
803 Q_D(QGLFramebufferObject);
804 d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
805}
806
807/*! \overload
808
809 Constructs an OpenGL framebuffer object of the given \a size based on the
810 supplied \a format.
811*/
812
813QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format)
814 : d_ptr(new QGLFramebufferObjectPrivate)
815{
816 Q_D(QGLFramebufferObject);
817 d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(),
818 format.samples(), format.mipmap());
819}
820
821/*! \overload
822
823 Constructs an OpenGL framebuffer object of the given \a width and \a height
824 based on the supplied \a format.
825*/
826
827QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format)
828 : d_ptr(new QGLFramebufferObjectPrivate)
829{
830 Q_D(QGLFramebufferObject);
831 d->init(this, QSize(width, height), format.attachment(), format.textureTarget(),
832 format.internalTextureFormat(), format.samples(), format.mipmap());
833}
834
835#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
836/*! \internal */
837QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target)
838 : d_ptr(new QGLFramebufferObjectPrivate)
839{
840 Q_D(QGLFramebufferObject);
841 d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT);
842}
843#endif
844
845/*! \overload
846
847 Constructs an OpenGL framebuffer object and binds a texture to the
848 buffer of the given \a width and \a height.
849
850 The \a attachment parameter describes the depth/stencil buffer
851 configuration, \a target the texture target and \a internal_format
852 the internal texture format. The default texture target is \c
853 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
854 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
855
856 \sa size(), texture(), attachment()
857*/
858QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
859 GLenum target, GLenum internal_format)
860 : d_ptr(new QGLFramebufferObjectPrivate)
861{
862 Q_D(QGLFramebufferObject);
863 d->init(this, QSize(width, height), attachment, target, internal_format);
864}
865
866#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
867/*! \internal */
868QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment,
869 QMacCompatGLenum target, QMacCompatGLenum internal_format)
870 : d_ptr(new QGLFramebufferObjectPrivate)
871{
872 Q_D(QGLFramebufferObject);
873 d->init(this, QSize(width, height), attachment, target, internal_format);
874}
875#endif
876
877/*! \overload
878
879 Constructs an OpenGL framebuffer object and binds a texture to the
880 buffer of the given \a size.
881
882 The \a attachment parameter describes the depth/stencil buffer
883 configuration, \a target the texture target and \a internal_format
884 the internal texture format. The default texture target is \c
885 GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8
886 for desktop OpenGL and \c GL_RGBA for OpenGL/ES.
887
888 \sa size(), texture(), attachment()
889*/
890QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
891 GLenum target, GLenum internal_format)
892 : d_ptr(new QGLFramebufferObjectPrivate)
893{
894 Q_D(QGLFramebufferObject);
895 d->init(this, size, attachment, target, internal_format);
896}
897
898#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
899/*! \internal */
900QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment,
901 QMacCompatGLenum target, QMacCompatGLenum internal_format)
902 : d_ptr(new QGLFramebufferObjectPrivate)
903{
904 Q_D(QGLFramebufferObject);
905 d->init(this, size, attachment, target, internal_format);
906}
907#endif
908
909/*!
910 \fn QGLFramebufferObject::~QGLFramebufferObject()
911
912 Destroys the framebuffer object and frees any allocated resources.
913*/
914QGLFramebufferObject::~QGLFramebufferObject()
915{
916 Q_D(QGLFramebufferObject);
917 QGL_FUNC_CONTEXT;
918
919 delete d->engine;
920
921 if (isValid() && ctx) {
922 QGLShareContextScope scope(ctx);
923 if (d->texture)
924 glDeleteTextures(1, &d->texture);
925 if (d->color_buffer)
926 glDeleteRenderbuffers(1, &d->color_buffer);
927 if (d->depth_buffer)
928 glDeleteRenderbuffers(1, &d->depth_buffer);
929 if (d->stencil_buffer && d->stencil_buffer != d->depth_buffer)
930 glDeleteRenderbuffers(1, &d->stencil_buffer);
931 GLuint fbo = d->fbo();
932 glDeleteFramebuffers(1, &fbo);
933 }
934}
935
936/*!
937 \fn bool QGLFramebufferObject::isValid() const
938
939 Returns true if the framebuffer object is valid.
940
941 The framebuffer can become invalid if the initialization process
942 fails, the user attaches an invalid buffer to the framebuffer
943 object, or a non-power of two width/height is specified as the
944 texture size if the texture target is \c{GL_TEXTURE_2D}.
945 The non-power of two limitation does not apply if the OpenGL version
946 is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension
947 is present.
948
949 The framebuffer can also become invalid if the QGLContext that
950 the framebuffer was created within is destroyed and there are
951 no other shared contexts that can take over ownership of the
952 framebuffer.
953*/
954bool QGLFramebufferObject::isValid() const
955{
956 Q_D(const QGLFramebufferObject);
957 return d->valid && d->fbo_guard.context();
958}
959
960/*!
961 \fn bool QGLFramebufferObject::bind()
962
963 Switches rendering from the default, windowing system provided
964 framebuffer to this framebuffer object.
965 Returns true upon success, false otherwise.
966
967 \sa release()
968*/
969bool QGLFramebufferObject::bind()
970{
971 if (!isValid())
972 return false;
973 Q_D(QGLFramebufferObject);
974 QGL_FUNC_CONTEXT;
975 if (!ctx)
976 return false; // Context no longer exists.
977 const QGLContext *current = QGLContext::currentContext();
978#ifdef QT_DEBUG
979 if (!current ||
980 QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx))
981 {
982 qWarning("QGLFramebufferObject::bind() called from incompatible context");
983 }
984#endif
985 glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo());
986 d->valid = d->checkFramebufferStatus();
987 if (d->valid && current)
988 current->d_ptr->current_fbo = d->fbo();
989 return d->valid;
990}
991
992/*!
993 \fn bool QGLFramebufferObject::release()
994
995 Switches rendering back to the default, windowing system provided
996 framebuffer.
997 Returns true upon success, false otherwise.
998
999 \sa bind()
1000*/
1001bool QGLFramebufferObject::release()
1002{
1003 if (!isValid())
1004 return false;
1005 QGL_FUNC_CONTEXT;
1006 if (!ctx)
1007 return false; // Context no longer exists.
1008
1009 const QGLContext *current = QGLContext::currentContext();
1010
1011#ifdef QT_DEBUG
1012 if (!current ||
1013 QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx))
1014 {
1015 qWarning("QGLFramebufferObject::release() called from incompatible context");
1016 }
1017#endif
1018
1019 if (current) {
1020 current->d_ptr->current_fbo = current->d_ptr->default_fbo;
1021 glBindFramebuffer(GL_FRAMEBUFFER_EXT, current->d_ptr->default_fbo);
1022 }
1023
1024 return true;
1025}
1026
1027/*!
1028 \fn GLuint QGLFramebufferObject::texture() const
1029
1030 Returns the texture id for the texture attached as the default
1031 rendering target in this framebuffer object. This texture id can
1032 be bound as a normal texture in your own GL code.
1033
1034 If a multisample framebuffer object is used then the value returned
1035 from this function will be invalid.
1036*/
1037GLuint QGLFramebufferObject::texture() const
1038{
1039 Q_D(const QGLFramebufferObject);
1040 return d->texture;
1041}
1042
1043/*!
1044 \fn QSize QGLFramebufferObject::size() const
1045
1046 Returns the size of the texture attached to this framebuffer
1047 object.
1048*/
1049QSize QGLFramebufferObject::size() const
1050{
1051 Q_D(const QGLFramebufferObject);
1052 return d->size;
1053}
1054
1055/*!
1056 Returns the format of this framebuffer object.
1057*/
1058QGLFramebufferObjectFormat QGLFramebufferObject::format() const
1059{
1060 Q_D(const QGLFramebufferObject);
1061 return d->format;
1062}
1063
1064/*!
1065 \fn QImage QGLFramebufferObject::toImage() const
1066
1067 Returns the contents of this framebuffer object as a QImage.
1068*/
1069QImage QGLFramebufferObject::toImage() const
1070{
1071 Q_D(const QGLFramebufferObject);
1072 if (!d->valid)
1073 return QImage();
1074
1075 // qt_gl_read_framebuffer doesn't work on a multisample FBO
1076 if (format().samples() != 0) {
1077 QGLFramebufferObject temp(size(), QGLFramebufferObjectFormat());
1078
1079 QRect rect(QPoint(0, 0), size());
1080 blitFramebuffer(&temp, rect, const_cast<QGLFramebufferObject *>(this), rect);
1081
1082 return temp.toImage();
1083 }
1084
1085 bool wasBound = isBound();
1086 if (!wasBound)
1087 const_cast<QGLFramebufferObject *>(this)->bind();
1088 QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true);
1089 if (!wasBound)
1090 const_cast<QGLFramebufferObject *>(this)->release();
1091
1092 return image;
1093}
1094
1095#if !defined(QT_OPENGL_ES_1)
1096Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
1097#endif
1098
1099#ifndef QT_OPENGL_ES_2
1100Q_GLOBAL_STATIC(QGLEngineThreadStorage<QOpenGLPaintEngine>, qt_buffer_engine)
1101#endif
1102
1103/*! \reimp */
1104QPaintEngine *QGLFramebufferObject::paintEngine() const
1105{
1106 Q_D(const QGLFramebufferObject);
1107 if (d->engine)
1108 return d->engine;
1109
1110#if !defined(QT_OPENGL_ES_1)
1111#if !defined (QT_OPENGL_ES_2)
1112 if (qt_gl_preferGL2Engine()) {
1113#endif
1114 QPaintEngine *engine = qt_buffer_2_engine()->engine();
1115 if (engine->isActive() && engine->paintDevice() != this) {
1116 d->engine = new QGL2PaintEngineEx;
1117 return d->engine;
1118 }
1119 return engine;
1120#if !defined (QT_OPENGL_ES_2)
1121 }
1122#endif
1123#endif
1124
1125#if !defined(QT_OPENGL_ES_2)
1126 QPaintEngine *engine = qt_buffer_engine()->engine();
1127 if (engine->isActive() && engine->paintDevice() != this) {
1128 d->engine = new QOpenGLPaintEngine;
1129 return d->engine;
1130 }
1131 return engine;
1132#endif
1133}
1134
1135/*!
1136 \fn bool QGLFramebufferObject::bindDefault()
1137 \internal
1138
1139 Switches rendering back to the default, windowing system provided
1140 framebuffer.
1141 Returns true upon success, false otherwise.
1142
1143 \sa bind(), release()
1144*/
1145bool QGLFramebufferObject::bindDefault()
1146{
1147 QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
1148
1149 if (ctx) {
1150 bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
1151 if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx)))
1152 return false;
1153
1154 ctx->d_ptr->current_fbo = ctx->d_ptr->default_fbo;
1155 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->default_fbo);
1156#ifdef QT_DEBUG
1157 } else {
1158 qWarning("QGLFramebufferObject::bindDefault() called without current context.");
1159#endif
1160 }
1161
1162 return ctx != 0;
1163}
1164
1165/*!
1166 \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
1167
1168 Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension
1169 is present on this system; otherwise returns false.
1170*/
1171bool QGLFramebufferObject::hasOpenGLFramebufferObjects()
1172{
1173 return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject);
1174}
1175
1176/*!
1177 \since 4.4
1178
1179 Draws the given texture, \a textureId, to the given target rectangle,
1180 \a target, in OpenGL model space. The \a textureTarget should be a 2D
1181 texture target.
1182
1183 The framebuffer object should be bound when calling this function.
1184
1185 Equivalent to the corresponding QGLContext::drawTexture().
1186*/
1187void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
1188{
1189 const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget);
1190}
1191
1192#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
1193/*! \internal */
1194void QGLFramebufferObject::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
1195{
1196 const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget);
1197}
1198#endif
1199
1200/*!
1201 \since 4.4
1202
1203 Draws the given texture, \a textureId, at the given \a point in OpenGL
1204 model space. The \a textureTarget should be a 2D texture target.
1205
1206 The framebuffer object should be bound when calling this function.
1207
1208 Equivalent to the corresponding QGLContext::drawTexture().
1209*/
1210void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
1211{
1212 const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget);
1213}
1214
1215#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
1216/*! \internal */
1217void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget)
1218{
1219 const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget);
1220}
1221#endif
1222
1223/*! \reimp */
1224int QGLFramebufferObject::metric(PaintDeviceMetric metric) const
1225{
1226 Q_D(const QGLFramebufferObject);
1227
1228 float dpmx = qt_defaultDpiX()*100./2.54;
1229 float dpmy = qt_defaultDpiY()*100./2.54;
1230 int w = d->size.width();
1231 int h = d->size.height();
1232 switch (metric) {
1233 case PdmWidth:
1234 return w;
1235
1236 case PdmHeight:
1237 return h;
1238
1239 case PdmWidthMM:
1240 return qRound(w * 1000 / dpmx);
1241
1242 case PdmHeightMM:
1243 return qRound(h * 1000 / dpmy);
1244
1245 case PdmNumColors:
1246 return 0;
1247
1248 case PdmDepth:
1249 return 32;//d->depth;
1250
1251 case PdmDpiX:
1252 return qRound(dpmx * 0.0254);
1253
1254 case PdmDpiY:
1255 return qRound(dpmy * 0.0254);
1256
1257 case PdmPhysicalDpiX:
1258 return qRound(dpmx * 0.0254);
1259
1260 case PdmPhysicalDpiY:
1261 return qRound(dpmy * 0.0254);
1262
1263 default:
1264 qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric);
1265 break;
1266 }
1267 return 0;
1268}
1269
1270/*!
1271 \fn GLuint QGLFramebufferObject::handle() const
1272
1273 Returns the GL framebuffer object handle for this framebuffer
1274 object (returned by the \c{glGenFrameBuffersEXT()} function). This
1275 handle can be used to attach new images or buffers to the
1276 framebuffer. The user is responsible for cleaning up and
1277 destroying these objects.
1278*/
1279GLuint QGLFramebufferObject::handle() const
1280{
1281 Q_D(const QGLFramebufferObject);
1282 return d->fbo();
1283}
1284
1285/*! \fn int QGLFramebufferObject::devType() const
1286 \internal
1287*/
1288
1289
1290/*!
1291 Returns the status of the depth and stencil buffers attached to
1292 this framebuffer object.
1293*/
1294
1295QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const
1296{
1297 Q_D(const QGLFramebufferObject);
1298 if (d->valid)
1299 return d->fbo_attachment;
1300 return NoAttachment;
1301}
1302
1303/*!
1304 \since 4.5
1305
1306 Returns true if the framebuffer object is currently bound to a context,
1307 otherwise false is returned.
1308*/
1309
1310bool QGLFramebufferObject::isBound() const
1311{
1312 Q_D(const QGLFramebufferObject);
1313 const QGLContext *current = QGLContext::currentContext();
1314 return current ? current->d_ptr->current_fbo == d->fbo() : false;
1315}
1316
1317/*!
1318 \fn bool QGLFramebufferObject::hasOpenGLFramebufferBlit()
1319
1320 \since 4.6
1321
1322 Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension
1323 is present on this system; otherwise returns false.
1324
1325 \sa blitFramebuffer()
1326*/
1327bool QGLFramebufferObject::hasOpenGLFramebufferBlit()
1328{
1329 return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit);
1330}
1331
1332/*!
1333 \since 4.6
1334
1335 Blits from the \a sourceRect rectangle in the \a source framebuffer
1336 object to the \a targetRect rectangle in the \a target framebuffer object.
1337
1338 If \a source or \a target is 0, the default framebuffer will be used
1339 instead of a framebuffer object as source or target respectively.
1340
1341 The \a buffers parameter should be a mask consisting of any combination of
1342 \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and
1343 \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both
1344 in the source and target buffers is ignored.
1345
1346 The \a sourceRect and \a targetRect rectangles may have different sizes;
1347 in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or
1348 \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to
1349 \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest
1350 interpolation should be used when scaling is performed.
1351
1352 If \a source equals \a target a copy is performed within the same buffer.
1353 Results are undefined if the source and target rectangles overlap and
1354 have different sizes. The sizes must also be the same if any of the
1355 framebuffer objects are multisample framebuffers.
1356
1357 Note that the scissor test will restrict the blit area if enabled.
1358
1359 This function will have no effect unless hasOpenGLFramebufferBlit() returns
1360 true.
1361
1362 \sa hasOpenGLFramebufferBlit()
1363*/
1364void QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect,
1365 QGLFramebufferObject *source, const QRect &sourceRect,
1366 GLbitfield buffers,
1367 GLenum filter)
1368{
1369 if (!(QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit))
1370 return;
1371
1372 const QGLContext *ctx = QGLContext::currentContext();
1373 if (!ctx)
1374 return;
1375
1376 const int height = ctx->device()->height();
1377
1378 const int sh = source ? source->height() : height;
1379 const int th = target ? target->height() : height;
1380
1381 const int sx0 = sourceRect.left();
1382 const int sx1 = sourceRect.left() + sourceRect.width();
1383 const int sy0 = sh - (sourceRect.top() + sourceRect.height());
1384 const int sy1 = sh - sourceRect.top();
1385
1386 const int tx0 = targetRect.left();
1387 const int tx1 = targetRect.left() + targetRect.width();
1388 const int ty0 = th - (targetRect.top() + targetRect.height());
1389 const int ty1 = th - targetRect.top();
1390
1391 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, source ? source->handle() : 0);
1392 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, target ? target->handle() : 0);
1393
1394 glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
1395 tx0, ty0, tx1, ty1,
1396 buffers, filter);
1397
1398 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
1399}
1400
1401QT_END_NAMESPACE
1402