summaryrefslogtreecommitdiff
path: root/recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch')
-rw-r--r--recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch771
1 files changed, 771 insertions, 0 deletions
diff --git a/recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch b/recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch
new file mode 100644
index 0000000000..605a913cb7
--- /dev/null
+++ b/recipes/qt4/qt-4.6.0/0947-Compressed-texture-binding-for-QtOpenGL-ETC1-and-PVR.patch
@@ -0,0 +1,771 @@
+From 147195bccfdf90924a1525398e9c7b3119c1e278 Mon Sep 17 00:00:00 2001
+From: Rhys Weatherley <rhys.weatherley@nokia.com>
+Date: Thu, 3 Dec 2009 10:07:22 +1000
+Subject: [PATCH 0947/1244] Compressed texture binding for QtOpenGL: ETC1 and PVRTC
+
+The QGLContext::bindTexture(QString) function has been augmented
+with support for ETC1, PVRTC2, and PVRTC4 compressed textures,
+in addition to the existing DDS support.
+
+The QGLPixmapData class has also been modified to recognize
+compressed texture formats in fromFile() and fromData().
+
+This change also fixes a bug in bindTexture() that prevented
+the same compressed texture file from being bound in multiple
+contexts. There is now a separate file cache for each context group.
+
+Task-number: QT-2547
+Reviewed-by: Trond
+---
+ src/opengl/qgl.cpp | 485 +++++++++++++++++++++++++++++++----------
+ src/opengl/qgl_p.h | 16 ++-
+ src/opengl/qglextensions_p.h | 17 ++
+ src/opengl/qpixmapdata_gl.cpp | 57 +++++
+ src/opengl/qpixmapdata_gl_p.h | 4 +
+ 5 files changed, 461 insertions(+), 118 deletions(-)
+
+diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
+index 94b8aa5..b376901 100644
+--- a/src/opengl/qgl.cpp
++++ b/src/opengl/qgl.cpp
+@@ -127,18 +127,6 @@ Q_GLOBAL_STATIC(QGLDefaultOverlayFormat, defaultOverlayFormatInstance)
+ 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()
+ {
+@@ -1887,118 +1875,42 @@ void QGLContextPrivate::cleanup()
+ {
+ }
+
+-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.
++ Reads the compressed texture file \a fileName and generates a 2D GL
++ texture from it.
+
+- Only the DXT1, DXT3 and DXT5 DDS formats are supported.
++ 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.
+
+- Note that this will only work if the implementation supports the
+- \c GL_ARB_texture_compression and \c GL_EXT_texture_compression_s3tc
+- extensions.
++ 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)
+@@ -2593,17 +2505,20 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q
+ */
+ 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;
+ }
+ }
+@@ -4907,8 +4822,14 @@ void QGLExtensions::init_extensions()
+ glExtensions |= SampleBuffers;
+ if (extensions.contains("GL_SGIS_generate_mipmap"))
+ glExtensions |= GenerateMipmap;
+- if (extensions.contains("GL_EXT_texture_compression_s3tc"))
++ if (extensions.contains("GL_ARB_texture_compression"))
+ glExtensions |= TextureCompression;
++ if (extensions.contains("GL_EXT_texture_compression_s3tc"))
++ glExtensions |= DDSTextureCompression;
++ if (extensions.contains("GL_OES_compressed_ETC1_RGB8_texture"))
++ glExtensions |= ETC1TextureCompression;
++ if (extensions.contains("GL_IMG_texture_compression_pvrtc"))
++ glExtensions |= PVRTCTextureCompression;
+ if (extensions.contains("GL_ARB_fragment_program"))
+ glExtensions |= FragmentProgram;
+ if (extensions.contains("GL_ARB_texture_mirrored_repeat"))
+@@ -4951,12 +4872,6 @@ void QGLExtensions::init_extensions()
+
+ if (extensions.contains("GL_EXT_bgra"))
+ glExtensions |= BGRATextureFormat;
+-
+-
+- QGLContext cx(QGLFormat::defaultFormat());
+- if (glExtensions & TextureCompression) {
+- qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
+- }
+ }
+
+ /*
+@@ -5112,4 +5027,340 @@ void QGLSharedResourceGuard::setContext(const QGLContext *context)
+ }
+ }
+
++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.
++ 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
+diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
+index b2407ba..0f785a5 100644
+--- a/src/opengl/qgl_p.h
++++ b/src/opengl/qgl_p.h
+@@ -222,6 +222,8 @@ public:
+ class QGLContextResource;
+ class QGLSharedResourceGuard;
+
++typedef QHash<QString, GLuint> QGLDDSCache;
++
+ // QGLContextPrivate has the responsibility of creating context groups.
+ // QGLContextPrivate and QGLShareRegister will both maintain the reference counter and destroy
+ // context groups when needed.
+@@ -246,6 +248,7 @@ private:
+ QHash<QGLContextResource *, void *> m_resources;
+ QGLSharedResourceGuard *m_guards; // double-linked list of active guards.
+ QAtomicInt m_refs;
++ QGLDDSCache m_dds_cache;
+
+ void cleanupResources(const QGLContext *ctx);
+
+@@ -377,7 +380,10 @@ public:
+ PixelBufferObject = 0x00000800,
+ FramebufferBlit = 0x00001000,
+ NPOTTextures = 0x00002000,
+- BGRATextureFormat = 0x00004000
++ BGRATextureFormat = 0x00004000,
++ DDSTextureCompression = 0x00008000,
++ ETC1TextureCompression = 0x00010000,
++ PVRTCTextureCompression = 0x00020000
+ };
+ Q_DECLARE_FLAGS(Extensions, Extension)
+
+@@ -482,6 +488,14 @@ public:
+ QPixmapData* boundPixmap;
+ #endif
+
++ bool canBindCompressedTexture
++ (const char *buf, int len, const char *format, bool *hasAlpha);
++ QSize bindCompressedTexture
++ (const QString& fileName, const char *format = 0);
++ QSize bindCompressedTexture
++ (const char *buf, int len, const char *format = 0);
++ QSize bindCompressedTextureDDS(const char *buf, int len);
++ QSize bindCompressedTexturePVR(const char *buf, int len);
+ };
+
+ class QGLTextureCache {
+diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
+index 3510765..62e216c 100644
+--- a/src/opengl/qglextensions_p.h
++++ b/src/opengl/qglextensions_p.h
+@@ -184,6 +184,10 @@ typedef void (APIENTRY *_glBlitFramebufferEXT) (int srcX0, int srcY0, int srcX1,
+ typedef void (APIENTRY *_glRenderbufferStorageMultisampleEXT) (GLenum target, GLsizei samples,
+ GLenum internalformat, GLsizei width, GLsizei height);
+
++// ARB_texture_compression
++typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei,
++ GLsizei, GLint, GLsizei, const GLvoid *);
++
+ QT_BEGIN_NAMESPACE
+
+ struct QGLExtensionFuncs
+@@ -289,6 +293,11 @@ struct QGLExtensionFuncs
+ #endif
+ qt_glMapBufferARB = 0;
+ qt_glUnmapBufferARB = 0;
++
++#if !defined(QT_OPENGL_ES)
++ // Texture compression
++ qt_glCompressedTexImage2DARB = 0;
++#endif
+ }
+
+
+@@ -397,6 +406,10 @@ struct QGLExtensionFuncs
+ _glMapBufferARB qt_glMapBufferARB;
+ _glUnmapBufferARB qt_glUnmapBufferARB;
+
++#if !defined(QT_OPENGL_ES)
++ // Texture compression
++ _glCompressedTexImage2DARB qt_glCompressedTexImage2DARB;
++#endif
+ };
+
+
+@@ -732,6 +745,10 @@ struct QGLExtensionFuncs
+ #define glClearDepth glClearDepthf
+ #endif
+
++#if !defined(QT_OPENGL_ES)
++#define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB
++#endif
++
+ extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx);
+ bool qt_resolve_buffer_extensions(QGLContext *ctx);
+
+diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
+index ab17789..0299cea 100644
+--- a/src/opengl/qpixmapdata_gl.cpp
++++ b/src/opengl/qpixmapdata_gl.cpp
+@@ -53,6 +53,8 @@
+ #include <private/qpaintengineex_opengl2_p.h>
+
+ #include <qdesktopwidget.h>
++#include <qfile.h>
++#include <qimagereader.h>
+
+ QT_BEGIN_NAMESPACE
+
+@@ -407,6 +409,61 @@ void QGLPixmapData::fromImage(const QImage &image,
+ }
+ }
+
++bool QGLPixmapData::fromFile(const QString &filename, const char *format,
++ Qt::ImageConversionFlags flags)
++{
++ if (pixelType() == QPixmapData::BitmapType)
++ return QPixmapData::fromFile(filename, format, flags);
++ QFile file(filename);
++ if (!file.open(QIODevice::ReadOnly))
++ return false;
++ QByteArray data = file.peek(64);
++ bool alpha;
++ if (m_texture.canBindCompressedTexture
++ (data.constData(), data.size(), format, &alpha)) {
++ resize(0, 0);
++ data = file.readAll();
++ file.close();
++ QSize size = m_texture.bindCompressedTexture
++ (data.constData(), data.size(), format);
++ if (!size.isEmpty()) {
++ w = size.width();
++ h = size.height();
++ is_null = false;
++ d = 32;
++ m_hasAlpha = alpha;
++ m_source = QImage();
++ m_dirty = isValid();
++ return true;
++ }
++ return false;
++ }
++ fromImage(QImageReader(&file, format).read(), flags);
++ return !isNull();
++}
++
++bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
++ Qt::ImageConversionFlags flags)
++{
++ bool alpha;
++ const char *buf = reinterpret_cast<const char *>(buffer);
++ if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) {
++ resize(0, 0);
++ QSize size = m_texture.bindCompressedTexture(buf, int(len), format);
++ if (!size.isEmpty()) {
++ w = size.width();
++ h = size.height();
++ is_null = false;
++ d = 32;
++ m_hasAlpha = alpha;
++ m_source = QImage();
++ m_dirty = isValid();
++ return true;
++ }
++ }
++ return QPixmapData::fromData(buffer, len, format, flags);
++}
++
+ bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
+ {
+ Q_UNUSED(dx);
+diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
+index 8a13e03..007c52a 100644
+--- a/src/opengl/qpixmapdata_gl_p.h
++++ b/src/opengl/qpixmapdata_gl_p.h
+@@ -106,6 +106,10 @@ public:
+ // Re-implemented from QPixmapData:
+ void resize(int width, int height);
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
++ bool fromFile(const QString &filename, const char *format,
++ Qt::ImageConversionFlags flags);
++ bool fromData(const uchar *buffer, uint len, const char *format,
++ Qt::ImageConversionFlags flags);
+ void copy(const QPixmapData *data, const QRect &rect);
+ bool scroll(int dx, int dy, const QRect &rect);
+ void fill(const QColor &color);
+--
+1.6.5
+