| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtOpenGL module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qglframebufferobject.h" |
| #include "qglframebufferobject_p.h" |
| |
| #include <qdebug.h> |
| #include <private/qgl_p.h> |
| #if !defined(QT_OPENGL_ES_1) |
| #include <private/qpaintengineex_opengl2_p.h> |
| #endif |
| |
| #ifndef QT_OPENGL_ES_2 |
| #include <private/qpaintengine_opengl_p.h> |
| #endif |
| |
| #include <qglframebufferobject.h> |
| #include <qlibrary.h> |
| #include <qimage.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); |
| |
| #define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context(); |
| #define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context(); |
| |
| #ifndef QT_NO_DEBUG |
| #define QT_RESET_GLERROR() \ |
| { \ |
| while (glGetError() != GL_NO_ERROR) {} \ |
| } |
| #define QT_CHECK_GLERROR() \ |
| { \ |
| GLenum err = glGetError(); \ |
| if (err != GL_NO_ERROR) { \ |
| qDebug("[%s line %d] GL Error: %d", \ |
| __FILE__, __LINE__, (int)err); \ |
| } \ |
| } |
| #else |
| #define QT_RESET_GLERROR() {} |
| #define QT_CHECK_GLERROR() {} |
| #endif |
| |
| /*! |
| \class QGLFramebufferObjectFormat |
| \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL |
| framebuffer object. |
| |
| \since 4.6 |
| |
| \ingroup painting-3D |
| |
| A framebuffer object has several characteristics: |
| \list |
| \i \link setSamples() Number of samples per pixels.\endlink |
| \i \link setAttachment() Depth and/or stencil attachments.\endlink |
| \i \link setTextureTarget() Texture target.\endlink |
| \i \link setInternalTextureFormat() Internal texture format.\endlink |
| \endlist |
| |
| Note that the desired attachments or number of samples per pixels might not |
| be supported by the hardware driver. Call QGLFramebufferObject::format() |
| after creating a QGLFramebufferObject to find the exact format that was |
| used to create the frame buffer object. |
| |
| \sa QGLFramebufferObject |
| */ |
| |
| /*! |
| \internal |
| */ |
| void QGLFramebufferObjectFormat::detach() |
| { |
| if (d->ref != 1) { |
| QGLFramebufferObjectFormatPrivate *newd |
| = new QGLFramebufferObjectFormatPrivate(d); |
| if (!d->ref.deref()) |
| delete d; |
| d = newd; |
| } |
| } |
| |
| /*! |
| Creates a QGLFramebufferObjectFormat object for specifying |
| the format of an OpenGL framebuffer object. |
| |
| By default the format specifies a non-multisample framebuffer object with no |
| attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8. |
| On OpenGL/ES systems, the default internal format is \c GL_RGBA. |
| |
| \sa samples(), attachment(), internalTextureFormat() |
| */ |
| |
| QGLFramebufferObjectFormat::QGLFramebufferObjectFormat() |
| { |
| d = new QGLFramebufferObjectFormatPrivate; |
| } |
| |
| /*! |
| Constructs a copy of \a other. |
| */ |
| |
| QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other) |
| { |
| d = other.d; |
| d->ref.ref(); |
| } |
| |
| /*! |
| Assigns \a other to this object. |
| */ |
| |
| QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other) |
| { |
| if (d != other.d) { |
| other.d->ref.ref(); |
| if (!d->ref.deref()) |
| delete d; |
| d = other.d; |
| } |
| return *this; |
| } |
| |
| /*! |
| Destroys the QGLFramebufferObjectFormat. |
| */ |
| QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat() |
| { |
| if (!d->ref.deref()) |
| delete d; |
| } |
| |
| /*! |
| Sets the number of samples per pixel for a multisample framebuffer object |
| to \a samples. The default sample count of 0 represents a regular |
| non-multisample framebuffer object. |
| |
| If the desired amount of samples per pixel is not supported by the hardware |
| then the maximum number of samples per pixel will be used. Note that |
| multisample framebuffer objects can not be bound as textures. Also, the |
| \c{GL_EXT_framebuffer_multisample} extension is required to create a |
| framebuffer with more than one sample per pixel. |
| |
| \sa samples() |
| */ |
| void QGLFramebufferObjectFormat::setSamples(int samples) |
| { |
| detach(); |
| d->samples = samples; |
| } |
| |
| /*! |
| Returns the number of samples per pixel if a framebuffer object |
| is a multisample framebuffer object. Otherwise, returns 0. |
| The default value is 0. |
| |
| \sa setSamples() |
| */ |
| int QGLFramebufferObjectFormat::samples() const |
| { |
| return d->samples; |
| } |
| |
| /*! |
| Sets the attachment configuration of a framebuffer object to \a attachment. |
| |
| \sa attachment() |
| */ |
| void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment) |
| { |
| detach(); |
| d->attachment = attachment; |
| } |
| |
| /*! |
| Returns the configuration of the depth and stencil buffers attached to |
| a framebuffer object. The default is QGLFramebufferObject::NoAttachment. |
| |
| \sa setAttachment() |
| */ |
| QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const |
| { |
| return d->attachment; |
| } |
| |
| /*! |
| Sets the texture target of the texture attached to a framebuffer object to |
| \a target. Ignored for multisample framebuffer objects. |
| |
| \sa textureTarget(), samples() |
| */ |
| void QGLFramebufferObjectFormat::setTextureTarget(GLenum target) |
| { |
| detach(); |
| d->target = target; |
| } |
| |
| /*! |
| Returns the texture target of the texture attached to a framebuffer object. |
| Ignored for multisample framebuffer objects. The default is |
| \c GL_TEXTURE_2D. |
| |
| \sa setTextureTarget(), samples() |
| */ |
| GLenum QGLFramebufferObjectFormat::textureTarget() const |
| { |
| return d->target; |
| } |
| |
| /*! |
| Sets the internal format of a framebuffer object's texture or |
| multisample framebuffer object's color buffer to |
| \a internalTextureFormat. |
| |
| \sa internalTextureFormat() |
| */ |
| void QGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat) |
| { |
| detach(); |
| d->internal_format = internalTextureFormat; |
| } |
| |
| /*! |
| Returns the internal format of a framebuffer object's texture or |
| multisample framebuffer object's color buffer. The default is |
| \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on |
| OpenGL/ES systems. |
| |
| \sa setInternalTextureFormat() |
| */ |
| GLenum QGLFramebufferObjectFormat::internalTextureFormat() const |
| { |
| return d->internal_format; |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| void QGLFramebufferObjectFormat::setTextureTarget(QMacCompatGLenum target) |
| { |
| detach(); |
| d->target = target; |
| } |
| |
| /*! \internal */ |
| void QGLFramebufferObjectFormat::setInternalTextureFormat(QMacCompatGLenum internalTextureFormat) |
| { |
| detach(); |
| d->internal_format = internalTextureFormat; |
| } |
| #endif |
| |
| /*! |
| Returns true if all the options of this framebuffer object format |
| are the same as \a other; otherwise returns false. |
| */ |
| bool QGLFramebufferObjectFormat::operator==(const QGLFramebufferObjectFormat& other) const |
| { |
| if (d == other.d) |
| return true; |
| else |
| return d->equals(other.d); |
| } |
| |
| /*! |
| Returns false if all the options of this framebuffer object format |
| are the same as \a other; otherwise returns true. |
| */ |
| bool QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const |
| { |
| return !(*this == other); |
| } |
| |
| void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f, |
| QGLFramebufferObject::Attachment attachment) |
| { |
| fbo = f; |
| m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed |
| |
| // The context that the fbo was created in may not have depth |
| // and stencil buffers, but the fbo itself might. |
| fboFormat = QGLContext::currentContext()->format(); |
| if (attachment == QGLFramebufferObject::CombinedDepthStencil) { |
| fboFormat.setDepth(true); |
| fboFormat.setStencil(true); |
| } else if (attachment == QGLFramebufferObject::Depth) { |
| fboFormat.setDepth(true); |
| } |
| |
| GLenum format = f->format().internalTextureFormat(); |
| reqAlpha = (format != GL_RGB |
| #ifndef QT_OPENGL_ES |
| && format != GL_RGB5 && format != GL_RGB8 |
| #endif |
| ); |
| } |
| |
| QGLContext *QGLFBOGLPaintDevice::context() const |
| { |
| QGLContext *fboContext = const_cast<QGLContext *>(fbo->d_ptr->fbo_guard.context()); |
| QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); |
| |
| if (QGLContextPrivate::contextGroup(fboContext) == QGLContextPrivate::contextGroup(currentContext)) |
| return currentContext; |
| else |
| return fboContext; |
| } |
| |
| bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const |
| { |
| QGL_FUNCP_CONTEXT; |
| if (!ctx) |
| return false; // Context no longer exists. |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); |
| switch(status) { |
| case GL_NO_ERROR: |
| case GL_FRAMEBUFFER_COMPLETE_EXT: |
| return true; |
| break; |
| case GL_FRAMEBUFFER_UNSUPPORTED_EXT: |
| qDebug("QGLFramebufferObject: Unsupported framebuffer format."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete attachment."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, missing attachment."); |
| break; |
| #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT |
| case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment."); |
| break; |
| #endif |
| case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, missing draw buffer."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer."); |
| break; |
| case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: |
| qDebug("QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel."); |
| break; |
| default: |
| qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status; |
| break; |
| } |
| return false; |
| } |
| |
| void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, |
| QGLFramebufferObject::Attachment attachment, |
| GLenum texture_target, GLenum internal_format, GLint samples) |
| { |
| QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
| fbo_guard.setContext(ctx); |
| |
| bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); |
| if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx))) |
| return; |
| |
| size = sz; |
| target = texture_target; |
| // texture dimensions |
| |
| QT_RESET_GLERROR(); // reset error state |
| GLuint fbo = 0; |
| glGenFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); |
| fbo_guard.setId(fbo); |
| |
| glDevice.setFBO(q, attachment); |
| |
| QT_CHECK_GLERROR(); |
| // init texture |
| if (samples == 0) { |
| glGenTextures(1, &texture); |
| glBindTexture(target, texture); |
| glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
| #ifndef QT_OPENGL_ES |
| glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| #else |
| glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| #endif |
| glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
| target, texture, 0); |
| |
| QT_CHECK_GLERROR(); |
| valid = checkFramebufferStatus(); |
| glBindTexture(target, 0); |
| |
| color_buffer = 0; |
| } else { |
| GLint maxSamples; |
| glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); |
| |
| samples = qBound(0, int(samples), int(maxSamples)); |
| |
| glGenRenderbuffers(1, &color_buffer); |
| glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer); |
| if (glRenderbufferStorageMultisampleEXT && samples > 0) { |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| internal_format, size.width(), size.height()); |
| } else { |
| samples = 0; |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format, |
| size.width(), size.height()); |
| } |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
| GL_RENDERBUFFER_EXT, color_buffer); |
| |
| QT_CHECK_GLERROR(); |
| valid = checkFramebufferStatus(); |
| |
| if (valid) |
| glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &samples); |
| } |
| |
| // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a |
| // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer |
| // might not be supported while separate buffers are, according to QTBUG-12861. |
| |
| if (attachment == QGLFramebufferObject::CombinedDepthStencil |
| && (QGLExtensions::glExtensions() & QGLExtensions::PackedDepthStencil)) { |
| // depth and stencil buffer needs another extension |
| glGenRenderbuffers(1, &depth_buffer); |
| Q_ASSERT(!glIsRenderbuffer(depth_buffer)); |
| glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer); |
| Q_ASSERT(glIsRenderbuffer(depth_buffer)); |
| if (samples != 0 && glRenderbufferStorageMultisampleEXT) |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); |
| else |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, |
| GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); |
| |
| stencil_buffer = depth_buffer; |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, |
| GL_RENDERBUFFER_EXT, depth_buffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, |
| GL_RENDERBUFFER_EXT, stencil_buffer); |
| |
| valid = checkFramebufferStatus(); |
| if (!valid) { |
| glDeleteRenderbuffers(1, &depth_buffer); |
| stencil_buffer = depth_buffer = 0; |
| } |
| } |
| |
| if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil |
| || (attachment == QGLFramebufferObject::Depth))) |
| { |
| glGenRenderbuffers(1, &depth_buffer); |
| Q_ASSERT(!glIsRenderbuffer(depth_buffer)); |
| glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_buffer); |
| Q_ASSERT(glIsRenderbuffer(depth_buffer)); |
| if (samples != 0 && glRenderbufferStorageMultisampleEXT) { |
| #ifdef QT_OPENGL_ES |
| if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) { |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_DEPTH_COMPONENT24_OES, size.width(), size.height()); |
| } else { |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_DEPTH_COMPONENT16, size.width(), size.height()); |
| } |
| #else |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_DEPTH_COMPONENT, size.width(), size.height()); |
| #endif |
| } else { |
| #ifdef QT_OPENGL_ES |
| if (QGLExtensions::glExtensions() & QGLExtensions::Depth24) { |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_OES, |
| size.width(), size.height()); |
| } else { |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, |
| size.width(), size.height()); |
| } |
| #else |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height()); |
| #endif |
| } |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, |
| GL_RENDERBUFFER_EXT, depth_buffer); |
| valid = checkFramebufferStatus(); |
| if (!valid) { |
| glDeleteRenderbuffers(1, &depth_buffer); |
| depth_buffer = 0; |
| } |
| } |
| |
| if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) { |
| glGenRenderbuffers(1, &stencil_buffer); |
| Q_ASSERT(!glIsRenderbuffer(stencil_buffer)); |
| glBindRenderbuffer(GL_RENDERBUFFER_EXT, stencil_buffer); |
| Q_ASSERT(glIsRenderbuffer(stencil_buffer)); |
| if (samples != 0 && glRenderbufferStorageMultisampleEXT) { |
| #ifdef QT_OPENGL_ES |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_STENCIL_INDEX8_EXT, size.width(), size.height()); |
| #else |
| glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, |
| GL_STENCIL_INDEX, size.width(), size.height()); |
| #endif |
| } else { |
| #ifdef QT_OPENGL_ES |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, |
| size.width(), size.height()); |
| #else |
| glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, |
| size.width(), size.height()); |
| #endif |
| } |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, |
| GL_RENDERBUFFER_EXT, stencil_buffer); |
| valid = checkFramebufferStatus(); |
| if (!valid) { |
| glDeleteRenderbuffers(1, &stencil_buffer); |
| stencil_buffer = 0; |
| } |
| } |
| |
| // The FBO might have become valid after removing the depth or stencil buffer. |
| valid = checkFramebufferStatus(); |
| |
| if (depth_buffer && stencil_buffer) { |
| fbo_attachment = QGLFramebufferObject::CombinedDepthStencil; |
| } else if (depth_buffer) { |
| fbo_attachment = QGLFramebufferObject::Depth; |
| } else { |
| fbo_attachment = QGLFramebufferObject::NoAttachment; |
| } |
| |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); |
| if (!valid) { |
| if (color_buffer) |
| glDeleteRenderbuffers(1, &color_buffer); |
| else |
| glDeleteTextures(1, &texture); |
| if (depth_buffer) |
| glDeleteRenderbuffers(1, &depth_buffer); |
| if (stencil_buffer && depth_buffer != stencil_buffer) |
| glDeleteRenderbuffers(1, &stencil_buffer); |
| glDeleteFramebuffers(1, &fbo); |
| fbo_guard.setId(0); |
| } |
| QT_CHECK_GLERROR(); |
| |
| format.setTextureTarget(target); |
| format.setSamples(int(samples)); |
| format.setAttachment(fbo_attachment); |
| format.setInternalTextureFormat(internal_format); |
| } |
| |
| /*! |
| \class QGLFramebufferObject |
| \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object. |
| \since 4.2 |
| |
| \ingroup painting-3D |
| |
| The QGLFramebufferObject class encapsulates an OpenGL framebuffer |
| object, defined by the \c{GL_EXT_framebuffer_object} extension. In |
| addition it provides a rendering surface that can be painted on |
| with a QPainter, rendered to using native GL calls, or both. This |
| surface can be bound and used as a regular texture in your own GL |
| drawing code. By default, the QGLFramebufferObject class |
| generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target), |
| which is used as the internal rendering target. |
| |
| \bold{It is important to have a current GL context when creating a |
| QGLFramebufferObject, otherwise initialization will fail.} |
| |
| OpenGL framebuffer objects and pbuffers (see |
| \l{QGLPixelBuffer}{QGLPixelBuffer}) can both be used to render to |
| offscreen surfaces, but there are a number of advantages with |
| using framebuffer objects instead of pbuffers: |
| |
| \list 1 |
| \o A framebuffer object does not require a separate rendering |
| context, so no context switching will occur when switching |
| rendering targets. There is an overhead involved in switching |
| targets, but in general it is cheaper than a context switch to a |
| pbuffer. |
| |
| \o Rendering to dynamic textures (i.e. render-to-texture |
| functionality) works on all platforms. No need to do explicit copy |
| calls from a render buffer into a texture, as was necessary on |
| systems that did not support the \c{render_texture} extension. |
| |
| \o It is possible to attach several rendering buffers (or texture |
| objects) to the same framebuffer object, and render to all of them |
| without doing a context switch. |
| |
| \o The OpenGL framebuffer extension is a pure GL extension with no |
| system dependant WGL, CGL, or GLX parts. This makes using |
| framebuffer objects more portable. |
| \endlist |
| |
| When using a QPainter to paint to a QGLFramebufferObject you should take |
| care that the QGLFramebufferObject is created with the CombinedDepthStencil |
| attachment for QPainter to be able to render correctly. |
| Note that you need to create a QGLFramebufferObject with more than one |
| sample per pixel for primitives to be antialiased when drawing using a |
| QPainter. To create a multisample framebuffer object you should use one of |
| the constructors that take a QGLFramebufferObject parameter, and set the |
| QGLFramebufferObject::samples() property to a non-zero value. |
| |
| When painting to a QGLFramebufferObject using QPainter, the state of |
| the current GL context will be altered by the paint engine to reflect |
| its needs. Applications should not rely upon the GL state being reset |
| to its original conditions, particularly the current shader program, |
| GL viewport, texture units, and drawing modes. |
| |
| For multisample framebuffer objects a color render buffer is created, |
| otherwise a texture with the specified texture target is created. |
| The color render buffer or texture will have the specified internal |
| format, and will be bound to the \c GL_COLOR_ATTACHMENT0 |
| attachment in the framebuffer object. |
| |
| If you want to use a framebuffer object with multisampling enabled |
| as a texture, you first need to copy from it to a regular framebuffer |
| object using QGLContext::blitFramebuffer(). |
| |
| \sa {Framebuffer Object Example} |
| */ |
| |
| |
| /*! |
| \enum QGLFramebufferObject::Attachment |
| \since 4.3 |
| |
| This enum type is used to configure the depth and stencil buffers |
| attached to the framebuffer object when it is created. |
| |
| \value NoAttachment No attachment is added to the framebuffer object. Note that the |
| OpenGL depth and stencil tests won't work when rendering to a |
| framebuffer object without any depth or stencil buffers. |
| This is the default value. |
| |
| \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present, |
| a combined depth and stencil buffer is attached. |
| If the extension is not present, only a depth buffer is attached. |
| |
| \value Depth A depth buffer is attached to the framebuffer object. |
| |
| \sa attachment() |
| */ |
| |
| |
| /*! \fn QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) |
| |
| Constructs an OpenGL framebuffer object and binds a 2D GL texture |
| to the buffer of the size \a size. The texture is bound to the |
| \c GL_COLOR_ATTACHMENT0 target in the framebuffer object. |
| |
| The \a target parameter is used to specify the GL texture |
| target. The default target is \c GL_TEXTURE_2D. Keep in mind that |
| \c GL_TEXTURE_2D textures must have a power of 2 width and height |
| (e.g. 256x512), unless you are using OpenGL 2.0 or higher. |
| |
| By default, no depth and stencil buffers are attached. This behavior |
| can be toggled using one of the overloaded constructors. |
| |
| The default internal texture format is \c GL_RGBA8 for desktop |
| OpenGL, and \c GL_RGBA for OpenGL/ES. |
| |
| It is important that you have a current GL context set when |
| creating the QGLFramebufferObject, otherwise the initialization |
| will fail. |
| |
| \sa size(), texture(), attachment() |
| */ |
| |
| QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, size, NoAttachment, target, DEFAULT_FORMAT); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| QGLFramebufferObject::QGLFramebufferObject(const QSize &size, QMacCompatGLenum target) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, size, NoAttachment, target, DEFAULT_FORMAT); |
| } |
| #endif |
| |
| /*! \overload |
| |
| Constructs an OpenGL framebuffer object and binds a 2D GL texture |
| to the buffer of the given \a width and \a height. |
| |
| \sa size(), texture() |
| */ |
| QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT); |
| } |
| |
| /*! \overload |
| |
| Constructs an OpenGL framebuffer object of the given \a size based on the |
| supplied \a format. |
| */ |
| |
| QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(), |
| format.samples()); |
| } |
| |
| /*! \overload |
| |
| Constructs an OpenGL framebuffer object of the given \a width and \a height |
| based on the supplied \a format. |
| */ |
| |
| QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, QSize(width, height), format.attachment(), format.textureTarget(), |
| format.internalTextureFormat(), format.samples()); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, QSize(width, height), NoAttachment, target, DEFAULT_FORMAT); |
| } |
| #endif |
| |
| /*! \overload |
| |
| Constructs an OpenGL framebuffer object and binds a texture to the |
| buffer of the given \a width and \a height. |
| |
| The \a attachment parameter describes the depth/stencil buffer |
| configuration, \a target the texture target and \a internal_format |
| the internal texture format. The default texture target is \c |
| GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
| for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
| |
| \sa size(), texture(), attachment() |
| */ |
| QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment, |
| GLenum target, GLenum internal_format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, QSize(width, height), attachment, target, internal_format); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment, |
| QMacCompatGLenum target, QMacCompatGLenum internal_format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, QSize(width, height), attachment, target, internal_format); |
| } |
| #endif |
| |
| /*! \overload |
| |
| Constructs an OpenGL framebuffer object and binds a texture to the |
| buffer of the given \a size. |
| |
| The \a attachment parameter describes the depth/stencil buffer |
| configuration, \a target the texture target and \a internal_format |
| the internal texture format. The default texture target is \c |
| GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
| for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
| |
| \sa size(), texture(), attachment() |
| */ |
| QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment, |
| GLenum target, GLenum internal_format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, size, attachment, target, internal_format); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment, |
| QMacCompatGLenum target, QMacCompatGLenum internal_format) |
| : d_ptr(new QGLFramebufferObjectPrivate) |
| { |
| Q_D(QGLFramebufferObject); |
| d->init(this, size, attachment, target, internal_format); |
| } |
| #endif |
| |
| /*! |
| \fn QGLFramebufferObject::~QGLFramebufferObject() |
| |
| Destroys the framebuffer object and frees any allocated resources. |
| */ |
| QGLFramebufferObject::~QGLFramebufferObject() |
| { |
| Q_D(QGLFramebufferObject); |
| QGL_FUNC_CONTEXT; |
| |
| delete d->engine; |
| |
| if (isValid() && ctx) { |
| QGLShareContextScope scope(ctx); |
| if (d->texture) |
| glDeleteTextures(1, &d->texture); |
| if (d->color_buffer) |
| glDeleteRenderbuffers(1, &d->color_buffer); |
| if (d->depth_buffer) |
| glDeleteRenderbuffers(1, &d->depth_buffer); |
| if (d->stencil_buffer && d->stencil_buffer != d->depth_buffer) |
| glDeleteRenderbuffers(1, &d->stencil_buffer); |
| GLuint fbo = d->fbo(); |
| glDeleteFramebuffers(1, &fbo); |
| } |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::isValid() const |
| |
| Returns true if the framebuffer object is valid. |
| |
| The framebuffer can become invalid if the initialization process |
| fails, the user attaches an invalid buffer to the framebuffer |
| object, or a non-power of two width/height is specified as the |
| texture size if the texture target is \c{GL_TEXTURE_2D}. |
| The non-power of two limitation does not apply if the OpenGL version |
| is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension |
| is present. |
| |
| The framebuffer can also become invalid if the QGLContext that |
| the framebuffer was created within is destroyed and there are |
| no other shared contexts that can take over ownership of the |
| framebuffer. |
| */ |
| bool QGLFramebufferObject::isValid() const |
| { |
| Q_D(const QGLFramebufferObject); |
| return d->valid && d->fbo_guard.context(); |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::bind() |
| |
| Switches rendering from the default, windowing system provided |
| framebuffer to this framebuffer object. |
| Returns true upon success, false otherwise. |
| |
| \sa release() |
| */ |
| bool QGLFramebufferObject::bind() |
| { |
| if (!isValid()) |
| return false; |
| Q_D(QGLFramebufferObject); |
| QGL_FUNC_CONTEXT; |
| if (!ctx) |
| return false; // Context no longer exists. |
| const QGLContext *current = QGLContext::currentContext(); |
| #ifdef QT_DEBUG |
| if (!current || |
| QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx)) |
| { |
| qWarning("QGLFramebufferObject::bind() called from incompatible context"); |
| } |
| #endif |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo()); |
| d->valid = d->checkFramebufferStatus(); |
| if (d->valid && current) |
| current->d_ptr->current_fbo = d->fbo(); |
| return d->valid; |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::release() |
| |
| Switches rendering back to the default, windowing system provided |
| framebuffer. |
| Returns true upon success, false otherwise. |
| |
| \sa bind() |
| */ |
| bool QGLFramebufferObject::release() |
| { |
| if (!isValid()) |
| return false; |
| QGL_FUNC_CONTEXT; |
| if (!ctx) |
| return false; // Context no longer exists. |
| |
| const QGLContext *current = QGLContext::currentContext(); |
| |
| #ifdef QT_DEBUG |
| if (!current || |
| QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx)) |
| { |
| qWarning("QGLFramebufferObject::release() called from incompatible context"); |
| } |
| #endif |
| |
| if (current) { |
| current->d_ptr->current_fbo = current->d_ptr->default_fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, current->d_ptr->default_fbo); |
| } |
| |
| return true; |
| } |
| |
| /*! |
| \fn GLuint QGLFramebufferObject::texture() const |
| |
| Returns the texture id for the texture attached as the default |
| rendering target in this framebuffer object. This texture id can |
| be bound as a normal texture in your own GL code. |
| |
| If a multisample framebuffer object is used then the value returned |
| from this function will be invalid. |
| */ |
| GLuint QGLFramebufferObject::texture() const |
| { |
| Q_D(const QGLFramebufferObject); |
| return d->texture; |
| } |
| |
| /*! |
| \fn QSize QGLFramebufferObject::size() const |
| |
| Returns the size of the texture attached to this framebuffer |
| object. |
| */ |
| QSize QGLFramebufferObject::size() const |
| { |
| Q_D(const QGLFramebufferObject); |
| return d->size; |
| } |
| |
| /*! |
| Returns the format of this framebuffer object. |
| */ |
| QGLFramebufferObjectFormat QGLFramebufferObject::format() const |
| { |
| Q_D(const QGLFramebufferObject); |
| return d->format; |
| } |
| |
| /*! |
| \fn QImage QGLFramebufferObject::toImage() const |
| |
| Returns the contents of this framebuffer object as a QImage. |
| */ |
| QImage QGLFramebufferObject::toImage() const |
| { |
| Q_D(const QGLFramebufferObject); |
| if (!d->valid) |
| return QImage(); |
| |
| // qt_gl_read_framebuffer doesn't work on a multisample FBO |
| if (format().samples() != 0) { |
| QGLFramebufferObject temp(size(), QGLFramebufferObjectFormat()); |
| |
| QRect rect(QPoint(0, 0), size()); |
| blitFramebuffer(&temp, rect, const_cast<QGLFramebufferObject *>(this), rect); |
| |
| return temp.toImage(); |
| } |
| |
| bool wasBound = isBound(); |
| if (!wasBound) |
| const_cast<QGLFramebufferObject *>(this)->bind(); |
| QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true); |
| if (!wasBound) |
| const_cast<QGLFramebufferObject *>(this)->release(); |
| |
| return image; |
| } |
| |
| #if !defined(QT_OPENGL_ES_1) |
| Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) |
| #endif |
| |
| #ifndef QT_OPENGL_ES_2 |
| Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) |
| #endif |
| |
| /*! \reimp */ |
| QPaintEngine *QGLFramebufferObject::paintEngine() const |
| { |
| Q_D(const QGLFramebufferObject); |
| if (d->engine) |
| return d->engine; |
| |
| #if !defined(QT_OPENGL_ES_1) |
| #if !defined (QT_OPENGL_ES_2) |
| if (qt_gl_preferGL2Engine()) { |
| #endif |
| QPaintEngine *engine = qt_buffer_2_engine(); |
| if (engine->isActive() && engine->paintDevice() != this) { |
| d->engine = new QGL2PaintEngineEx; |
| return d->engine; |
| } |
| return engine; |
| #if !defined (QT_OPENGL_ES_2) |
| } |
| #endif |
| #endif |
| |
| #if !defined(QT_OPENGL_ES_2) |
| QPaintEngine *engine = qt_buffer_engine(); |
| if (engine->isActive() && engine->paintDevice() != this) { |
| d->engine = new QOpenGLPaintEngine; |
| return d->engine; |
| } |
| return engine; |
| #endif |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::bindDefault() |
| \internal |
| |
| Switches rendering back to the default, windowing system provided |
| framebuffer. |
| Returns true upon success, false otherwise. |
| |
| \sa bind(), release() |
| */ |
| bool QGLFramebufferObject::bindDefault() |
| { |
| QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
| |
| if (ctx) { |
| bool ext_detected = (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); |
| if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx))) |
| return false; |
| |
| ctx->d_ptr->current_fbo = ctx->d_ptr->default_fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->default_fbo); |
| #ifdef QT_DEBUG |
| } else { |
| qWarning("QGLFramebufferObject::bindDefault() called without current context."); |
| #endif |
| } |
| |
| return ctx != 0; |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects() |
| |
| Returns true if the OpenGL \c{GL_EXT_framebuffer_object} extension |
| is present on this system; otherwise returns false. |
| */ |
| bool QGLFramebufferObject::hasOpenGLFramebufferObjects() |
| { |
| return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Draws the given texture, \a textureId, to the given target rectangle, |
| \a target, in OpenGL model space. The \a textureTarget should be a 2D |
| texture target. |
| |
| The framebuffer object should be bound when calling this function. |
| |
| Equivalent to the corresponding QGLContext::drawTexture(). |
| */ |
| void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) |
| { |
| const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| void QGLFramebufferObject::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget) |
| { |
| const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget); |
| } |
| #endif |
| |
| /*! |
| \since 4.4 |
| |
| Draws the given texture, \a textureId, at the given \a point in OpenGL |
| model space. The \a textureTarget should be a 2D texture target. |
| |
| The framebuffer object should be bound when calling this function. |
| |
| Equivalent to the corresponding QGLContext::drawTexture(). |
| */ |
| void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) |
| { |
| const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget); |
| } |
| |
| #ifdef Q_MAC_COMPAT_GL_FUNCTIONS |
| /*! \internal */ |
| void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget) |
| { |
| const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget); |
| } |
| #endif |
| |
| Q_GUI_EXPORT int qt_defaultDpiX(); |
| Q_GUI_EXPORT int qt_defaultDpiY(); |
| |
| /*! \reimp */ |
| int QGLFramebufferObject::metric(PaintDeviceMetric metric) const |
| { |
| Q_D(const QGLFramebufferObject); |
| |
| float dpmx = qt_defaultDpiX()*100./2.54; |
| float dpmy = qt_defaultDpiY()*100./2.54; |
| int w = d->size.width(); |
| int h = d->size.height(); |
| switch (metric) { |
| case PdmWidth: |
| return w; |
| |
| case PdmHeight: |
| return h; |
| |
| case PdmWidthMM: |
| return qRound(w * 1000 / dpmx); |
| |
| case PdmHeightMM: |
| return qRound(h * 1000 / dpmy); |
| |
| case PdmNumColors: |
| return 0; |
| |
| case PdmDepth: |
| return 32;//d->depth; |
| |
| case PdmDpiX: |
| return qRound(dpmx * 0.0254); |
| |
| case PdmDpiY: |
| return qRound(dpmy * 0.0254); |
| |
| case PdmPhysicalDpiX: |
| return qRound(dpmx * 0.0254); |
| |
| case PdmPhysicalDpiY: |
| return qRound(dpmy * 0.0254); |
| |
| default: |
| qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric); |
| break; |
| } |
| return 0; |
| } |
| |
| /*! |
| \fn GLuint QGLFramebufferObject::handle() const |
| |
| Returns the GL framebuffer object handle for this framebuffer |
| object (returned by the \c{glGenFrameBuffersEXT()} function). This |
| handle can be used to attach new images or buffers to the |
| framebuffer. The user is responsible for cleaning up and |
| destroying these objects. |
| */ |
| GLuint QGLFramebufferObject::handle() const |
| { |
| Q_D(const QGLFramebufferObject); |
| return d->fbo(); |
| } |
| |
| /*! \fn int QGLFramebufferObject::devType() const |
| \internal |
| */ |
| |
| |
| /*! |
| Returns the status of the depth and stencil buffers attached to |
| this framebuffer object. |
| */ |
| |
| QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const |
| { |
| Q_D(const QGLFramebufferObject); |
| if (d->valid) |
| return d->fbo_attachment; |
| return NoAttachment; |
| } |
| |
| /*! |
| \since 4.5 |
| |
| Returns true if the framebuffer object is currently bound to a context, |
| otherwise false is returned. |
| */ |
| |
| bool QGLFramebufferObject::isBound() const |
| { |
| Q_D(const QGLFramebufferObject); |
| const QGLContext *current = QGLContext::currentContext(); |
| return current ? current->d_ptr->current_fbo == d->fbo() : false; |
| } |
| |
| /*! |
| \fn bool QGLFramebufferObject::hasOpenGLFramebufferBlit() |
| |
| \since 4.6 |
| |
| Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension |
| is present on this system; otherwise returns false. |
| |
| \sa blitFramebuffer() |
| */ |
| bool QGLFramebufferObject::hasOpenGLFramebufferBlit() |
| { |
| return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit); |
| } |
| |
| /*! |
| \since 4.6 |
| |
| Blits from the \a sourceRect rectangle in the \a source framebuffer |
| object to the \a targetRect rectangle in the \a target framebuffer object. |
| |
| If \a source or \a target is 0, the default framebuffer will be used |
| instead of a framebuffer object as source or target respectively. |
| |
| The \a buffers parameter should be a mask consisting of any combination of |
| \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and |
| \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both |
| in the source and target buffers is ignored. |
| |
| The \a sourceRect and \a targetRect rectangles may have different sizes; |
| in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or |
| \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to |
| \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest |
| interpolation should be used when scaling is performed. |
| |
| If \a source equals \a target a copy is performed within the same buffer. |
| Results are undefined if the source and target rectangles overlap and |
| have different sizes. The sizes must also be the same if any of the |
| framebuffer objects are multisample framebuffers. |
| |
| Note that the scissor test will restrict the blit area if enabled. |
| |
| This function will have no effect unless hasOpenGLFramebufferBlit() returns |
| true. |
| |
| \sa hasOpenGLFramebufferBlit() |
| */ |
| void QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, |
| QGLFramebufferObject *source, const QRect &sourceRect, |
| GLbitfield buffers, |
| GLenum filter) |
| { |
| if (!(QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) |
| return; |
| |
| const QGLContext *ctx = QGLContext::currentContext(); |
| if (!ctx) |
| return; |
| |
| const int height = ctx->device()->height(); |
| |
| const int sh = source ? source->height() : height; |
| const int th = target ? target->height() : height; |
| |
| const int sx0 = sourceRect.left(); |
| const int sx1 = sourceRect.left() + sourceRect.width(); |
| const int sy0 = sh - (sourceRect.top() + sourceRect.height()); |
| const int sy1 = sh - sourceRect.top(); |
| |
| const int tx0 = targetRect.left(); |
| const int tx1 = targetRect.left() + targetRect.width(); |
| const int ty0 = th - (targetRect.top() + targetRect.height()); |
| const int ty1 = th - targetRect.top(); |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, source ? source->handle() : 0); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, target ? target->handle() : 0); |
| |
| glBlitFramebufferEXT(sx0, sy0, sx1, sy1, |
| tx0, ty0, tx1, ty1, |
| buffers, filter); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); |
| } |
| |
| QT_END_NAMESPACE |