diff options
Diffstat (limited to 'src/opengl/qglpixelbuffer.cpp')
-rw-r--r-- | src/opengl/qglpixelbuffer.cpp | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp new file mode 100644 index 0000000..5f74f26 --- /dev/null +++ b/src/opengl/qglpixelbuffer.cpp @@ -0,0 +1,582 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGLPixelBuffer + \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer. + \since 4.1 + + \ingroup multimedia + + Rendering into a pbuffer is normally done using full hardware + acceleration. This can be significantly faster than rendering + into a QPixmap. + + There are three approaches to using this class: + + \list 1 + \o \bold{We can draw into the pbuffer and convert it to a QImage + using toImage().} This is normally much faster than calling + QGLWidget::renderPixmap(). + + \o \bold{We can draw into the pbuffer and copy the contents into + an OpenGL texture using updateDynamicTexture().} This allows + us to create dynamic textures and works on all systems + with pbuffer support. + + \o \bold{On systems that support it, we can bind the pbuffer to + an OpenGL texture.} The texture is then updated automatically + when the pbuffer contents change, eliminating the need for + additional copy operations. This is supported only on Windows + and Mac OS X systems that provide the \c render_texture + extension. + \endlist + + Pbuffers are provided by the OpenGL \c pbuffer extension; call + hasOpenGLPbuffer() to find out if the system provides pbuffers. + + \sa {opengl/pbuffers}{Pbuffers Example} +*/ + + +#include <qglpixelbuffer.h> +#include <private/qglpixelbuffer_p.h> +#include <qimage.h> + +#if !defined(QT_OPENGL_ES_2) +#include <private/qpaintengine_opengl_p.h> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(QT_OPENGL_ES_2) +extern void qgl_cleanup_glyph_cache(QGLContext *); +#else +void qgl_cleanup_glyph_cache(QGLContext *) {} +#endif + +extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); + +void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget) +{ + Q_Q(QGLPixelBuffer); + if(init(size, format, shareWidget)) { + req_size = size; + req_format = format; + req_shareWidget = shareWidget; + invalid = false; + qctx = new QGLContext(format); + qctx->d_func()->sharing = (shareWidget != 0); + if (shareWidget != 0 && shareWidget->d_func()->glcx) + qgl_share_reg()->addShare(qctx, shareWidget->d_func()->glcx); + + qctx->d_func()->paintDevice = q; + qctx->d_func()->valid = true; +#if defined(Q_WS_WIN) && !defined(QT_OPENGL_ES) + qctx->d_func()->dc = dc; + qctx->d_func()->rc = ctx; +#elif (defined(Q_WS_X11) && !defined(QT_OPENGL_ES)) + qctx->d_func()->cx = ctx; + qctx->d_func()->pbuf = (void *) pbuf; + qctx->d_func()->vi = 0; +#elif defined(Q_WS_MAC) + qctx->d_func()->cx = ctx; + qctx->d_func()->vi = 0; +#elif defined(QT_OPENGL_ES) + qctx->d_func()->eglContext = ctx; +#endif + } +} + +/*! + Constructs an OpenGL pbuffer of the given \a size. If no \a + format is specified, the \l{QGLFormat::defaultFormat()}{default + format} is used. If the \a shareWidget parameter points to a + valid QGLWidget, the pbuffer will share its context with \a + shareWidget. + + If you intend to bind this pbuffer as a dynamic texture, the width + and height components of \c size must be powers of two (e.g., 512 + x 128). + + \sa size(), format() +*/ +QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget) + : d_ptr(new QGLPixelBufferPrivate(this)) +{ + Q_D(QGLPixelBuffer); + d->common_init(size, format, shareWidget); +} + + +/*! \overload + + Constructs an OpenGL pbuffer with the \a width and \a height. If + no \a format is specified, the + \l{QGLFormat::defaultFormat()}{default format} is used. If the \a + shareWidget parameter points to a valid QGLWidget, the pbuffer + will share its context with \a shareWidget. + + If you intend to bind this pbuffer as a dynamic texture, the width + and height components of \c size must be powers of two (e.g., 512 + x 128). + + \sa size(), format() +*/ +QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget) + : d_ptr(new QGLPixelBufferPrivate(this)) +{ + Q_D(QGLPixelBuffer); + d->common_init(QSize(width, height), format, shareWidget); +} + + +/*! \fn QGLPixelBuffer::~QGLPixelBuffer() + + Destroys the pbuffer and frees any allocated resources. +*/ +QGLPixelBuffer::~QGLPixelBuffer() +{ + Q_D(QGLPixelBuffer); + + // defined in qpaintengine_opengl.cpp + QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext()); + if (current != d->qctx) + makeCurrent(); + qgl_cleanup_glyph_cache(d->qctx); + d->cleanup(); + delete d->qctx; + if (current && current != d->qctx) + current->makeCurrent(); + delete d_ptr; +} + +/*! \fn bool QGLPixelBuffer::makeCurrent() + + Makes this pbuffer the current OpenGL rendering context. Returns + true on success; otherwise returns false. + + \sa QGLContext::makeCurrent(), doneCurrent() +*/ + +bool QGLPixelBuffer::makeCurrent() +{ + Q_D(QGLPixelBuffer); + if (d->invalid) + return false; + d->qctx->makeCurrent(); + return true; +} + +/*! \fn bool QGLPixelBuffer::doneCurrent() + + Makes no context the current OpenGL context. Returns true on + success; otherwise returns false. +*/ + +bool QGLPixelBuffer::doneCurrent() +{ + Q_D(QGLPixelBuffer); + if (d->invalid) + return false; + d->qctx->doneCurrent(); + return true; +} + +/*! + Generates and binds a 2D GL texture that is the same size as the + pbuffer, and returns the texture's ID. This can be used in + conjunction with bindToDynamicTexture() and + updateDynamicTexture(). + + \sa size() +*/ + +#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) && !defined(QT_OPENGL_ES) +GLuint QGLPixelBuffer::generateDynamicTexture() const +{ + Q_D(const QGLPixelBuffer); + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, d->req_size.width(), d->req_size.height(), 0, GL_RGBA, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + return texture; +} +#endif + +/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id) + + Binds the texture specified by \a texture_id to this pbuffer. + Returns true on success; otherwise returns false. + + The texture must be of the same size and format as the pbuffer. + + To unbind the texture, call releaseFromDynamicTexture(). While + the texture is bound, it is updated automatically when the + pbuffer contents change, eliminating the need for additional copy + operations. + + Example: + + \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 0 + + \warning This function uses the \c {render_texture} extension, + which is currently not supported under X11. An alternative that + works on all systems (including X11) is to manually copy the + pbuffer contents to a texture using updateDynamicTexture(). + + \warning For the bindToDynamicTexture() call to succeed on the + Mac OS X, the pbuffer needs a shared context, i.e. the + QGLPixelBuffer must be created with a share widget. + + \sa generateDynamicTexture(), releaseFromDynamicTexture() +*/ + +/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture() + + Releases the pbuffer from any previously bound texture. + + \sa bindToDynamicTexture() +*/ + +/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers() + + Returns true if the OpenGL \c pbuffer extension is present on + this system; otherwise returns false. +*/ + +/*! + Copies the pbuffer contents into the texture specified with \a + texture_id. + + The texture must be of the same size and format as the pbuffer. + + Example: + + \snippet doc/src/snippets/code/src_opengl_qglpixelbuffer.cpp 1 + + An alternative on Windows and Mac OS X systems that support the + \c render_texture extension is to use bindToDynamicTexture() to + get dynamic updates of the texture. + + \sa generateDynamicTexture(), bindToDynamicTexture() +*/ +void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const +{ + Q_D(const QGLPixelBuffer); + if (d->invalid) + return; + glBindTexture(GL_TEXTURE_2D, texture_id); +#ifndef QT_OPENGL_ES + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0); +#else + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0); +#endif +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +void QGLPixelBuffer::updateDynamicTexture(QMacCompatGLuint texture_id) const +{ + updateDynamicTexture(GLuint(texture_id)); +} +#endif + +/*! + Returns the size of the pbuffer. +*/ +QSize QGLPixelBuffer::size() const +{ + Q_D(const QGLPixelBuffer); + return d->req_size; +} + +/*! + Returns the contents of the pbuffer as a QImage. +*/ +QImage QGLPixelBuffer::toImage() const +{ + Q_D(const QGLPixelBuffer); + if (d->invalid) + return QImage(); + + const_cast<QGLPixelBuffer *>(this)->makeCurrent(); + return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true); +} + +/*! + Returns the native pbuffer handle. +*/ +Qt::HANDLE QGLPixelBuffer::handle() const +{ + Q_D(const QGLPixelBuffer); + if (d->invalid) + return 0; + return (Qt::HANDLE) d->pbuf; +} + +/*! + Returns true if this pbuffer is valid; otherwise returns false. +*/ +bool QGLPixelBuffer::isValid() const +{ + Q_D(const QGLPixelBuffer); + return !d->invalid; +} + +#if !defined(QT_OPENGL_ES_2) +Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine) +#endif + +/*! \reimp */ +QPaintEngine *QGLPixelBuffer::paintEngine() const +{ +#if !defined(QT_OPENGL_ES_2) + return qt_buffer_paintengine(); +#else + return 0; +#endif +} + +extern int qt_defaultDpi(); + +/*! \reimp */ +int QGLPixelBuffer::metric(PaintDeviceMetric metric) const +{ + Q_D(const QGLPixelBuffer); + + float dpmx = qt_defaultDpi()*100./2.54; + float dpmy = qt_defaultDpi()*100./2.54; + int w = d->req_size.width(); + int h = d->req_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 (int)(dpmx * 0.0254); + + case PdmDpiY: + return (int)(dpmy * 0.0254); + + case PdmPhysicalDpiX: + return (int)(dpmx * 0.0254); + + case PdmPhysicalDpiY: + return (int)(dpmy * 0.0254); + + default: + qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric); + break; + } + return 0; +} + +/*! + Generates and binds a 2D GL texture to the current context, based + on \a image. The generated texture id is returned and can be used + in later glBindTexture() calls. + + The \a target parameter specifies the texture target. + + Equivalent to calling QGLContext::bindTexture(). + + \sa deleteTexture() +*/ +GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target) +{ + Q_D(QGLPixelBuffer); +#ifndef QT_OPENGL_ES + return d->qctx->bindTexture(image, target, GLint(GL_RGBA8)); +#else + return d->qctx->bindTexture(image, target, GL_RGBA); +#endif +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +/*! \internal */ +GLuint QGLPixelBuffer::bindTexture(const QImage &image, QMacCompatGLenum target) +{ + Q_D(QGLPixelBuffer); + return d->qctx->bindTexture(image, target, QMacCompatGLint(GL_RGBA8)); +} +#endif + +/*! \overload + + Generates and binds a 2D GL texture based on \a pixmap. + + Equivalent to calling QGLContext::bindTexture(). + + \sa deleteTexture() +*/ +GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target) +{ + Q_D(QGLPixelBuffer); +#ifndef QT_OPENGL_ES + return d->qctx->bindTexture(pixmap, target, GLint(GL_RGBA8)); +#else + return d->qctx->bindTexture(pixmap, target, GL_RGBA); +#endif +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +/*! \internal */ +GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target) +{ + Q_D(QGLPixelBuffer); + return d->qctx->bindTexture(pixmap, target, QMacCompatGLint(GL_RGBA8)); +} +#endif + +/*! \overload + + Reads the DirectDrawSurface (DDS) compressed file \a fileName and + generates a 2D GL texture from it. + + Equivalent to calling QGLContext::bindTexture(). + + \sa deleteTexture() +*/ +GLuint QGLPixelBuffer::bindTexture(const QString &fileName) +{ + Q_D(QGLPixelBuffer); + return d->qctx->bindTexture(fileName); +} + +/*! + Removes the texture identified by \a texture_id from the texture cache. + + Equivalent to calling QGLContext::deleteTexture(). + */ +void QGLPixelBuffer::deleteTexture(GLuint texture_id) +{ + Q_D(QGLPixelBuffer); + d->qctx->deleteTexture(texture_id); +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +/*! \internal */ +void QGLPixelBuffer::deleteTexture(QMacCompatGLuint texture_id) +{ + Q_D(QGLPixelBuffer); + d->qctx->deleteTexture(texture_id); +} +#endif + +/*! + \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. + + Equivalent to the corresponding QGLContext::drawTexture(). +*/ +void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) +{ + Q_D(QGLPixelBuffer); + d->qctx->drawTexture(target, textureId, textureTarget); +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +/*! \internal */ +void QGLPixelBuffer::drawTexture(const QRectF &target, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget) +{ + Q_D(QGLPixelBuffer); + d->qctx->drawTexture(target, textureId, textureTarget); +} +#endif + +/*! + \since 4.4 + + Draws the given texture, \a textureId, at the given \a point in OpenGL model + space. The textureTarget parameter should be a 2D texture target. + + Equivalent to the corresponding QGLContext::drawTexture(). +*/ +void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) +{ + Q_D(QGLPixelBuffer); + d->qctx->drawTexture(point, textureId, textureTarget); +} + +#ifdef Q_MAC_COMPAT_GL_FUNCTIONS +/*! \internal */ +void QGLPixelBuffer::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget) +{ + Q_D(QGLPixelBuffer); + d->qctx->drawTexture(point, textureId, textureTarget); +} +#endif + +/*! + Returns the format of the pbuffer. The format may be different + from the one that was requested. +*/ +QGLFormat QGLPixelBuffer::format() const +{ + Q_D(const QGLPixelBuffer); + return d->format; +} + +/*! \fn int QGLPixelBuffer::devType() const + \reimp +*/ + +QT_END_NAMESPACE |