src/opengl/qgl.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
--- a/src/opengl/qgl.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/src/opengl/qgl.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -127,18 +127,6 @@
 QGLExtensions::Extensions QGLExtensions::glExtensions = 0;
 bool QGLExtensions::nvidiaFboNeedsFinish = false;
 
-#ifndef APIENTRY
-# define APIENTRY
-#endif
-typedef void (APIENTRY *pfn_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei,
-                                                        GLsizei, GLint, GLsizei, const GLvoid *);
-static pfn_glCompressedTexImage2DARB qt_glCompressedTexImage2DARB = 0;
-
-
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-
 Q_GLOBAL_STATIC(QGLSignalProxy, theSignalProxy)
 QGLSignalProxy *QGLSignalProxy::instance()
 {
@@ -184,8 +172,13 @@
             engineType = QPaintEngine::OpenGL2;
 #else
             // We can't do this in the constructor for this object because it
-            // needs to be called *before* the QApplication constructor
+            // needs to be called *before* the QApplication constructor.
+            // Also check for the FragmentShader extension in conjunction with
+            // the 2.0 version flag, to cover the case where we export the display
+            // from an old GL 1.1 server to a GL 2.x client. In that case we can't
+            // use GL 2.0.
             if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)
+                && (QGLExtensions::glExtensions & QGLExtensions::FragmentShader)
                 && qgetenv("QT_GL_USE_OPENGL1ENGINE").isEmpty())
                 engineType = QPaintEngine::OpenGL2;
             else
@@ -404,8 +397,7 @@
     \i \link setDirectRendering() Direct rendering:\endlink Enabled.
     \i \link setOverlay() Overlay:\endlink Disabled.
     \i \link setPlane() Plane:\endlink 0 (i.e., normal plane).
-    \i \link setSampleBuffers() Multisample buffers:\endlink Enabled on
-       OpenGL/ES 2.0, disabled on other platforms.
+    \i \link setSampleBuffers() Multisample buffers:\endlink Disabled.
     \endlist
 */
 
@@ -1490,6 +1482,7 @@
     version_flags_cached = false;
     version_flags = QGLFormat::OpenGL_Version_None;
     current_fbo = 0;
+    default_fbo = 0;
     active_engine = 0;
 }
 
@@ -1773,10 +1766,12 @@
 
 /*!
     \enum QGLContext::BindOption
+    \since 4.6
+
     A set of options to decide how to bind a texture using bindTexture().
 
     \value NoBindOption Don't do anything, pass the texture straight
-    thru.
+    through.
 
     \value InvertedYBindOption Specifies that the texture should be flipped
     over the X axis so that the texture coordinate 0,0 corresponds to
@@ -1879,118 +1874,42 @@
 {
 }
 
-typedef QHash<QString, GLuint> QGLDDSCache;
-Q_GLOBAL_STATIC(QGLDDSCache, qgl_dds_cache)
-
 /*!
     \overload
 
-    Reads the DirectDrawSurface (DDS) compressed file \a fileName and
-    generates a 2D GL texture from it.
-
-    Only the DXT1, DXT3 and DXT5 DDS formats are supported.
-
-    Note that this will only work if the implementation supports the
-    \c GL_ARB_texture_compression and \c GL_EXT_texture_compression_s3tc
-    extensions.
+    Reads the compressed texture file \a fileName and generates a 2D GL
+    texture from it.
+
+    This function can load DirectDrawSurface (DDS) textures in the
+    DXT1, DXT3 and DXT5 DDS formats if the \c GL_ARB_texture_compression
+    and \c GL_EXT_texture_compression_s3tc extensions are supported.
+
+    Since 4.6.1, textures in the ETC1 format can be loaded if the
+    \c GL_OES_compressed_ETC1_RGB8_texture extension is supported
+    and the ETC1 texture has been encapsulated in the PVR container format.
+    Also, textures in the PVRTC2 and PVRTC4 formats can be loaded
+    if the \c GL_IMG_texture_compression_pvrtc extension is supported.
 
     \sa deleteTexture()
 */
 
 GLuint QGLContext::bindTexture(const QString &fileName)
 {
-    if (!qt_glCompressedTexImage2DARB) {
-        qWarning("QGLContext::bindTexture(): The GL implementation does not support texture"
-                 "compression extensions.");
-        return 0;
-    }
-
-    QGLDDSCache::const_iterator it = qgl_dds_cache()->constFind(fileName);
-    if (it != qgl_dds_cache()->constEnd()) {
+    Q_D(QGLContext);
+    QGLDDSCache *dds_cache = &(d->group->m_dds_cache);
+    QGLDDSCache::const_iterator it = dds_cache->constFind(fileName);
+    if (it != dds_cache->constEnd()) {
         glBindTexture(GL_TEXTURE_2D, it.value());
         return it.value();
     }
 
-    QFile f(fileName);
-    f.open(QIODevice::ReadOnly);
-
-    char tag[4];
-    f.read(&tag[0], 4);
-    if (strncmp(tag,"DDS ", 4) != 0) {
-        qWarning("QGLContext::bindTexture(): not a DDS image file.");
-        return 0;
-    }
-
-    DDSFormat ddsHeader;
-    f.read((char *) &ddsHeader, sizeof(DDSFormat));
-
-    if (!ddsHeader.dwLinearSize) {
-        qWarning("QGLContext::bindTexture() DDS image size is not valid.");
-        return 0;
-    }
-
-    int factor = 4;
-    int bufferSize = 0;
-    int blockSize = 16;
-    GLenum format;
-
-    switch(ddsHeader.ddsPixelFormat.dwFourCC) {
-    case FOURCC_DXT1:
-        format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
-        factor = 2;
-        blockSize = 8;
-        break;
-    case FOURCC_DXT3:
-        format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
-        break;
-    case FOURCC_DXT5:
-        format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
-        break;
-    default:
-        qWarning("QGLContext::bindTexture() DDS image format not supported.");
+    QGLTexture texture(this);
+    QSize size = texture.bindCompressedTexture(fileName);
+    if (!size.isValid())
         return 0;
-    }
-
-    if (ddsHeader.dwMipMapCount > 1)
-        bufferSize = ddsHeader.dwLinearSize * factor;
-    else
-        bufferSize = ddsHeader.dwLinearSize;
-
-    GLubyte *pixels = (GLubyte *) malloc(bufferSize*sizeof(GLubyte));
-    f.seek(ddsHeader.dwSize + 4);
-    f.read((char *) pixels, bufferSize);
-    f.close();
-
-    GLuint tx_id;
-    glGenTextures(1, &tx_id);
-    glBindTexture(GL_TEXTURE_2D, tx_id);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-    int size;
-    int offset = 0;
-    int w = ddsHeader.dwWidth;
-    int h = ddsHeader.dwHeight;
-
-    // load mip-maps
-    for(int i = 0; i < (int) ddsHeader.dwMipMapCount; ++i) {
-        if (w == 0) w = 1;
-        if (h == 0) h = 1;
-
-        size = ((w+3)/4) * ((h+3)/4) * blockSize;
-        qt_glCompressedTexImage2DARB(GL_TEXTURE_2D, i, format, w, h, 0,
-                                     size, pixels + offset);
-        offset += size;
-
-        // half size for each mip-map level
-        w = w/2;
-        h = h/2;
-    }
-
-    free(pixels);
-
-    qgl_dds_cache()->insert(fileName, tx_id);
-    return tx_id;
+
+    dds_cache->insert(fileName, texture.id);
+    return texture.id;
 }
 
 static inline QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format)
@@ -2137,13 +2056,36 @@
     Q_ASSERT(texture);
 
     if (texture->id > 0)
-        const_cast<QImage &>(image).data_ptr()->is_cached = true;
+        QImagePixmapCleanupHooks::enableCleanupHooks(image);
 
     return texture;
 }
 
 // #define QGL_BIND_TEXTURE_DEBUG
 
+// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout
+static inline void qgl_byteSwapImage(QImage &img, GLenum pixel_type)
+{
+    const int width = img.width();
+    const int height = img.height();
+
+    if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV
+        || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian))
+    {
+        for (int i = 0; i < height; ++i) {
+            uint *p = (uint *) img.scanLine(i);
+            for (int x = 0; x < width; ++x)
+                p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
+        }
+    } else {
+        for (int i = 0; i < height; ++i) {
+            uint *p = (uint *) img.scanLine(i);
+            for (int x = 0; x < width; ++x)
+                p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff);
+        }
+    }
+}
+
 QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint internalFormat,
                                            const qint64 key, QGLContext::BindOptions options)
 {
@@ -2156,6 +2098,11 @@
     time.start();
 #endif
 
+#ifndef QT_NO_DEBUG
+    // Reset the gl error stack...git
+    while (glGetError() != GL_NO_ERROR) ;
+#endif
+
     // Scale the pixmap if needed. GL textures needs to have the
     // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
     // 2.0 or use the GL_TEXTURE_RECTANGLE texture target
@@ -2163,8 +2110,8 @@
     int tx_h = qt_next_power_of_two(image.height());
 
     QImage img = image;
-    if (( !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) &&
-          !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0) )
+    if (!(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures)
+        && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0)
         && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height())))
     {
         img = img.scaled(tx_w, tx_h);
@@ -2185,9 +2132,9 @@
     bool genMipmap = false;
 #endif
     if (glFormat.directRendering()
-        && QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap
+        && (QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap)
         && target == GL_TEXTURE_2D
-        && options & QGLContext::MipmapBindOption)
+        && (options & QGLContext::MipmapBindOption))
     {
 #ifdef QGL_BIND_TEXTURE_DEBUG
         printf(" - generating mipmaps (%d ms)\n", time.elapsed());
@@ -2213,7 +2160,7 @@
     bool premul = options & QGLContext::PremultipliedAlphaBindOption;
     GLenum externalFormat;
     GLuint pixel_type;
-    if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
+    if (QGLExtensions::glExtensions & QGLExtensions::BGRATextureFormat) {
         externalFormat = GL_BGRA;
         pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
     } else {
@@ -2265,13 +2212,21 @@
 #ifdef QGL_BIND_TEXTURE_DEBUG
             printf(" - flipping bits over y (%d ms)\n", time.elapsed());
 #endif
-        int ipl = img.bytesPerLine() / 4;
-        int h = img.height();
-        for (int y=0; y<h/2; ++y) {
-            int *a = (int *) img.scanLine(y);
-            int *b = (int *) img.scanLine(h - y - 1);
-            for (int x=0; x<ipl; ++x)
-                qSwap(a[x], b[x]);
+        if (img.isDetached()) {
+            int ipl = img.bytesPerLine() / 4;
+            int h = img.height();
+            for (int y=0; y<h/2; ++y) {
+                int *a = (int *) img.scanLine(y);
+                int *b = (int *) img.scanLine(h - y - 1);
+                for (int x=0; x<ipl; ++x)
+                    qSwap(a[x], b[x]);
+            }
+        } else {
+            // Create a new image and copy across.  If we use the
+            // above in-place code then a full copy of the image is
+            // made before the lines are swapped, which processes the
+            // data twice.  This version should only do it once.
+            img = img.mirrored();
         }
     }
 
@@ -2283,24 +2238,16 @@
         // 32 in the switch above is for the RGB16 case, where we set
         // the format to GL_RGB
         Q_ASSERT(img.depth() == 32);
-        const int width = img.width();
-        const int height = img.height();
-
-        if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV
-            || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
-            for (int i=0; i < height; ++i) {
-                uint *p = (uint *) img.scanLine(i);
-                for (int x=0; x<width; ++x)
-                    p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
-            }
-        } else {
-            for (int i=0; i < height; ++i) {
-                uint *p = (uint *) img.scanLine(i);
-                for (int x=0; x<width; ++x)
-                    p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff);
-            }
-        }
+        qgl_byteSwapImage(img, pixel_type);
     }
+#ifdef QT_OPENGL_ES
+    // OpenGL/ES requires that the internal and external formats be identical.
+    // This is typically used to convert GL_RGBA into GL_BGRA.
+    // Also, we need to use GL_UNSIGNED_BYTE when the format is GL_BGRA.
+    internalFormat = externalFormat;
+    if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV)
+        pixel_type = GL_UNSIGNED_BYTE;
+#endif
 #ifdef QGL_BIND_TEXTURE_DEBUG
     printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n",
            img.format(), externalFormat, internalFormat, pixel_type);
@@ -2391,7 +2338,7 @@
     Q_ASSERT(texture);
 
     if (texture->id > 0)
-        const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = true;
+        QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
 
     return texture;
 }
@@ -2440,6 +2387,9 @@
 */
 GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(image, target, format, false, DefaultBindOption);
     return texture->id;
@@ -2472,6 +2422,9 @@
 */
 GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(image, target, format, false, options);
     return texture->id;
@@ -2481,6 +2434,9 @@
 /*! \internal */
 GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, DefaultBindOption);
     return texture->id;
@@ -2490,6 +2446,9 @@
 GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format,
                                BindOptions options)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, options);
     return texture->id;
@@ -2502,6 +2461,9 @@
 */
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(pixmap, target, format, DefaultBindOption);
     return texture->id;
@@ -2516,6 +2478,9 @@
 */
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, BindOptions options)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(pixmap, target, format, options);
     return texture->id;
@@ -2525,6 +2490,9 @@
 /*! \internal */
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), DefaultBindOption);
     return texture->id;
@@ -2533,6 +2501,9 @@
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format,
                                BindOptions options)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
     QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), options);
     return texture->id;
@@ -2548,17 +2519,20 @@
 */
 void QGLContext::deleteTexture(GLuint id)
 {
+    Q_D(QGLContext);
+
     if (QGLTextureCache::instance()->remove(this, id))
         return;
 
     // check the DDS cache if the texture wasn't found in the pixmap/image
     // cache
-    QList<QString> ddsKeys = qgl_dds_cache()->keys();
+    QGLDDSCache *dds_cache = &(d->group->m_dds_cache);
+    QList<QString> ddsKeys = dds_cache->keys();
     for (int i = 0; i < ddsKeys.size(); ++i) {
-        GLuint texture = qgl_dds_cache()->value(ddsKeys.at(i));
+        GLuint texture = dds_cache->value(ddsKeys.at(i));
         if (id == texture) {
             glDeleteTextures(1, &texture);
-            qgl_dds_cache()->remove(ddsKeys.at(i));
+            dds_cache->remove(ddsKeys.at(i));
             return;
         }
     }
@@ -2999,7 +2973,7 @@
 bool QGLContext::isSharing() const
 {
     Q_D(const QGLContext);
-    return d->sharing;
+    return d->group->isSharing();
 }
 
 QGLFormat QGLContext::format() const
@@ -3836,6 +3810,11 @@
     }
 
 #if defined(QT_OPENGL_ES)
+    // A re-parent is likely to destroy the X11 window and re-create it. It is important
+    // that we free the EGL surface _before_ the winID changes - otherwise we can leak.
+    if (e->type() == QEvent::ParentAboutToChange)
+        d->glcx->d_func()->destroyEglSurfaceForDevice();
+
     if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) {
         // The window may have been re-created during re-parent or state change - if so, the EGL
         // surface will need to be re-created.
@@ -4047,7 +4026,7 @@
         glReadPixels(0, 0, w, h, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, res.bits());
         const QVector<QColor> pal = QColormap::instance().colormap();
         if (pal.size()) {
-            res.setNumColors(pal.size());
+            res.setColorCount(pal.size());
             for (int i = 0; i < pal.size(); i++)
                 res.setColor(i, pal.at(i).rgb());
         }
@@ -4126,7 +4105,7 @@
     const QGLContext *ctx = QGLContext::currentContext();
     if (ctx) {
         if (ctx->format().rgba())
-            glColor4ub(c.red(), c.green(), c.blue(), c.alpha());
+            glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());
         else if (!d->cmap.isEmpty()) { // QGLColormap in use?
             int i = d->cmap.find(c.rgb());
             if (i < 0)
@@ -4323,6 +4302,7 @@
     glDisable(GL_CULL_FACE);
     glDisable(GL_LIGHTING);
     glDisable(GL_STENCIL_TEST);
+    glDisable(GL_DEPTH_TEST);
     glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 }
@@ -4376,6 +4356,10 @@
    \note This function clears the stencil buffer.
 
    \note This function is not supported on OpenGL/ES systems.
+
+   \note This function temporarily disables depth-testing when the
+   text is drawn.
+
    \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead.
 */
 
@@ -4466,6 +4450,13 @@
     have the labels move with the model as it is rotated etc.
 
     \note This function is not supported on OpenGL/ES systems.
+
+    \note If depth testing is enabled before this function is called,
+    then the drawn text will be depth-tested against the models that
+    have already been drawn in the scene.  Use \c{glDisable(GL_DEPTH_TEST)}
+    before calling this function to annotate the models without
+    depth-testing the text.
+
     \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead.
 */
 void QGLWidget::renderText(double x, double y, double z, const QString &str, const QFont &font, int)
@@ -4590,6 +4581,9 @@
 */
 GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLWidget);
     return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption);
 }
@@ -4603,6 +4597,9 @@
  */
 GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLWidget);
     return d->glcx->bindTexture(image, target, format, options);
 }
@@ -4612,6 +4609,9 @@
 /*! \internal */
 GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
 {
+    if (image.isNull())
+        return 0;
+
    Q_D(QGLWidget);
    return d->glcx->bindTexture(image, GLenum(target), GLint(format), QGLContext::DefaultBindOption);
 }
@@ -4619,6 +4619,9 @@
 GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format,
                               QGLContext::BindOptions options)
 {
+    if (image.isNull())
+        return 0;
+
    Q_D(QGLWidget);
    return d->glcx->bindTexture(image, GLenum(target), GLint(format), options);
 }
@@ -4632,6 +4635,9 @@
 */
 GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLWidget);
     return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption);
 }
@@ -4840,58 +4846,67 @@
 
 void QGLExtensions::init_extensions()
 {
-    QString extensions = QLatin1String(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
-    if (extensions.contains(QLatin1String("texture_rectangle")))
+    QGLExtensionMatcher extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
+
+    if (extensions.match("GL_ARB_texture_rectangle"))
         glExtensions |= TextureRectangle;
-    if (extensions.contains(QLatin1String("multisample")))
+    if (extensions.match("GL_ARB_multisample"))
         glExtensions |= SampleBuffers;
-    if (extensions.contains(QLatin1String("generate_mipmap")))
+    if (extensions.match("GL_SGIS_generate_mipmap"))
         glExtensions |= GenerateMipmap;
-    if (extensions.contains(QLatin1String("texture_compression_s3tc")))
+    if (extensions.match("GL_ARB_texture_compression"))
         glExtensions |= TextureCompression;
-    if (extensions.contains(QLatin1String("ARB_fragment_program")))
+    if (extensions.match("GL_EXT_texture_compression_s3tc"))
+        glExtensions |= DDSTextureCompression;
+    if (extensions.match("GL_OES_compressed_ETC1_RGB8_texture"))
+        glExtensions |= ETC1TextureCompression;
+    if (extensions.match("GL_IMG_texture_compression_pvrtc"))
+        glExtensions |= PVRTCTextureCompression;
+    if (extensions.match("GL_ARB_fragment_program"))
         glExtensions |= FragmentProgram;
-    if (extensions.contains(QLatin1String("mirrored_repeat")))
+    if (extensions.match("GL_ARB_fragment_shader"))
+        glExtensions |= FragmentShader;
+    if (extensions.match("GL_ARB_texture_mirrored_repeat"))
         glExtensions |= MirroredRepeat;
-    if (extensions.contains(QLatin1String("EXT_framebuffer_object")))
+    if (extensions.match("GL_EXT_framebuffer_object"))
         glExtensions |= FramebufferObject;
-    if (extensions.contains(QLatin1String("EXT_stencil_two_side")))
+    if (extensions.match("GL_EXT_stencil_two_side"))
         glExtensions |= StencilTwoSide;
-    if (extensions.contains(QLatin1String("EXT_stencil_wrap")))
+    if (extensions.match("GL_EXT_stencil_wrap"))
         glExtensions |= StencilWrap;
-    if (extensions.contains(QLatin1String("EXT_packed_depth_stencil")))
+    if (extensions.match("GL_EXT_packed_depth_stencil"))
         glExtensions |= PackedDepthStencil;
-    if (extensions.contains(QLatin1String("GL_NV_float_buffer")))
+    if (extensions.match("GL_NV_float_buffer"))
         glExtensions |= NVFloatBuffer;
-    if (extensions.contains(QLatin1String("ARB_pixel_buffer_object")))
+    if (extensions.match("GL_ARB_pixel_buffer_object"))
         glExtensions |= PixelBufferObject;
 #if defined(QT_OPENGL_ES_2)
     glExtensions |= FramebufferObject;
     glExtensions |= GenerateMipmap;
+    glExtensions |= FragmentShader;
 #endif
 #if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
-    if (extensions.contains(QLatin1String("OES_framebuffer_object")))
+    if (extensions.match("GL_OES_framebuffer_object"))
         glExtensions |= FramebufferObject;
 #endif
 #if defined(QT_OPENGL_ES)
-    if (extensions.contains(QLatin1String("OES_packed_depth_stencil")))
+    if (extensions.match("GL_OES_packed_depth_stencil"))
         glExtensions |= PackedDepthStencil;
 #endif
-    if (extensions.contains(QLatin1String("ARB_framebuffer_object"))) {
+    if (extensions.match("GL_ARB_framebuffer_object")) {
         // ARB_framebuffer_object also includes EXT_framebuffer_blit.
         glExtensions |= FramebufferObject;
         glExtensions |= FramebufferBlit;
     }
-    if (extensions.contains(QLatin1String("EXT_framebuffer_blit")))
+
+    if (extensions.match("GL_EXT_framebuffer_blit"))
         glExtensions |= FramebufferBlit;
 
-    if (extensions.contains(QLatin1String("GL_ARB_texture_non_power_of_two")))
+    if (extensions.match("GL_ARB_texture_non_power_of_two"))
         glExtensions |= NPOTTextures;
 
-    QGLContext cx(QGLFormat::defaultFormat());
-    if (glExtensions & TextureCompression) {
-        qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
-    }
+    if (extensions.match("GL_EXT_bgra"))
+        glExtensions |= BGRATextureFormat;
 }
 
 /*
@@ -5047,4 +5062,341 @@
     }
 }
 
+QSize QGLTexture::bindCompressedTexture
+    (const QString& fileName, const char *format)
+{
+    QFile file(fileName);
+    if (!file.open(QIODevice::ReadOnly))
+        return QSize();
+    QByteArray contents = file.readAll();
+    file.close();
+    return bindCompressedTexture
+        (contents.constData(), contents.size(), format);
+}
+
+// PVR header format for container files that store textures compressed
+// with the ETC1, PVRTC2, and PVRTC4 encodings.  Format information from the
+// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp
+// "PVRTexTool Reference Manual, version 1.11f".
+struct PvrHeader
+{
+    quint32 headerSize;
+    quint32 height;
+    quint32 width;
+    quint32 mipMapCount;
+    quint32 flags;
+    quint32 dataSize;
+    quint32 bitsPerPixel;
+    quint32 redMask;
+    quint32 greenMask;
+    quint32 blueMask;
+    quint32 alphaMask;
+    quint32 magic;
+    quint32 surfaceCount;
+};
+
+#define PVR_MAGIC               0x21525650      // "PVR!" in little-endian
+
+#define PVR_FORMAT_MASK         0x000000FF
+#define PVR_FORMAT_PVRTC2       0x00000018
+#define PVR_FORMAT_PVRTC4       0x00000019
+#define PVR_FORMAT_ETC1         0x00000036
+
+#define PVR_HAS_MIPMAPS         0x00000100
+#define PVR_TWIDDLED            0x00000200
+#define PVR_NORMAL_MAP          0x00000400
+#define PVR_BORDER_ADDED        0x00000800
+#define PVR_CUBE_MAP            0x00001000
+#define PVR_FALSE_COLOR_MIPMAPS 0x00002000
+#define PVR_VOLUME_TEXTURE      0x00004000
+#define PVR_ALPHA_IN_TEXTURE    0x00008000
+#define PVR_VERTICAL_FLIP       0x00010000
+
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG      0x8C00
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG      0x8C01
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG     0x8C02
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG     0x8C03
+#endif
+
+#ifndef GL_ETC1_RGB8_OES
+#define GL_ETC1_RGB8_OES                        0x8D64
+#endif
+
+bool QGLTexture::canBindCompressedTexture
+    (const char *buf, int len, const char *format, bool *hasAlpha)
+{
+    if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+        // Compressed texture loading only supported on little-endian
+        // systems such as x86 and ARM at the moment.
+        return false;
+    }
+    if (!format) {
+        // Auto-detect the format from the header.
+        if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+            *hasAlpha = true;
+            return true;
+        } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+            const PvrHeader *pvrHeader =
+                reinterpret_cast<const PvrHeader *>(buf);
+            *hasAlpha = (pvrHeader->alphaMask != 0);
+            return true;
+        }
+    } else {
+        // Validate the format against the header.
+        if (!qstricmp(format, "DDS")) {
+            if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) {
+                *hasAlpha = true;
+                return true;
+            }
+        } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+            if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) {
+                const PvrHeader *pvrHeader =
+                    reinterpret_cast<const PvrHeader *>(buf);
+                *hasAlpha = (pvrHeader->alphaMask != 0);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+#define ctx QGLContext::currentContext()
+
+QSize QGLTexture::bindCompressedTexture
+    (const char *buf, int len, const char *format)
+{
+    if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) {
+        // Compressed texture loading only supported on little-endian
+        // systems such as x86 and ARM at the moment.
+        return QSize();
+    }
+#if !defined(QT_OPENGL_ES)
+    if (!glCompressedTexImage2D) {
+        if (!(QGLExtensions::glExtensions & QGLExtensions::TextureCompression)) {
+            qWarning("QGLContext::bindTexture(): The GL implementation does "
+                     "not support texture compression extensions.");
+            return QSize();
+        }
+        glCompressedTexImage2D = (_glCompressedTexImage2DARB) ctx->getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+        if (!glCompressedTexImage2D) {
+            qWarning("QGLContext::bindTexture(): could not resolve "
+                     "glCompressedTexImage2DARB.");
+            return QSize();
+        }
+    }
+#endif
+    if (!format) {
+        // Auto-detect the format from the header.
+        if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+            return bindCompressedTextureDDS(buf, len);
+        else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+            return bindCompressedTexturePVR(buf, len);
+    } else {
+        // Validate the format against the header.
+        if (!qstricmp(format, "DDS")) {
+            if (len >= 4 && !qstrncmp(buf, "DDS ", 4))
+                return bindCompressedTextureDDS(buf, len);
+        } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) {
+            if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4))
+                return bindCompressedTexturePVR(buf, len);
+        }
+    }
+    return QSize();
+}
+
+QSize QGLTexture::bindCompressedTextureDDS(const char *buf, int len)
+{
+    // We only support 2D texture loading at present.
+    if (target != GL_TEXTURE_2D)
+        return QSize();
+
+    // Bail out if the necessary extension is not present.
+    if (!(QGLExtensions::glExtensions & QGLExtensions::DDSTextureCompression)) {
+        qWarning("QGLContext::bindTexture(): DDS texture compression is not supported.");
+        return QSize();
+    }
+
+    const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4);
+    if (!ddsHeader->dwLinearSize) {
+        qWarning("QGLContext::bindTexture(): DDS image size is not valid.");
+        return QSize();
+    }
+
+    int blockSize = 16;
+    GLenum format;
+
+    switch(ddsHeader->ddsPixelFormat.dwFourCC) {
+    case FOURCC_DXT1:
+        format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+        blockSize = 8;
+        break;
+    case FOURCC_DXT3:
+        format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+        break;
+    case FOURCC_DXT5:
+        format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+        break;
+    default:
+        qWarning("QGLContext::bindTexture(): DDS image format not supported.");
+        return QSize();
+    }
+
+    const GLubyte *pixels =
+        reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4);
+
+    glGenTextures(1, &id);
+    glBindTexture(GL_TEXTURE_2D, id);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+    int size;
+    int offset = 0;
+    int available = len - int(ddsHeader->dwSize + 4);
+    int w = ddsHeader->dwWidth;
+    int h = ddsHeader->dwHeight;
+
+    // load mip-maps
+    for(int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) {
+        if (w == 0) w = 1;
+        if (h == 0) h = 1;
+
+        size = ((w+3)/4) * ((h+3)/4) * blockSize;
+        if (size > available)
+            break;
+        glCompressedTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0,
+                               size, pixels + offset);
+        offset += size;
+        available -= size;
+
+        // half size for each mip-map level
+        w = w/2;
+        h = h/2;
+    }
+
+    // DDS images are not inverted.
+    options &= ~QGLContext::InvertedYBindOption;
+
+    return QSize(ddsHeader->dwWidth, ddsHeader->dwHeight);
+}
+
+QSize QGLTexture::bindCompressedTexturePVR(const char *buf, int len)
+{
+    // We only support 2D texture loading at present.  Cube maps later.
+    if (target != GL_TEXTURE_2D)
+        return QSize();
+
+    // Determine which texture format we will be loading.
+    const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf);
+    GLenum textureFormat;
+    quint32 minWidth, minHeight;
+    switch (pvrHeader->flags & PVR_FORMAT_MASK) {
+    case PVR_FORMAT_PVRTC2:
+        if (pvrHeader->alphaMask)
+            textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+        else
+            textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+        minWidth = 16;
+        minHeight = 8;
+        break;
+
+    case PVR_FORMAT_PVRTC4:
+        if (pvrHeader->alphaMask)
+            textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+        else
+            textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+        minWidth = 8;
+        minHeight = 8;
+        break;
+
+    case PVR_FORMAT_ETC1:
+        textureFormat = GL_ETC1_RGB8_OES;
+        minWidth = 4;
+        minHeight = 4;
+        break;
+
+    default:
+        qWarning("QGLContext::bindTexture(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK));
+        return QSize();
+    }
+
+    // Bail out if the necessary extension is not present.
+    if (textureFormat == GL_ETC1_RGB8_OES) {
+        if (!(QGLExtensions::glExtensions &
+                    QGLExtensions::ETC1TextureCompression)) {
+            qWarning("QGLContext::bindTexture(): ETC1 texture compression is not supported.");
+            return QSize();
+        }
+    } else {
+        if (!(QGLExtensions::glExtensions &
+                    QGLExtensions::PVRTCTextureCompression)) {
+            qWarning("QGLContext::bindTexture(): PVRTC texture compression is not supported.");
+            return QSize();
+        }
+    }
+
+    // Boundary check on the buffer size.
+    quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize;
+    if (bufferSize > quint32(len)) {
+        qWarning("QGLContext::bindTexture(): PVR image size is not valid.");
+        return QSize();
+    }
+
+    // Create the texture.
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    glGenTextures(1, &id);
+    glBindTexture(GL_TEXTURE_2D, id);
+    if (pvrHeader->mipMapCount) {
+        if ((options & QGLContext::LinearFilteringBindOption) != 0) {
+            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+        } else {
+            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+        }
+    } else if ((options & QGLContext::LinearFilteringBindOption) != 0) {
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    } else {
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    }
+
+    // Load the compressed mipmap levels.
+    const GLubyte *buffer =
+        reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize);
+    bufferSize = pvrHeader->dataSize;
+    quint32 level = 0;
+    quint32 width = pvrHeader->width;
+    quint32 height = pvrHeader->height;
+    while (bufferSize > 0 && level < pvrHeader->mipMapCount) {
+        quint32 size =
+            (qMax(width, minWidth) * qMax(height, minHeight) *
+             pvrHeader->bitsPerPixel) / 8;
+        if (size > bufferSize)
+            break;
+        glCompressedTexImage2D(GL_TEXTURE_2D, GLint(level), textureFormat,
+                               GLsizei(width), GLsizei(height), 0,
+                               GLsizei(size), buffer);
+        width /= 2;
+        height /= 2;
+        buffer += size;
+        ++level;
+    }
+
+    // Restore the default pixel alignment for later texture uploads.
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+    // Set the invert flag for the texture.  The "vertical flip"
+    // flag in PVR is the opposite sense to our sense of inversion.
+    if ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0)
+        options &= ~QGLContext::InvertedYBindOption;
+    else
+        options |= QGLContext::InvertedYBindOption;
+
+    return QSize(pvrHeader->width, pvrHeader->height);
+}
+
+#undef ctx
+
 QT_END_NAMESPACE