diff options
author | Jason Barron <jbarron@trolltech.com> | 2009-06-25 13:49:53 (GMT) |
---|---|---|
committer | Jason Barron <jbarron@trolltech.com> | 2009-06-25 13:49:53 (GMT) |
commit | db8f05e257019694f5e8076845626008f2adc3dd (patch) | |
tree | 05d3959403cf15ac5f702091439e028af01f343b /src/opengl/qglframebufferobject.cpp | |
parent | 8aafaa65a1d16f8b982279f5aceedf1e281ddb5a (diff) | |
parent | 796a5a2c7d8c91a46ac761dde18b7da2ec6c177b (diff) | |
download | Qt-db8f05e257019694f5e8076845626008f2adc3dd.zip Qt-db8f05e257019694f5e8076845626008f2adc3dd.tar.gz Qt-db8f05e257019694f5e8076845626008f2adc3dd.tar.bz2 |
Merge commit 'qt/master-stable' into 4.6-stable
Bring Qt 4.6 into the Qt-S60 repo.
Conflicts:
configure.exe
mkspecs/features/qttest_p4.prf
qmake/generators/makefile.cpp
src/corelib/io/qdir.cpp
src/corelib/io/qprocess.h
src/corelib/kernel/qcoreevent.h
src/corelib/kernel/qobject.cpp
src/corelib/kernel/qsharedmemory_unix.cpp
src/corelib/thread/qthread_p.h
src/corelib/tools/qvector.h
src/gui/dialogs/qdialog.cpp
src/gui/dialogs/qfiledialog.cpp
src/gui/dialogs/qfiledialog_p.h
src/gui/dialogs/qmessagebox.cpp
src/gui/graphicsview/qgraphicsitem.cpp
src/gui/graphicsview/qgraphicsview.cpp
src/gui/image/qpixmapcache.cpp
src/gui/kernel/qapplication.cpp
src/gui/kernel/qapplication_p.h
src/gui/kernel/qwidget.cpp
src/gui/kernel/qwidget_p.h
src/gui/painting/qdrawhelper.cpp
src/gui/painting/qpaintengine_raster.cpp
src/gui/text/qfontengine_qpf.cpp
src/gui/widgets/qmenubar.cpp
src/network/socket/qlocalserver.cpp
src/testlib/qtestcase.cpp
src/testlib/testlib.pro
tests/auto/qimagereader/tst_qimagereader.cpp
tests/auto/qitemdelegate/tst_qitemdelegate.cpp
tests/auto/qnetworkreply/tst_qnetworkreply.cpp
tests/auto/qpixmap/qpixmap.pro
Diffstat (limited to 'src/opengl/qglframebufferobject.cpp')
-rw-r--r-- | src/opengl/qglframebufferobject.cpp | 587 |
1 files changed, 518 insertions, 69 deletions
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index 6e908e2..159a00a 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -43,7 +43,14 @@ #include <qdebug.h> #include <private/qgl_p.h> +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +#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> @@ -67,29 +74,242 @@ extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); } \ } +class QGLFramebufferObjectFormatPrivate +{ +public: + int samples; + QGLFramebufferObject::Attachment attachment; + GLenum target; + GLenum internal_format; +}; + +/*! + \class QGLFramebufferObjectFormat + \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL + framebuffer object. + + \since 4.6 + + \ingroup multimedia + + 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 setInternalFormat() Internal 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 +*/ + +/*! + \since 4.6 + + Creates a QGLFramebufferObjectFormat object with properties specifying + the format of an OpenGL framebuffer object. + + A multisample framebuffer object is specified by setting \a samples + to a value different from zero. 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. + + For multisample framebuffer objects a color render buffer is created, + otherwise a texture with the texture target \a target is created. + The color render buffer or texture will have the internal format + \a internalFormat, and will be bound to the \c GL_COLOR_ATTACHMENT0 + attachment in the framebuffer object. + + The \a attachment parameter describes the depth/stencil buffer + configuration. + + \sa samples(), attachment(), target(), internalFormat() +*/ + +QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(int samples, + QGLFramebufferObject::Attachment attachment, + GLenum target, + GLenum internalFormat) +{ + d = new QGLFramebufferObjectFormatPrivate; + d->samples = samples; + d->attachment = attachment; + d->target = target; + d->internal_format = internalFormat; +} + +/*! + \since 4.6 + + Constructs a copy of \a other. +*/ + +QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other) +{ + d = new QGLFramebufferObjectFormatPrivate; + *d = *other.d; +} + +/*! + \since 4.6 + + Assigns \a other to this object. +*/ + +QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other) +{ + *d = *other.d; + return *this; +} + +/*! + \since 4.6 + + Destroys the QGLFramebufferObjectFormat. +*/ +QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat() +{ + delete d; +} + +/*! + \since 4.6 + + Sets the number of samples per pixel for a multisample framebuffer object + to \a samples. + A sample count of 0 represents a regular non-multisample framebuffer object. + + \sa samples() +*/ +void QGLFramebufferObjectFormat::setSamples(int samples) +{ + d->samples = samples; +} + +/*! + \since 4.6 + + Returns the number of samples per pixel if a framebuffer object + is a multisample framebuffer object. Otherwise, returns 0. + + \sa setSamples() +*/ +int QGLFramebufferObjectFormat::samples() const +{ + return d->samples; +} + +/*! + \since 4.6 + + Sets the attachments a framebuffer object should have to \a attachment. + + \sa attachment() +*/ +void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment) +{ + d->attachment = attachment; +} + +/*! + \since 4.6 + + Returns the status of the depth and stencil buffers attached to + a framebuffer object. + + \sa setAttachment() +*/ +QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const +{ + return d->attachment; +} + +/*! + \since 4.6 + + 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) +{ + d->target = target; +} + +/*! + \since 4.6 + + Returns the texture target of the texture attached to a framebuffer object. + Ignored for multisample framebuffer objects. + + \sa setTextureTarget(), samples() +*/ +GLenum QGLFramebufferObjectFormat::textureTarget() const +{ + return d->target; +} + +/*! + \since 4.6 + + Sets the internal format of a framebuffer object's texture or multisample + framebuffer object's color buffer to \a internalFormat. + + \sa internalFormat() +*/ +void QGLFramebufferObjectFormat::setInternalFormat(GLenum internalFormat) +{ + d->internal_format = internalFormat; +} + +/*! + \since 4.6 + + Returns the internal format of a framebuffer object's texture or + multisample framebuffer object's color buffer. + + \sa setInternalFormat() +*/ +GLenum QGLFramebufferObjectFormat::internalFormat() const +{ + return d->internal_format; +} + class QGLFramebufferObjectPrivate { public: - QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), bound(false), ctx(0) {} + QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), bound(false), ctx(0), previous_fbo(0) {} ~QGLFramebufferObjectPrivate() {} void init(const QSize& sz, QGLFramebufferObject::Attachment attachment, - GLenum internal_format, GLenum texture_target); + GLenum internal_format, GLenum texture_target, GLint samples = 0); bool checkFramebufferStatus() const; GLuint texture; GLuint fbo; GLuint depth_stencil_buffer; + GLuint color_buffer; GLenum target; QSize size; + QGLFramebufferObjectFormat format; + int samples; uint valid : 1; uint bound : 1; QGLFramebufferObject::Attachment fbo_attachment; QGLContext *ctx; // for Windows extension ptrs + GLuint previous_fbo; }; bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const { - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_NO_ERROR: case GL_FRAMEBUFFER_COMPLETE_EXT: @@ -121,6 +341,9 @@ bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const 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; @@ -129,7 +352,7 @@ bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const } void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::Attachment attachment, - GLenum texture_target, GLenum internal_format) + GLenum texture_target, GLenum internal_format, GLint samples) { ctx = const_cast<QGLContext *>(QGLContext::currentContext()); bool ext_detected = (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); @@ -141,81 +364,137 @@ void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::At // texture dimensions while (glGetError() != GL_NO_ERROR) {} // reset error state - glGenFramebuffersEXT(1, &fbo); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); QT_CHECK_GLERROR(); // init texture - glGenTextures(1, &texture); - glBindTexture(target, texture); - glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + 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); + 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); + 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 - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - target, texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + target, texture, 0); - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(); + QT_CHECK_GLERROR(); + valid = checkFramebufferStatus(); + + color_buffer = 0; + samples = 0; + } else { + GLint maxSamples; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + + samples = qBound(1, int(samples), int(maxSamples)); + + glGenRenderbuffers(1, &color_buffer); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer); + if (glRenderbufferStorageMultisampleEXT) { + 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); + } if (attachment == QGLFramebufferObject::CombinedDepthStencil && (QGLExtensions::glExtensions & QGLExtensions::PackedDepthStencil)) { // depth and stencil buffer needs another extension - glGenRenderbuffersEXT(1, &depth_stencil_buffer); - Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer)); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer); - Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer)); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); + glGenRenderbuffers(1, &depth_stencil_buffer); + Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer)); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer); + Q_ASSERT(glIsRenderbuffer(depth_stencil_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()); + GLint i = 0; - glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); fbo_attachment = QGLFramebufferObject::CombinedDepthStencil; + valid = checkFramebufferStatus(); if (!valid) - glDeleteRenderbuffersEXT(1, &depth_stencil_buffer); + glDeleteRenderbuffers(1, &depth_stencil_buffer); } else if (attachment == QGLFramebufferObject::Depth || attachment == QGLFramebufferObject::CombinedDepthStencil) { - glGenRenderbuffersEXT(1, &depth_stencil_buffer); - Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer)); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer); - Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer)); + glGenRenderbuffers(1, &depth_stencil_buffer); + Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer)); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer); + Q_ASSERT(glIsRenderbuffer(depth_stencil_buffer)); + if (samples != 0 && glRenderbufferStorageMultisampleEXT) { +#ifdef QT_OPENGL_ES +#define GL_DEPTH_COMPONENT16 0x81A5 + 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 #define GL_DEPTH_COMPONENT16 0x81A5 - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height()); + glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height()); #else - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height()); + glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height()); #endif + } GLint i = 0; - glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); fbo_attachment = QGLFramebufferObject::Depth; valid = checkFramebufferStatus(); if (!valid) - glDeleteRenderbuffersEXT(1, &depth_stencil_buffer); + glDeleteRenderbuffers(1, &depth_stencil_buffer); } else { fbo_attachment = QGLFramebufferObject::NoAttachment; } - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); if (!valid) { - glDeleteTextures(1, &texture); - glDeleteFramebuffersEXT(1, &fbo); + if (color_buffer) + glDeleteRenderbuffers(1, &color_buffer); + else + glDeleteTextures(1, &texture); + glDeleteFramebuffers(1, &fbo); } QT_CHECK_GLERROR(); + + format.setTextureTarget(target); + format.setSamples(int(samples)); + format.setAttachment(fbo_attachment); + format.setInternalFormat(internal_format); } /*! @@ -263,13 +542,18 @@ void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::At framebuffer objects more portable. \endlist - Note that primitives drawn to a QGLFramebufferObject with QPainter - will only be antialiased if the QPainter::HighQualityAntialiasing - render hint is set. This is because there is currently no support - for the \c{GL_EXT_framebuffer_multisample} extension, which is - required to do multisample based antialiasing. Also note that the - QPainter::HighQualityAntialiasing render hint requires the - \c{GL_ARB_fragment_program} extension to work in OpenGL. + 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, unless if the QPainter::HighQualityAntialiasing render hint is + set. The QPainter::HighQualityAntialiasing render hint will enable + antialiasing as long as the \c{GL_ARB_fragment_program} extension is + present. 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. + + 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} */ @@ -357,6 +641,32 @@ QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target) d->init(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(size, format.attachment(), format.textureTarget(), format.internalFormat(), 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(QSize(width, height), format.attachment(), format.textureTarget(), format.internalFormat(), format.samples()); +} + #ifdef Q_MAC_COMPAT_GL_FUNCTIONS /*! \internal */ QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target) @@ -444,9 +754,11 @@ QGLFramebufferObject::~QGLFramebufferObject() || qgl_share_reg()->checkSharing(d->ctx, QGLContext::currentContext()))) { glDeleteTextures(1, &d->texture); + if (d->color_buffer) + glDeleteRenderbuffers(1, &d->color_buffer); if (d->depth_stencil_buffer) - glDeleteRenderbuffersEXT(1, &d->depth_stencil_buffer); - glDeleteFramebuffersEXT(1, &d->fbo); + glDeleteRenderbuffers(1, &d->depth_stencil_buffer); + glDeleteFramebuffers(1, &d->fbo); } } @@ -472,6 +784,15 @@ bool QGLFramebufferObject::isValid() const Switches rendering from the default, windowing system provided framebuffer to this framebuffer object. Returns true upon success, false otherwise. + + Since 4.6: if another QGLFramebufferObject instance was already bound + to the current context, then its handle() will be remembered and + automatically restored when release() is called. This allows multiple + framebuffer rendering targets to be stacked up. It is important that + release() is called on the stacked framebuffer objects in the reverse + order of the calls to bind(). + + \sa release() */ bool QGLFramebufferObject::bind() { @@ -479,8 +800,16 @@ bool QGLFramebufferObject::bind() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, d->fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo); d->bound = d->valid = d->checkFramebufferStatus(); + const QGLContext *context = QGLContext::currentContext(); + if (d->valid && context) { + // Save the previous setting to automatically restore in release(). + if (context->d_ptr->current_fbo != d->fbo) { + d->previous_fbo = context->d_ptr->current_fbo; + context->d_ptr->current_fbo = d->fbo; + } + } return d->valid; } @@ -490,6 +819,12 @@ bool QGLFramebufferObject::bind() Switches rendering back to the default, windowing system provided framebuffer. Returns true upon success, false otherwise. + + Since 4.6: if another QGLFramebufferObject instance was already bound + to the current context when bind() was called, then this function will + automatically re-bind it to the current context. + + \sa bind() */ bool QGLFramebufferObject::release() { @@ -497,10 +832,19 @@ bool QGLFramebufferObject::release() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - d->valid = d->checkFramebufferStatus(); d->bound = false; - return d->valid; + + const QGLContext *context = QGLContext::currentContext(); + if (context) { + // Restore the previous setting for stacked framebuffer objects. + if (d->previous_fbo != context->d_ptr->current_fbo) { + context->d_ptr->current_fbo = d->previous_fbo; + glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->previous_fbo); + } + d->previous_fbo = 0; + } + + return true; } /*! @@ -509,6 +853,9 @@ bool QGLFramebufferObject::release() 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 { @@ -529,6 +876,15 @@ QSize QGLFramebufferObject::size() const } /*! + Returns the format of this framebuffer object. +*/ +const 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. @@ -546,17 +902,27 @@ QImage QGLFramebufferObject::toImage() const return image; } -#if !defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine) +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +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 { -#if !defined(QT_OPENGL_ES_2) - return qt_buffer_paintengine(); +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) + return qt_buffer_engine(); +#elif defined(QT_OPENGL_ES_2) + return qt_buffer_2_engine(); #else - return 0; + Q_D(const QGLFramebufferObject); + if (d->ctx->d_func()->internal_context || qt_gl_preferGL2Engine()) + return qt_buffer_2_engine(); + else + return qt_buffer_engine(); #endif } @@ -568,7 +934,8 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const */ bool QGLFramebufferObject::hasOpenGLFramebufferObjects() { - QGLWidget dmy; // needed to detect and init the QGLExtensions object + if (!QGLContext::currentContext()) + QGLWidget dmy; // needed to detect and init the QGLExtensions object return (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); } @@ -623,15 +990,16 @@ void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint te } #endif -extern int qt_defaultDpi(); +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); /*! \reimp */ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const { Q_D(const QGLFramebufferObject); - float dpmx = qt_defaultDpi()*100./2.54; - float dpmy = qt_defaultDpi()*100./2.54; + 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) { @@ -654,16 +1022,16 @@ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const return 32;//d->depth; case PdmDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); case PdmPhysicalDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmPhysicalDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); default: qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric); @@ -719,4 +1087,85 @@ bool QGLFramebufferObject::isBound() const return d->bound; } +/*! + \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. +*/ +bool QGLFramebufferObject::hasOpenGLFramebufferBlit() +{ + if (!QGLContext::currentContext()) + QGLWidget dmy; // needed to detect and init the QGLExtensions object + 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 + COLOR_BUFFER_BIT, DEPTH_BUFFER_BIT, and 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 DEPTH_BUFFER_BIT or + STENCIL_BUFFER_BIT. The \a filter parameter should be set to GL_LINEAR or + 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. +*/ +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 |