Index: trunk/src/opengl/qgl.cpp
===================================================================
--- trunk/src/opengl/qgl.cpp	(revision 5)
+++ trunk/src/opengl/qgl.cpp	(revision 561)
@@ -2,5 +2,6 @@
 **
 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: Qt Software Information (qt-info@nokia.com)
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
 **
 ** This file is part of the QtOpenGL module of the Qt Toolkit.
@@ -21,8 +22,7 @@
 ** 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.
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** GNU General Public License Usage
@@ -34,6 +34,6 @@
 ** 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.
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
 ** $QT_END_LICENSE$
 **
@@ -60,4 +60,6 @@
 #endif
 
+#include <qdatetime.h>
+
 #include <stdlib.h> // malloc
 
@@ -66,15 +68,26 @@
 #include "qgl_p.h"
 
-#if defined(QT_OPENGL_ES_2)
+#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
 #include "gl2paintengineex/qpaintengineex_opengl2_p.h"
-#else
+#endif
+
+#ifndef QT_OPENGL_ES_2
 #include <private/qpaintengine_opengl_p.h>
 #endif
+
+#ifdef Q_WS_QWS
+#include <private/qglwindowsurface_qws_p.h>
+#endif
+
+#include <qglpixelbuffer.h>
+#include <qglframebufferobject.h>
 
 #include <private/qimage_p.h>
 #include <private/qpixmapdata_p.h>
 #include <private/qpixmapdata_gl_p.h>
+#include <private/qglpixelbuffer_p.h>
+#include <private/qwindowsurface_gl_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
 #include "qcolormap.h"
-#include "qcache.h"
 #include "qfile.h"
 #include "qlibrary.h"
@@ -92,5 +105,9 @@
 #endif
 
-QThreadStorage<QGLThreadContext *> qgl_context_storage;
+struct QGLThreadContext {
+    QGLContext *context;
+};
+
+static QThreadStorage<QGLThreadContext *> qgl_context_storage;
 
 Q_GLOBAL_STATIC(QGLFormat, qgl_default_format)
@@ -111,16 +128,4 @@
 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()
@@ -129,11 +134,80 @@
 }
 
+
+class QGLEngineSelector
+{
+public:
+    QGLEngineSelector() : engineType(QPaintEngine::MaxUser)
+    {
+    }
+
+    void setPreferredPaintEngine(QPaintEngine::Type type) {
+        if (type == QPaintEngine::OpenGL || type == QPaintEngine::OpenGL2)
+            engineType = type;
+    }
+
+    QPaintEngine::Type preferredPaintEngine() {
+#ifdef Q_WS_MAC
+        // The ATI X1600 driver for Mac OS X does not support return
+        // values from functions in GLSL. Since working around this in
+        // the GL2 engine would require a big, ugly rewrite, we're
+        // falling back to the GL 1 engine..
+        static bool mac_x1600_check_done = false;
+        if (!mac_x1600_check_done) {
+            QGLWidget *tmp = 0;
+            if (!QGLContext::currentContext()) {
+                tmp = new QGLWidget();
+                tmp->makeCurrent();
+            }
+            if (strstr((char *) glGetString(GL_RENDERER), "X1600"))
+                engineType = QPaintEngine::OpenGL;
+            if (tmp)
+                delete tmp;
+            mac_x1600_check_done = true;
+        }
+#endif
+        if (engineType == QPaintEngine::MaxUser) {
+            // No user-set engine - use the defaults
+#if defined(QT_OPENGL_ES_2)
+            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.
+            // 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
+                engineType = QPaintEngine::OpenGL;
+#endif
+        }
+        return engineType;
+    }
+
+private:
+    QPaintEngine::Type engineType;
+};
+
+Q_GLOBAL_STATIC(QGLEngineSelector, qgl_engine_selector)
+
+
+bool qt_gl_preferGL2Engine()
+{
+    return qgl_engine_selector()->preferredPaintEngine() == QPaintEngine::OpenGL2;
+}
+
+
 /*!
     \namespace QGL
+    \inmodule QtOpenGL
 
     \brief The QGL namespace specifies miscellaneous identifiers used
     in the Qt OpenGL module.
 
-    \ingroup multimedia
+    \ingroup painting-3D
 */
 
@@ -168,4 +242,30 @@
 */
 
+/*!
+   \fn void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType)
+
+   \since 4.6
+
+   Sets the preferred OpenGL paint engine that is used to draw onto
+   QGLWidget, QGLPixelBuffer and QGLFramebufferObject targets with QPainter
+   in Qt.
+
+   The \a engineType parameter specifies which of the GL engines to
+   use. Only \c QPaintEngine::OpenGL and \c QPaintEngine::OpenGL2 are
+   valid parameters to this function. All other values are ignored.
+
+   By default, the \c QPaintEngine::OpenGL2 engine is used if GL/GLES
+   version 2.0 is available, otherwise \c QPaintEngine::OpenGL is
+   used.
+
+   \warning This function must be called before the QApplication
+   constructor is called.
+*/
+void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType)
+{
+    qgl_engine_selector()->setPreferredPaintEngine(engineType);
+}
+
+
 /*****************************************************************************
   QGLFormat implementation
@@ -178,5 +278,5 @@
     rendering context.
 
-    \ingroup multimedia
+    \ingroup painting-3D
 
     A display format has several characteristics:
@@ -191,11 +291,12 @@
     \i \link setDirectRendering() Direct rendering.\endlink
     \i \link setOverlay() Presence of an overlay.\endlink
-    \i \link setPlane() The plane of an overlay format.\endlink
+    \i \link setPlane() Plane of an overlay.\endlink
     \i \link setSampleBuffers() Multisample buffers.\endlink
     \endlist
 
-    You can also specify preferred bit depths for the depth buffer,
-    alpha buffer, accumulation buffer and the stencil buffer with the
-    functions: setDepthBufferSize(), setAlphaBufferSize(),
+    You can also specify preferred bit depths for the color buffer,
+    depth buffer, alpha buffer, accumulation buffer and the stencil
+    buffer with the functions: setRedBufferSize(), setGreenBufferSize(),
+    setBlueBufferSize(), setDepthBufferSize(), setAlphaBufferSize(),
     setAccumBufferSize() and setStencilBufferSize().
 
@@ -237,4 +338,6 @@
     \sa QGLContext, QGLWidget
 */
+
+#ifndef QT_OPENGL_ES
 
 static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
@@ -280,6 +383,8 @@
 }
 
-/*!
-    Constructs a QGLFormat object with the factory default settings:
+#endif // !QT_OPENGL_ES
+
+/*!
+    Constructs a QGLFormat object with the following default settings:
     \list
     \i \link setDoubleBuffer() Double buffer:\endlink Enabled.
@@ -288,5 +393,5 @@
     \i \link setAlpha() Alpha channel:\endlink Disabled.
     \i \link setAccum() Accumulator buffer:\endlink Disabled.
-    \i \link setStencil() Stencil buffer:\endlink Disabled.
+    \i \link setStencil() Stencil buffer:\endlink Enabled.
     \i \link setStereo() Stereo:\endlink Disabled.
     \i \link setDirectRendering() Direct rendering:\endlink Enabled.
@@ -304,10 +409,10 @@
 
 /*!
-    Creates a QGLFormat object that is a copy of the current \link
-    defaultFormat() application default format\endlink.
-
-    If \a options is not 0, this copy is modified by these format
-    options. The \a options parameter should be \c FormatOption values
-    OR'ed together.
+    Creates a QGLFormat object that is a copy of the current
+    defaultFormat().
+
+    If \a options is not 0, the default format is modified by the
+    specified format options. The \a options parameter should be
+    QGL::FormatOption values OR'ed together.
 
     This constructor makes it easy to specify a certain desired format
@@ -315,7 +420,7 @@
     \snippet doc/src/snippets/code/src_opengl_qgl.cpp 3
 
-    Note that there are \c FormatOption values to turn format settings
-    both on and off, e.g. \c DepthBuffer and \c NoDepthBuffer,
-    \c DirectRendering and \c IndirectRendering, etc.
+    Note that there are QGL::FormatOption values to turn format settings
+    both on and off, e.g. QGL::DepthBuffer and QGL::NoDepthBuffer,
+    QGL::DirectRendering and QGL::IndirectRendering, etc.
 
     The \a plane parameter defaults to 0 and is the plane which this
@@ -323,5 +428,5 @@
     supports overlay/underlay rendering planes.
 
-    \sa defaultFormat(), setOption()
+    \sa defaultFormat(), setOption(), setPlane()
 */
 
@@ -337,4 +442,17 @@
 
 /*!
+    \internal
+*/
+void QGLFormat::detach()
+{
+    if (d->ref != 1) {
+        QGLFormatPrivate *newd = new QGLFormatPrivate(d);
+        if (!d->ref.deref())
+            delete d;
+        d = newd;
+    }
+}
+
+/*!
     Constructs a copy of \a other.
 */
@@ -342,6 +460,6 @@
 QGLFormat::QGLFormat(const QGLFormat &other)
 {
-    d = new QGLFormatPrivate;
-    *d = *other.d;
+    d = other.d;
+    d->ref.ref();
 }
 
@@ -352,5 +470,10 @@
 QGLFormat &QGLFormat::operator=(const QGLFormat &other)
 {
-    *d = *other.d;
+    if (d != other.d) {
+        other.d->ref.ref();
+        if (!d->ref.deref())
+            delete d;
+        d = other.d;
+    }
     return *this;
 }
@@ -361,5 +484,6 @@
 QGLFormat::~QGLFormat()
 {
-    delete d;
+    if (!d->ref.deref())
+        delete d;
 }
 
@@ -515,5 +639,5 @@
 
     Returns true if the stencil buffer is enabled; otherwise returns
-    false. The stencil buffer is disabled by default.
+    false. The stencil buffer is enabled by default.
 
     \sa setStencil(), setStencilBufferSize()
@@ -524,5 +648,5 @@
     disables the stencil buffer.
 
-    The stencil buffer is disabled by default.
+    The stencil buffer is enabled by default.
 
     The stencil buffer masks certain parts of the drawing area so that
@@ -637,4 +761,5 @@
 void QGLFormat::setSamples(int numSamples)
 {
+    detach();
     if (numSamples < 0) {
         qWarning("QGLFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples);
@@ -664,4 +789,5 @@
 void QGLFormat::setSwapInterval(int interval)
 {
+    detach();
     d->swapInterval = interval;
 }
@@ -709,5 +835,5 @@
     formats is 1, which is the first overlay plane.
 
-    \sa setPlane()
+    \sa setPlane(), defaultOverlayFormat()
 */
 int QGLFormat::plane() const
@@ -731,4 +857,5 @@
 void QGLFormat::setPlane(int plane)
 {
+    detach();
     d->pln = plane;
 }
@@ -742,4 +869,5 @@
 void QGLFormat::setOption(QGL::FormatOptions opt)
 {
+    detach();
     if (opt & 0xffff)
         d->opts |= opt;
@@ -771,4 +899,5 @@
 void QGLFormat::setDepthBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size);
@@ -797,4 +926,5 @@
 void QGLFormat::setRedBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setRedBufferSize: Cannot set negative red buffer size %d", size);
@@ -825,4 +955,5 @@
 void QGLFormat::setGreenBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size);
@@ -853,4 +984,5 @@
 void QGLFormat::setBlueBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size);
@@ -880,4 +1012,5 @@
 void QGLFormat::setAlphaBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size);
@@ -906,4 +1039,5 @@
 void QGLFormat::setAccumBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size);
@@ -930,4 +1064,5 @@
 void QGLFormat::setStencilBufferSize(int size)
 {
+    detach();
     if (size < 0) {
         qWarning("QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size);
@@ -1125,9 +1260,9 @@
             return defaultVersionFlags;
         } else {
-            cachedDefault = true;
             if (!hasOpenGL())
                 return defaultVersionFlags;
             dummy = new QGLWidget;
             dummy->makeCurrent(); // glGetString() needs a current context
+            cachedDefault = true;
         }
     }
@@ -1149,6 +1284,6 @@
 
 /*!
-    Returns the default QGLFormat for the application. All QGLWidgets
-    that are created use this format unless another format is
+    Returns the default QGLFormat for the application. All QGLWidget
+    objects that are created use this format unless another format is
     specified, e.g. when they are constructed.
 
@@ -1183,5 +1318,5 @@
     Returns the default QGLFormat for overlay contexts.
 
-    The factory default overlay format is:
+    The default overlay format is:
     \list
     \i \link setDoubleBuffer() Double buffer:\endlink Disabled.
@@ -1194,4 +1329,5 @@
     \i \link setDirectRendering() Direct rendering:\endlink Enabled.
     \i \link setOverlay() Overlay:\endlink Disabled.
+    \i \link setSampleBuffers() Multisample buffers:\endlink Disabled.
     \i \link setPlane() Plane:\endlink 1 (i.e., first overlay plane).
     \endlist
@@ -1236,6 +1372,8 @@
 
 /*!
-    Returns true if all the options of the two QGLFormats are equal;
-    otherwise returns false.
+    Returns true if all the options of the two QGLFormat objects
+    \a a and \a b are equal; otherwise returns false.
+
+    \relates QGLFormat
 */
 
@@ -1244,11 +1382,18 @@
     return (int) a.d->opts == (int) b.d->opts && a.d->pln == b.d->pln && a.d->alphaSize == b.d->alphaSize
         && a.d->accumSize == b.d->accumSize && a.d->stencilSize == b.d->stencilSize
-        && a.d->depthSize == b.d->depthSize;
-}
-
-
-/*!
-    Returns false if all the options of the two QGLFormats are equal;
-    otherwise returns true.
+        && a.d->depthSize == b.d->depthSize
+        && a.d->redSize == b.d->redSize
+        && a.d->greenSize == b.d->greenSize
+        && a.d->blueSize == b.d->blueSize
+        && a.d->numSamples == b.d->numSamples
+        && a.d->swapInterval == b.d->swapInterval;
+}
+
+
+/*!
+    Returns false if all the options of the two QGLFormat objects
+    \a a and \a b are equal; otherwise returns true.
+
+    \relates QGLFormat
 */
 
@@ -1261,4 +1406,43 @@
   QGLContext implementation
  *****************************************************************************/
+
+QGLContextGroup::~QGLContextGroup()
+{
+    // Clear any remaining QGLSharedResourceGuard objects on the group.
+    QGLSharedResourceGuard *guard = m_guards;
+    while (guard != 0) {
+        guard->m_group = 0;
+        guard->m_id = 0;
+        guard = guard->m_next;
+    }
+}
+
+void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard)
+{
+    if (m_guards)
+        m_guards->m_prev = guard;
+    guard->m_next = m_guards;
+    guard->m_prev = 0;
+    m_guards = guard;
+}
+
+void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard)
+{
+    if (guard->m_next)
+        guard->m_next->m_prev = guard->m_prev;
+    if (guard->m_prev)
+        guard->m_prev->m_next = guard->m_next;
+    else
+        m_guards = guard->m_next;
+}
+
+QGLContextPrivate::~QGLContextPrivate()
+{
+    if (!group->m_refs.deref()) {
+        Q_ASSERT(group->context() == q_ptr);
+        delete group;
+    }
+}
+
 void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
 {
@@ -1289,13 +1473,16 @@
 #if defined(QT_OPENGL_ES)
     eglContext = 0;
-#endif
-    pbo = 0;
+    eglSurface = EGL_NO_SURFACE;
+#endif
+    fbo = 0;
     crWin = false;
     initDone = false;
     sharing = false;
-    clear_on_painter_begin = true;
     max_texture_size = -1;
     version_flags_cached = false;
     version_flags = QGLFormat::OpenGL_Version_None;
+    current_fbo = 0;
+    default_fbo = 0;
+    active_engine = 0;
 }
 
@@ -1308,10 +1495,6 @@
 */
 
-QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
-{
-    QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32);
-    int w = size.width();
-    int h = size.height();
-    glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+static void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha)
+{
     if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
         // OpenGL gives RGBA; Qt wants ARGB
@@ -1328,5 +1511,5 @@
             // we shouldn't remove
             while (p < end) {
-                *p = 0xFF000000 | (*p>>8);
+                *p = 0xff000000 | (*p>>8);
                 ++p;
             }
@@ -1334,7 +1517,38 @@
     } else {
         // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB
-        img = img.rgbSwapped();
-    }
-    return img.mirrored();
+        for (int y = 0; y < h; y++) {
+            uint *q = (uint*)img.scanLine(y);
+            for (int x=0; x < w; ++x) {
+                const uint pixel = *q;
+                *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
+                q++;
+            }
+        }
+
+    }
+    img = img.mirrored();
+}
+
+QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha)
+{
+    QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32);
+    int w = size.width();
+    int h = size.height();
+    glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+    convertFromGLImage(img, w, h, alpha_format, include_alpha);
+    return img;
+}
+
+QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha)
+{
+    QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32);
+    int w = size.width();
+    int h = size.height();
+#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+    //### glGetTexImage not in GL ES 2.0, need to do something else here!
+    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+#endif
+    convertFromGLImage(img, w, h, alpha_format, include_alpha);
+    return img;
 }
 
@@ -1353,31 +1567,4 @@
 }
 
-class QGLTexture {
-public:
-    QGLTexture(const QGLContext *ctx, GLuint tx_id, GLenum tx_target, bool _clean = false)
-        : context(ctx), id(tx_id), target(tx_target), clean(_clean) {}
-    ~QGLTexture() {
-        if (clean) {
-            QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
-            QGLContext *ctx = const_cast<QGLContext *>(context);
-            bool switch_context = current && current != ctx && !qgl_share_reg()->checkSharing(current, ctx);
-            if (switch_context)
-                ctx->makeCurrent();
-            glDeleteTextures(1, &id);
-            if (switch_context)
-                current->makeCurrent();
-        }
-     }
-
-    const QGLContext *context;
-    GLuint id;
-    GLenum target;
-    bool clean;
-};
-
-typedef QCache<qint64, QGLTexture> QGLTextureCache;
-static int qt_tex_cache_limit = 64*1024; // cache ~64 MB worth of textures - this is not accurate though
-static QGLTextureCache *qt_tex_cache = 0;
-
 typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
 typedef void (*_qt_image_cleanup_hook_64)(qint64);
@@ -1385,4 +1572,125 @@
 extern Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64;
 extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;
+
+static QGLTextureCache *qt_gl_texture_cache = 0;
+
+QGLTextureCache::QGLTextureCache()
+    : m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though
+{
+    Q_ASSERT(qt_gl_texture_cache == 0);
+    qt_gl_texture_cache = this;
+
+    QImagePixmapCleanupHooks::instance()->addPixmapModificationHook(cleanupTextures);
+#ifdef Q_WS_X11
+    QImagePixmapCleanupHooks::instance()->addPixmapDestructionHook(cleanupPixmapSurfaces);
+#endif
+    QImagePixmapCleanupHooks::instance()->addImageHook(imageCleanupHook);
+}
+
+QGLTextureCache::~QGLTextureCache()
+{
+    qt_gl_texture_cache = 0;
+
+    QImagePixmapCleanupHooks::instance()->removePixmapModificationHook(cleanupTextures);
+#ifdef Q_WS_X11
+    QImagePixmapCleanupHooks::instance()->removePixmapDestructionHook(cleanupPixmapSurfaces);
+#endif
+    QImagePixmapCleanupHooks::instance()->removeImageHook(imageCleanupHook);
+}
+
+void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost)
+{
+    if (m_cache.totalCost() + cost > m_cache.maxCost()) {
+        // the cache is full - make an attempt to remove something
+        const QList<qint64> keys = m_cache.keys();
+        int i = 0;
+        while (i < m_cache.count()
+               && (m_cache.totalCost() + cost > m_cache.maxCost())) {
+            QGLTexture *tex = m_cache.object(keys.at(i));
+            if (tex->context == ctx)
+                m_cache.remove(keys.at(i));
+            ++i;
+        }
+    }
+    m_cache.insert(key, texture, cost);
+}
+
+bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId)
+{
+    QList<qint64> keys = m_cache.keys();
+    for (int i = 0; i < keys.size(); ++i) {
+        QGLTexture *tex = m_cache.object(keys.at(i));
+        if (tex->id == textureId && tex->context == ctx) {
+            tex->options |= QGLContext::MemoryManagedBindOption; // forces a glDeleteTextures() call
+            m_cache.remove(keys.at(i));
+            return true;
+        }
+    }
+    return false;
+}
+
+void QGLTextureCache::removeContextTextures(QGLContext* ctx)
+{
+    QList<qint64> keys = m_cache.keys();
+    for (int i = 0; i < keys.size(); ++i) {
+        const qint64 &key = keys.at(i);
+        if (m_cache.object(key)->context == ctx)
+            m_cache.remove(key);
+    }
+}
+
+QGLTextureCache* QGLTextureCache::instance()
+{
+    if (!qt_gl_texture_cache)
+        qt_gl_texture_cache = new QGLTextureCache;
+
+    return qt_gl_texture_cache;
+}
+
+/*
+  a hook that removes textures from the cache when a pixmap/image
+  is deref'ed
+*/
+void QGLTextureCache::imageCleanupHook(qint64 cacheKey)
+{
+    // ### remove when the GL texture cache becomes thread-safe
+    if (qApp->thread() != QThread::currentThread())
+        return;
+    QGLTexture *texture = instance()->getTexture(cacheKey);
+    if (texture && texture->options & QGLContext::MemoryManagedBindOption)
+        instance()->remove(cacheKey);
+}
+
+
+void QGLTextureCache::cleanupTextures(QPixmap* pixmap)
+{
+    // ### remove when the GL texture cache becomes thread-safe
+    if (qApp->thread() == QThread::currentThread()) {
+        const qint64 cacheKey = pixmap->cacheKey();
+        QGLTexture *texture = instance()->getTexture(cacheKey);
+        if (texture && texture->options & QGLContext::MemoryManagedBindOption)
+            instance()->remove(cacheKey);
+    }
+}
+
+#if defined(Q_WS_X11)
+void QGLTextureCache::cleanupPixmapSurfaces(QPixmap* pixmap)
+{
+    // Remove any bound textures first:
+    cleanupTextures(pixmap);
+
+    QPixmapData *pd = pixmap->data_ptr().data();
+    if (pd->classId() == QPixmapData::X11Class) {
+        Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken
+        QGLContextPrivate::destroyGlSurfaceForPixmap(pd);
+    }
+}
+#endif
+
+void QGLTextureCache::deleteIfEmpty()
+{
+    if (instance()->size() == 0)
+        delete instance();
+}
 
 // DDS format structure
@@ -1422,5 +1730,5 @@
 #endif
 
-Q_GLOBAL_STATIC(QGLShareRegister, _qgl_share_reg);
+Q_GLOBAL_STATIC(QGLShareRegister, _qgl_share_reg)
 Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg()
 {
@@ -1432,5 +1740,5 @@
     \brief The QGLContext class encapsulates an OpenGL rendering context.
 
-    \ingroup multimedia
+    \ingroup painting-3D
 
     An OpenGL rendering context is a complete set of OpenGL state
@@ -1457,4 +1765,45 @@
 */
 
+/*!
+    \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
+    through.
+
+    \value InvertedYBindOption Specifies that the texture should be flipped
+    over the X axis so that the texture coordinate 0,0 corresponds to
+    the top left corner. Inverting the texture implies a deep copy
+    prior to upload.
+
+    \value MipmapBindOption Specifies that bindTexture() should try
+    to generate mipmaps.  If the GL implementation supports the \c
+    GL_SGIS_generate_mipmap extension, mipmaps will be automatically
+    generated for the texture. Mipmap generation is only supported for
+    the \c GL_TEXTURE_2D target.
+
+    \value PremultipliedAlphaBindOption Specifies that the image should be
+    uploaded with premultiplied alpha and does a conversion accordingly.
+
+    \value LinearFilteringBindOption Specifies that the texture filtering
+    should be set to GL_LINEAR. Default is GL_NEAREST. If mipmap is
+    also enabled, filtering will be set to GL_LINEAR_MIPMAP_LINEAR.
+
+    \value DefaultBindOption In Qt 4.5 and earlier, bindTexture()
+    would mirror the image and automatically generate mipmaps. This
+    option helps preserve this default behavior.
+
+    \omitvalue CanFlipNativePixmapBindOption Used by x11 from pixmap to choose
+    wether or not it can bind the pixmap upside down or not.
+
+    \omitvalue MemoryManagedBindOption Used by paint engines to
+    indicate that the pixmap should be memory managed along side with
+    the pixmap/image that it stems from, e.g. installing destruction
+    hooks in them.
+
+    \omitvalue InternalBindOption
+*/
 
 /*!
@@ -1477,6 +1826,6 @@
 
 QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device)
-{
-    d_ptr = new QGLContextPrivate(this);
+    : d_ptr(new QGLContextPrivate(this))
+{
     Q_D(QGLContext);
     d->init(device, format);
@@ -1500,6 +1849,6 @@
 */
 QGLContext::QGLContext(const QGLFormat &format)
-{
-    d_ptr = new QGLContextPrivate(this);
+    : d_ptr(new QGLContextPrivate(this))
+{
     Q_D(QGLContext);
     d->init(0, format);
@@ -1512,51 +1861,33 @@
 QGLContext::~QGLContext()
 {
-    Q_D(QGLContext);
     // remove any textures cached in this context
-    if (qt_tex_cache) {
-        QList<qint64> keys = qt_tex_cache->keys();
-        for (int i = 0; i < keys.size(); ++i) {
-            const qint64 &key = keys.at(i);
-            if (qt_tex_cache->object(key)->context == this)
-                qt_tex_cache->remove(key);
-        }
-        // ### thread safety
-        if (qt_tex_cache->size() == 0) {
-            qt_pixmap_cleanup_hook_64 = 0;
-            qt_image_cleanup_hook_64 = 0;
-            delete qt_tex_cache;
-            qt_tex_cache = 0;
-        }
-    }
+    QGLTextureCache::instance()->removeContextTextures(this);
+    QGLTextureCache::deleteIfEmpty(); // ### thread safety
+
+    d_ptr->group->cleanupResources(this);
 
     QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
     reset();
-    delete d;
 }
 
 void QGLContextPrivate::cleanup()
 {
-    Q_Q(QGLContext);
-    if (pbo) {
-        QGLContext *ctx = q;
-        glDeleteBuffersARB(1, &pbo);
-        pbo = 0;
-    }
-}
-
-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()
@@ -1565,168 +1896,131 @@
 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.");
+    QGLTexture texture(this);
+    QSize size = texture.bindCompressedTexture(fileName);
+    if (!size.isValid())
         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.");
-        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;
-}
-
-/*
-  a hook that removes textures from the cache when a pixmap/image
-  is deref'ed
-*/
-static void qt_gl_clean_cache(qint64 cacheKey)
-{
-    // ### remove when the GL texture cache becomes thread-safe
-    if (qApp->thread() != QThread::currentThread())
-        return;
-    if (qt_tex_cache) {
-        QGLTexture *texture = qt_tex_cache->object(cacheKey);
-        if (texture && texture->clean)
-            qt_tex_cache->remove(cacheKey);
-    }
+
+    dds_cache->insert(fileName, texture.id);
+    return texture.id;
+}
+
+static inline QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format)
+{
+    if (texture_format == GL_BGRA) {
+        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+            return ((src_pixel << 24) & 0xff000000)
+                   | ((src_pixel >> 24) & 0x000000ff)
+                   | ((src_pixel << 8) & 0x00ff0000)
+                   | ((src_pixel >> 8) & 0x0000ff00);
+        } else {
+            return src_pixel;
+        }
+    } else {  // GL_RGBA
+        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+            return (src_pixel << 8) | ((src_pixel >> 24) & 0xff);
+        } else {
+            return ((src_pixel << 16) & 0xff0000)
+                   | ((src_pixel >> 16) & 0xff)
+                   | (src_pixel & 0xff00ff00);
+        }
+    }
+}
+
+QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format)
+{
+    return qt_gl_convertToGLFormatHelper(src_pixel, texture_format);
 }
 
 static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format)
 {
-    Q_ASSERT(dst.size() == img.size());
     Q_ASSERT(dst.depth() == 32);
     Q_ASSERT(img.depth() == 32);
 
-    const int width = img.width();
-    const int height = img.height();
-    const uint *p = (const uint*) img.scanLine(img.height() - 1);
-    uint *q = (uint*) dst.scanLine(0);
-
-    if (texture_format == GL_BGRA) {
-        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
-            // mirror + swizzle
-            for (int i=0; i < height; ++i) {
-                const uint *end = p + width;
-                while (p < end) {
-                    *q = ((*p << 24) & 0xff000000)
-                         | ((*p >> 24) & 0x000000ff)
-                         | ((*p << 8) & 0x00ff0000)
-                         | ((*p >> 8) & 0x0000ff00);
-                    p++;
-                    q++;
+    if (dst.size() != img.size()) {
+        int target_width = dst.width();
+        int target_height = dst.height();
+        qreal sx = target_width / qreal(img.width());
+        qreal sy = target_height / qreal(img.height());
+
+        quint32 *dest = (quint32 *) dst.scanLine(0); // NB! avoid detach here
+        uchar *srcPixels = (uchar *) img.scanLine(img.height() - 1);
+        int sbpl = img.bytesPerLine();
+        int dbpl = dst.bytesPerLine();
+
+        int ix = int(0x00010000 / sx);
+        int iy = int(0x00010000 / sy);
+
+        quint32 basex = int(0.5 * ix);
+        quint32 srcy = int(0.5 * iy);
+
+        // scale, swizzle and mirror in one loop
+        while (target_height--) {
+            const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl);
+            int srcx = basex;
+            for (int x=0; x<target_width; ++x) {
+                dest[x] = qt_gl_convertToGLFormatHelper(src[srcx >> 16], texture_format);
+                srcx += ix;
+            }
+            dest = (quint32 *)(((uchar *) dest) + dbpl);
+            srcy += iy;
+        }
+    } else {
+        const int width = img.width();
+        const int height = img.height();
+        const uint *p = (const uint*) img.scanLine(img.height() - 1);
+        uint *q = (uint*) dst.scanLine(0);
+
+        if (texture_format == GL_BGRA) {
+            if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+                // mirror + swizzle
+                for (int i=0; i < height; ++i) {
+                    const uint *end = p + width;
+                    while (p < end) {
+                        *q = ((*p << 24) & 0xff000000)
+                             | ((*p >> 24) & 0x000000ff)
+                             | ((*p << 8) & 0x00ff0000)
+                             | ((*p >> 8) & 0x0000ff00);
+                        p++;
+                        q++;
+                    }
+                    p -= 2 * width;
                 }
-                p -= 2 * width;
+            } else {
+                const uint bytesPerLine = img.bytesPerLine();
+                for (int i=0; i < height; ++i) {
+                    memcpy(q, p, bytesPerLine);
+                    q += width;
+                    p -= width;
+                }
             }
         } else {
-            const uint bytesPerLine = img.bytesPerLine();
-            for (int i=0; i < height; ++i) {
-                memcpy(q, p, bytesPerLine);
-                q += width;
-                p -= width;
-            }
-        }
-    } else {
-        if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
-            for (int i=0; i < height; ++i) {
-                const uint *end = p + width;
-                while (p < end) {
-                    *q = (*p << 8) | ((*p >> 24) & 0xFF);
-                    p++;
-                    q++;
+            if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+                for (int i=0; i < height; ++i) {
+                    const uint *end = p + width;
+                    while (p < end) {
+                        *q = (*p << 8) | ((*p >> 24) & 0xff);
+                        p++;
+                        q++;
+                    }
+                    p -= 2 * width;
                 }
-                p -= 2 * width;
-            }
-        } else {
-            for (int i=0; i < height; ++i) {
-                const uint *end = p + width;
-                while (p < end) {
-                    *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
-                    p++;
-                    q++;
+            } else {
+                for (int i=0; i < height; ++i) {
+                    const uint *end = p + width;
+                    while (p < end) {
+                        *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
+                        p++;
+                        q++;
+                    }
+                    p -= 2 * width;
                 }
-                p -= 2 * width;
             }
         }
@@ -1746,52 +2040,105 @@
 }
 
-GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
-                                      const qint64 key, bool clean)
+/*! \internal */
+QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
+                                           QGLContext::BindOptions options)
+{
+    const qint64 key = image.cacheKey();
+    QGLTexture *texture = textureCacheLookup(key, target);
+    if (texture) {
+        glBindTexture(target, texture->id);
+        return texture;
+    }
+
+    if (!texture)
+        texture = bindTexture(image, target, format, key, options);
+    // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+    Q_ASSERT(texture);
+
+    if (texture->id > 0)
+        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)
 {
     Q_Q(QGLContext);
 
-    QGLContext *ctx = q;
-
-    bool use_pbo = false;
-    if (QGLExtensions::glExtensions & QGLExtensions::PixelBufferObject) {
-
-        use_pbo = qt_resolve_buffer_extensions(ctx);
-        if (use_pbo && pbo == 0)
-            glGenBuffersARB(1, &pbo);
-    }
-
-    // the GL_BGRA format is only present in GL version >= 1.2
-    GLenum texture_format = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
-                            ? GL_BGRA : GL_RGBA;
-    if (!qt_tex_cache) {
-        qt_tex_cache = new QGLTextureCache(qt_tex_cache_limit);
-        qt_pixmap_cleanup_hook_64 = qt_gl_clean_cache;
-        qt_image_cleanup_hook_64 = qt_gl_clean_cache;
-    }
+#ifdef QGL_BIND_TEXTURE_DEBUG
+    printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x\n",
+           image.width(), image.height(), internalFormat, int(options));
+    QTime time;
+    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).
+    // 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
     int tx_w = qt_next_power_of_two(image.width());
     int tx_h = qt_next_power_of_two(image.height());
 
-    // Note: the clean param is only true when a texture is bound
-    // from the QOpenGLPaintEngine - in that case we have to force
-    // a premultiplied texture format
     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 = image.scaled(tx_w, tx_h);
-    }
+        img = img.scaled(tx_w, tx_h);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+        printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed());
+
+#endif
+    }
+
+    GLuint filtering = options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST;
 
     GLuint tx_id;
     glGenTextures(1, &tx_id);
     glBindTexture(target, tx_id);
-    glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameterf(target, GL_TEXTURE_MAG_FILTER, filtering);
+
+#if defined(QT_OPENGL_ES_2)
+    bool genMipmap = false;
+#endif
     if (glFormat.directRendering()
-        && QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap
-        && target == GL_TEXTURE_2D && !clean)
+        && (QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap)
+        && target == GL_TEXTURE_2D
+        && (options & QGLContext::MipmapBindOption))
     {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+        printf(" - generating mipmaps (%d ms)\n", time.elapsed());
+#endif
+#if !defined(QT_OPENGL_ES_2)
         glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
 #ifndef QT_OPENGL_ES
@@ -1800,105 +2147,199 @@
         glTexParameterf(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
 #endif
-        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
-
-        // Mipmap generation causes huge slowdown with PBO's for some reason
-        use_pbo = false;
+#else
+        glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
+        genMipmap = true;
+#endif
+        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, options & QGLContext::LinearFilteringBindOption
+                        ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST);
     } else {
-        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    }
-
-    uchar *ptr = 0;
-    if (use_pbo) {
-        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
-        glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, img.width() * img.height() * 4, 0, GL_STREAM_DRAW_ARB);
-        ptr = reinterpret_cast<uchar *>(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB));
-    }
-
-    if (ptr) {
-        QImage::Format target_format = img.format();
-        if (clean || img.format() != QImage::Format_ARGB32)
-            target_format = QImage::Format_ARGB32_Premultiplied;
-
-        QImage buffer(ptr, img.width(), img.height(), target_format);
-        convertToGLFormatHelper(buffer, img.convertToFormat(target_format), texture_format);
-        glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
-        glTexImage2D(target, 0, format, img.width(), img.height(), 0, texture_format, GL_UNSIGNED_BYTE, 0);
+        glTexParameterf(target, GL_TEXTURE_MIN_FILTER, filtering);
+    }
+
+    QImage::Format target_format = img.format();
+    bool premul = options & QGLContext::PremultipliedAlphaBindOption;
+    GLenum externalFormat;
+    GLuint pixel_type;
+    if (QGLExtensions::glExtensions & QGLExtensions::BGRATextureFormat) {
+        externalFormat = GL_BGRA;
+        pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
     } else {
-        QImage tx = convertToGLFormat(img, clean, texture_format);
-        glTexImage2D(target, 0, format, tx.width(), tx.height(), 0, texture_format,
-                     GL_UNSIGNED_BYTE, tx.bits());
-    }
-
-    if (use_pbo)
-        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+        externalFormat = GL_RGBA;
+        pixel_type = GL_UNSIGNED_BYTE;
+    }
+
+    switch (target_format) {
+    case QImage::Format_ARGB32:
+        if (premul) {
+            img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed());
+#endif
+        }
+        break;
+    case QImage::Format_ARGB32_Premultiplied:
+        if (!premul) {
+            img = img.convertToFormat(target_format = QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed());
+#endif
+        }
+        break;
+    case QImage::Format_RGB16:
+        pixel_type = GL_UNSIGNED_SHORT_5_6_5;
+        externalFormat = GL_RGB;
+        internalFormat = GL_RGB;
+        break;
+    case QImage::Format_RGB32:
+        break;
+    default:
+        if (img.hasAlphaChannel()) {
+            img = img.convertToFormat(premul
+                                      ? QImage::Format_ARGB32_Premultiplied
+                                      : QImage::Format_ARGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed());
+#endif
+        } else {
+            img = img.convertToFormat(QImage::Format_RGB32);
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - converting to 32-bit (%d ms)\n", time.elapsed());
+#endif
+        }
+    }
+
+    if (options & QGLContext::InvertedYBindOption) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - flipping bits over y (%d ms)\n", time.elapsed());
+#endif
+        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();
+        }
+    }
+
+    if (externalFormat == GL_RGBA) {
+#ifdef QGL_BIND_TEXTURE_DEBUG
+            printf(" - doing byte swapping (%d ms)\n", time.elapsed());
+#endif
+        // The only case where we end up with a depth different from
+        // 32 in the switch above is for the RGB16 case, where we set
+        // the format to GL_RGB
+        Q_ASSERT(img.depth() == 32);
+        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);
+#endif
+
+    const QImage &constRef = img; // to avoid detach in bits()...
+    glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat,
+                 pixel_type, constRef.bits());
+#if defined(QT_OPENGL_ES_2)
+    if (genMipmap)
+        glGenerateMipmap(target);
+#endif
+#ifndef QT_NO_DEBUG
+    GLenum error = glGetError();
+    if (error != GL_NO_ERROR) {
+        qWarning(" - texture upload failed, error code 0x%x\n", error);
+    }
+#endif
+
+#ifdef QGL_BIND_TEXTURE_DEBUG
+    static int totalUploadTime = 0;
+    totalUploadTime += time.elapsed();
+    printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime);
+#endif
+
 
     // this assumes the size of a texture is always smaller than the max cache size
     int cost = img.width()*img.height()*4/1024;
-    if (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost()) {
-        // the cache is full - make an attempt to remove something
-        const QList<qint64> keys = qt_tex_cache->keys();
-        int i = 0;
-        while (i < qt_tex_cache->count()
-               && (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost())) {
-            QGLTexture *tex = qt_tex_cache->object(keys.at(i));
-            if (tex->context == q)
-                qt_tex_cache->remove(keys.at(i));
-            ++i;
+    QGLTexture *texture = new QGLTexture(q, tx_id, target, options);
+    QGLTextureCache::instance()->insert(q, key, texture, cost);
+    return texture;
+}
+
+QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target)
+{
+    Q_Q(QGLContext);
+    QGLTexture *texture = QGLTextureCache::instance()->getTexture(key);
+    if (texture && texture->target == target
+        && (texture->context == q || QGLContext::areSharing(q, texture->context)))
+    {
+        return texture;
+    }
+    return 0;
+}
+
+
+/*! \internal */
+QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, QGLContext::BindOptions options)
+{
+    Q_Q(QGLContext);
+    QPixmapData *pd = pixmap.pixmapData();
+#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+    if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) {
+        const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd);
+
+        if (data->isValidContext(q)) {
+            data->bind();
+            return data->texture();
         }
     }
-    qt_tex_cache->insert(key, new QGLTexture(q, tx_id, target, clean), cost);
-    return tx_id;
-}
-
-bool QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target, GLuint *id)
-{
-    Q_Q(QGLContext);
-    if (qt_tex_cache) {
-        QGLTexture *texture = qt_tex_cache->object(key);
-        if (texture && texture->target == target
-            && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context)))
-        {
-            *id = texture->id;
-            return true;
+#else
+    Q_UNUSED(pd);
+    Q_UNUSED(q);
+#endif
+
+    const qint64 key = pixmap.cacheKey();
+    QGLTexture *texture = textureCacheLookup(key, target);
+    if (texture) {
+        glBindTexture(target, texture->id);
+        return texture;
+    }
+
+#if defined(Q_WS_X11)
+    // Try to use texture_from_pixmap
+    if (pd->classId() == QPixmapData::X11Class && pd->pixelType() == QPixmapData::PixmapType) {
+        texture = bindTextureFromNativePixmap(pd, key, options);
+        if (texture) {
+            texture->options |= QGLContext::MemoryManagedBindOption;
+            texture->boundPixmap = pd;
+            boundPixmaps.insert(pd, QPixmap(pixmap));
         }
     }
-    return false;
-}
-
-/*! \internal */
-GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, bool clean)
-{
-    const qint64 key = image.cacheKey();
-    GLuint id;
-    if (textureCacheLookup(key, target, &id)) {
-        glBindTexture(target, id);
-        return id;
-    }
-    GLuint cached = bindTexture(image, target, format, key, clean);
-    const_cast<QImage &>(image).data_ptr()->is_cached = (cached > 0);
-    return cached;
-}
-
-/*! \internal */
-GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean)
-{
-#if !defined(QT_OPENGL_ES_2)
-    if (target == qt_gl_preferredTextureTarget() && pixmap.pixmapData()->classId() == QPixmapData::OpenGLClass) {
-        const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pixmap.pixmapData());
-
-        if (data->isValidContext(QGLContext::currentContext()))
-            return data->bind();
-    }
-#endif
-
-    const qint64 key = pixmap.cacheKey();
-    GLuint id;
-    if (textureCacheLookup(key, target, &id)) {
-        glBindTexture(target, id);
-        return id;
-    }
-    GLuint cached = bindTexture(pixmap.toImage(), target, format, key, clean);
-    const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = (cached > 0);
-    return cached;
+#endif
+
+    if (!texture)
+        texture = bindTexture(pixmap.toImage(), target, format, key, options);
+    // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+    Q_ASSERT(texture);
+
+    if (texture->id > 0)
+        QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
+
+    return texture;
 }
 
@@ -1939,4 +2380,23 @@
 
 /*!
+  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 \c glBindTexture() calls.
+
+  \overload
+*/
+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;
+}
+
+/*!
+    \since 4.6
+
     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
@@ -1947,10 +2407,8 @@
 
     The \a format parameter sets the internal format for the
-    texture. The default format is \c GL_RGBA8.
-
-    If the GL implementation supports the \c GL_SGIS_generate_mipmap
-    extension, mipmaps will be automatically generated for the
-    texture. Mipmap generation is only supported for the \c
-    GL_TEXTURE_2D target.
+    texture. The default format is \c GL_RGBA.
+
+    The binding \a options are a set of options used to decide how to
+    bind the texture to the context.
 
     The texture that is generated is cached, so multiple calls to
@@ -1963,8 +2421,12 @@
     \sa deleteTexture()
 */
-GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
-{
+GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options)
+{
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
-    return d->bindTexture(image, target, format, false);
+    QGLTexture *texture = d->bindTexture(image, target, format, false, options);
+    return texture->id;
 }
 
@@ -1973,6 +2435,22 @@
 GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
 {
+    if (image.isNull())
+        return 0;
+
     Q_D(QGLContext);
-    return d->bindTexture(image, GLenum(target), GLint(format), false);
+    QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, DefaultBindOption);
+    return texture->id;
+}
+
+/*! \internal */
+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;
 }
 #endif
@@ -1984,6 +2462,27 @@
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
-    return d->bindTexture(pixmap, target, format, false);
+    QGLTexture *texture = d->bindTexture(pixmap, target, format, DefaultBindOption);
+    return texture->id;
+}
+
+/*!
+  \overload
+  \since 4.6
+
+  Generates and binds a 2D GL texture to the current context, based
+  on \a pixmap.
+*/
+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;
 }
 
@@ -1992,6 +2491,21 @@
 GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
 {
+    if (pixmap.isNull())
+        return 0;
+
     Q_D(QGLContext);
-    return d->bindTexture(pixmap, GLenum(target), GLint(format), false);
+    QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), DefaultBindOption);
+    return texture->id;
+}
+/*! \internal */
+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;
 }
 #endif
@@ -2006,24 +2520,18 @@
 void QGLContext::deleteTexture(GLuint id)
 {
-    if (qt_tex_cache) {
-        QList<qint64> keys = qt_tex_cache->keys();
-        for (int i = 0; i < keys.size(); ++i) {
-            QGLTexture *tex = qt_tex_cache->object(keys.at(i));
-            if (tex->id == id && tex->context == this) {
-                tex->clean = true; // forces a glDeleteTextures() call
-                qt_tex_cache->remove(keys.at(i));
-                return;
-            }
-        }
-    }
+    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;
         }
@@ -2039,10 +2547,34 @@
 #endif
 
-// qpaintengine_opengl.cpp
+void qt_add_rect_to_array(const QRectF &r, q_vertexType *array)
+{
+    qreal left = r.left();
+    qreal right = r.right();
+    qreal top = r.top();
+    qreal bottom = r.bottom();
+
+    array[0] = f2vt(left);
+    array[1] = f2vt(top);
+    array[2] = f2vt(right);
+    array[3] = f2vt(top);
+    array[4] = f2vt(right);
+    array[5] = f2vt(bottom);
+    array[6] = f2vt(left);
+    array[7] = f2vt(bottom);
+}
+
+void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array)
+{
+    array[0] = f2vt(x1);
+    array[1] = f2vt(y1);
+    array[2] = f2vt(x2);
+    array[3] = f2vt(y1);
+    array[4] = f2vt(x2);
+    array[5] = f2vt(y2);
+    array[6] = f2vt(x1);
+    array[7] = f2vt(y2);
+}
+
 #if !defined(QT_OPENGL_ES_2)
-extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
-#else
-void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) {};
-#endif
 
 static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget)
@@ -2074,5 +2606,4 @@
     qt_add_rect_to_array(target, vertexArray);
 
-#if !defined(QT_OPENGL_ES_2)
     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
@@ -2084,6 +2615,7 @@
     glDisableClientState(GL_VERTEX_ARRAY);
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-#endif
-}
+}
+
+#endif // !QT_OPENGL_ES_2
 
 /*!
@@ -2094,8 +2626,9 @@
     texture target.
 
-    Equivalent to the corresponding QGLContext::drawTexture().
+    \note This function is not supported under OpenGL/ES 2.0.
 */
 void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget)
 {
+#ifndef QT_OPENGL_ES_2
 #ifdef QT_OPENGL_ES
     if (textureTarget != GL_TEXTURE_2D) {
@@ -2121,4 +2654,10 @@
     glBindTexture(textureTarget, oldTexture);
 #endif
+#else
+    Q_UNUSED(target);
+    Q_UNUSED(textureId);
+    Q_UNUSED(textureTarget);
+    qWarning("drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES/2.0");
+#endif
 }
 
@@ -2137,5 +2676,5 @@
     space. The \a textureTarget should be a 2D texture target.
 
-    Equivalent to the corresponding QGLContext::drawTexture().
+    \note This function is not supported under OpenGL/ES.
 */
 void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
@@ -2188,7 +2727,5 @@
 void QGLContext::setTextureCacheLimit(int size)
 {
-    qt_tex_cache_limit = size;
-    if (qt_tex_cache)
-        qt_tex_cache->setMaxCost(qt_tex_cache_limit);
+    QGLTextureCache::instance()->setMaxCost(size);
 }
 
@@ -2200,5 +2737,5 @@
 int QGLContext::textureCacheLimit()
 {
-    return qt_tex_cache_limit;
+    return QGLTextureCache::instance()->maxCost();
 }
 
@@ -2279,4 +2816,18 @@
     formats.
 */
+
+/*!
+    Returns true if \a context1 and \a context2 are sharing their
+    GL resources such as textures, shader programs, etc;
+    otherwise returns false.
+
+    \since 4.6
+*/
+bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2)
+{
+    if (!context1 || !context2)
+        return false;
+    return context1->d_ptr->group == context2->d_ptr->group;
+}
 
 /*!
@@ -2399,4 +2950,8 @@
     reset();
     d->valid = chooseContext(shareContext);
+    if (d->valid && d->paintDevice->devType() == QInternal::Widget) {
+        QWidgetPrivate *wd = qt_widget_private(static_cast<QWidget *>(d->paintDevice));
+        wd->usesDoubleBufferedGLContext = d->glFormat.doubleBuffer();
+    }
     if (d->sharing)  // ok, we managed to share
         qgl_share_reg()->addShare(this, shareContext);
@@ -2419,5 +2974,5 @@
 {
     Q_D(const QGLContext);
-    return d->sharing;
+    return d->group->isSharing();
 }
 
@@ -2474,7 +3029,24 @@
 const QGLContext* QGLContext::currentContext()
 {
-    if (qgl_context_storage.hasLocalData())
-        return qgl_context_storage.localData()->context;
+    QGLThreadContext *threadContext = qgl_context_storage.localData();
+    if (threadContext)
+        return threadContext->context;
     return 0;
+}
+
+void QGLContextPrivate::setCurrentContext(QGLContext *context)
+{
+    QGLThreadContext *threadContext = qgl_context_storage.localData();
+    if (!threadContext) {
+        if (!QThread::currentThread()) {
+            // We don't have a current QThread, so just set the static.
+            QGLContext::currentCtx = context;
+            return;
+        }
+        threadContext = new QGLThreadContext;
+        qgl_context_storage.setLocalData(threadContext);
+    }
+    threadContext->context = context;
+    QGLContext::currentCtx = context; // XXX: backwards-compat, not thread-safe
 }
 
@@ -2493,4 +3065,40 @@
 */
 
+/*! \fn int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc)
+
+    \bold{Win32 only:} This virtual function chooses a pixel format
+    that matches the OpenGL \link setFormat() format\endlink.
+    Reimplement this function in a subclass if you need a custom
+    context.
+
+    \warning The \a dummyPfd pointer and \a pdc are used as a \c
+    PIXELFORMATDESCRIPTOR*. We use \c void to avoid using
+    Windows-specific types in our header files.
+
+    \sa chooseContext()
+*/
+
+/*! \fn void *QGLContext::chooseVisual()
+
+  \bold{X11 only:} This virtual function tries to find a visual that
+  matches the format, reducing the demands if the original request
+  cannot be met.
+
+  The algorithm for reducing the demands of the format is quite
+  simple-minded, so override this method in your subclass if your
+  application has spcific requirements on visual selection.
+
+  \sa chooseContext()
+*/
+
+/*! \fn void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
+  \internal
+
+  \bold{X11 only:} This virtual function chooses a visual that matches
+  the OpenGL \link format() format\endlink. Reimplement this function
+  in a subclass if you need a custom visual.
+
+  \sa chooseContext()
+*/
 
 /*!
@@ -2561,6 +3169,6 @@
     \brief The QGLWidget class is a widget for rendering OpenGL graphics.
 
-    \ingroup multimedia
-    \mainclass
+    \ingroup painting-3D
+
 
     QGLWidget provides functionality for displaying OpenGL graphics
@@ -2577,5 +3185,5 @@
     needs to be updated.
     \i resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets
-    called whenever the the widget has been resized (and also when it
+    called whenever the widget has been resized (and also when it
     is shown for the first time because all newly created widgets get a
     resize event automatically).
@@ -2603,5 +3211,5 @@
     customized rendering \link QGLContext contexts\endlink.
 
-    You can also share OpenGL display lists between QGLWidgets (see
+    You can also share OpenGL display lists between QGLWidget objects (see
     the documentation of the QGLWidget constructors for details).
 
@@ -2885,5 +3493,5 @@
     Returns true if this widget's GL context is shared with another GL
     context, otherwise false is returned. Context sharing might not be
-    possible if the QGLWidgets use different formats.
+    possible if the widgets use different formats.
 
     \sa format()
@@ -2983,5 +3591,5 @@
 
   This method will try to keep display list and texture object sharing
-  in effect with other QGLWidgets, but changing the format might make
+  in effect with other QGLWidget objects, but changing the format might make
   sharing impossible. Use isSharing() to see if sharing is still in
   effect.
@@ -3009,9 +3617,8 @@
 
 /*
+  \fn void QGLWidget::setContext(QGLContext *context,
+                                 const QGLContext* shareContext,
+                                 bool deleteOldContext)
   \obsolete
-
-  \fn void QGLWidget::setContext(QGLContext *context,
-                                  const QGLContext* shareContext,
-                                  bool deleteOldContext)
 
   Sets a new context for this widget. The QGLContext \a context must
@@ -3163,7 +3770,8 @@
 }
 
-
+/*! \fn bool QGLWidget::event(QEvent *e)
+  \reimp
+*/
 #if !defined(Q_OS_WINCE) && !defined(Q_WS_QWS)
-/*! \reimp */
 bool QGLWidget::event(QEvent *e)
 {
@@ -3191,19 +3799,32 @@
         doneCurrent();
     } else if (e->type() == QEvent::ParentChange) {
-        if (d->glcx->d_func()->screen != d->xinfo.screen()) {
+        // if we've reparented a window that has the current context
+        // bound, we need to rebind that context to the new window id
+        if (d->glcx == QGLContext::currentContext())
+            makeCurrent();
+
+        if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) {
             setContext(new QGLContext(d->glcx->requestedFormat(), this));
             // ### recreating the overlay isn't supported atm
         }
+    }
+
 #if defined(QT_OPENGL_ES)
-        // The window may have been re-created during re-parent - if so, the EGL
+    // 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.
         d->recreateEglSurface(false);
-#endif
-    }
+    }
+#endif
 #elif defined(Q_WS_WIN)
     if (e->type() == QEvent::ParentChange) {
         QGLContext *newContext = new QGLContext(d->glcx->requestedFormat(), this);
-        qgl_share_reg()->replaceShare(d->glcx, newContext);
-        setContext(newContext);
+        setContext(newContext, d->glcx);
+
         // the overlay needs to be recreated as well
         delete d->olcx;
@@ -3284,5 +3905,5 @@
     Renders the current scene on a pixmap and returns the pixmap.
 
-    You can use this method on both visible and invisible QGLWidgets.
+    You can use this method on both visible and invisible QGLWidget objects.
 
     This method will create a pixmap and a temporary QGLContext to
@@ -3406,5 +4027,5 @@
         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());
@@ -3470,4 +4091,6 @@
     with the color \a c. Applies to this widgets GL context.
 
+    \note This function is not supported on OpenGL/ES 2.0 systems.
+
     \sa qglClearColor(), QGLContext::currentContext(), QColor
 */
@@ -3477,5 +4100,5 @@
 #if !defined(QT_OPENGL_ES_2)
 #ifdef QT_OPENGL_ES
-    glColor4f(c.red()/255.0, c.green()/255.0, c.blue()/255.0, c.alpha()/255.0);
+    glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());
 #else
     Q_D(const QGLWidget);
@@ -3483,5 +4106,5 @@
     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());
@@ -3493,4 +4116,6 @@
     }
 #endif //QT_OPENGL_ES
+#else
+    Q_UNUSED(c);
 #endif //QT_OPENGL_ES_2
 }
@@ -3507,6 +4132,5 @@
 {
 #ifdef QT_OPENGL_ES
-    glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0,
-                 (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0);
+    glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
 #else
     Q_D(const QGLWidget);
@@ -3514,6 +4138,5 @@
     if (ctx) {
         if (ctx->format().rgba())
-            glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0,
-                          (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0);
+            glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
         else if (!d->cmap.isEmpty()) { // QGLColormap in use?
             int i = d->cmap.find(c.rgb());
@@ -3586,8 +4209,8 @@
     will return the colormap for the child's top-level widget.
 
-    If no colormap has been set for this widget, the QColormap
+    If no colormap has been set for this widget, the QGLColormap
     returned will be empty.
 
-    \sa setColormap()
+    \sa setColormap(), QGLColormap::isEmpty()
 */
 
@@ -3609,7 +4232,10 @@
     value used when generating the display lists for the font. The
     default value is 2000.
+
+    \note This function is not supported on OpenGL/ES systems.
 */
 int QGLWidget::fontDisplayListBase(const QFont & font, int listBase)
 {
+#ifndef QT_OPENGL_ES
     Q_D(QGLWidget);
     int base;
@@ -3629,7 +4255,5 @@
     if (font.styleStrategy() != QFont::NoAntialias) {
         GLfloat color[4];
-#ifndef QT_OPENGL_ES
         glGetFloatv(GL_CURRENT_COLOR, color);
-#endif
         color_key.sprintf("%f_%f_%f",color[0], color[1], color[2]);
     }
@@ -3654,13 +4278,17 @@
     }
     return base;
-}
+#else // QT_OPENGL_ES
+    Q_UNUSED(font);
+    Q_UNUSED(listBase);
+    return 0;
+#endif
+}
+
+#ifndef QT_OPENGL_ES
 
 static void qt_save_gl_state()
 {
-#ifndef QT_OPENGL_ES
     glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
     glPushAttrib(GL_ALL_ATTRIB_BITS);
-#endif
-#if !defined(QT_OPENGL_ES_2)
     glMatrixMode(GL_TEXTURE);
     glPushMatrix();
@@ -3675,12 +4303,11 @@
     glDisable(GL_LIGHTING);
     glDisable(GL_STENCIL_TEST);
+    glDisable(GL_DEPTH_TEST);
     glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-#endif // !defined(QT_OPENGL_ES_2)
 }
 
 static void qt_restore_gl_state()
 {
-#if !defined(QT_OPENGL_ES_2)
     glMatrixMode(GL_TEXTURE);
     glPopMatrix();
@@ -3689,9 +4316,6 @@
     glMatrixMode(GL_MODELVIEW);
     glPopMatrix();
-#endif // !defined(QT_OPENGL_ES_2)
-#ifndef QT_OPENGL_ES
     glPopAttrib();
     glPopClientAttrib();
-#endif
 }
 
@@ -3700,7 +4324,5 @@
 {
     GLfloat color[4];
-#ifndef QT_OPENGL_ES
     glGetFloatv(GL_CURRENT_COLOR, &color[0]);
-#endif
 
     QColor col;
@@ -3716,4 +4338,6 @@
     p->setFont(old_font);
 }
+
+#endif // !QT_OPENGL_ES
 
 /*!
@@ -3731,8 +4355,16 @@
 
    \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.
 */
 
 void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, int)
 {
+#ifndef QT_OPENGL_ES
     Q_D(QGLWidget);
     if (str.isEmpty() || !isValid())
@@ -3740,15 +4372,13 @@
 
     GLint view[4];
-#ifndef QT_OPENGL_ES
     bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
     if (!use_scissor_testing)
         glGetIntegerv(GL_VIEWPORT, &view[0]);
-#else
-    bool use_scissor_testing = false;
-#endif
     int width = d->glcx->device()->width();
     int height = d->glcx->device()->height();
     bool auto_swap = autoBufferSwap();
 
+    QPaintEngine::Type oldEngineType = qgl_engine_selector()->preferredPaintEngine();
+    qgl_engine_selector()->setPreferredPaintEngine(QPaintEngine::OpenGL);
     QPaintEngine *engine = paintEngine();
     QPainter *p;
@@ -3759,22 +4389,16 @@
         qt_save_gl_state();
 
-#if !defined(QT_OPENGL_ES_2)
         glDisable(GL_DEPTH_TEST);
         glViewport(0, 0, width, height);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
-#ifndef QT_OPENGL_ES
         glOrtho(0, width, height, 0, 0, 1);
-#else
-        glOrthof(0, width, height, 0, 0, 1);
-#endif
         glMatrixMode(GL_MODELVIEW);
 
         glLoadIdentity();
-#endif // !defined(QT_OPENGL_ES_2)
     } else {
         setAutoBufferSwap(false);
         // disable glClear() as a result of QPainter::begin()
-        d->glcx->d_func()->clear_on_painter_begin = false;
+        d->disable_clear_on_painter_begin = true;
         p = new QPainter(this);
     }
@@ -3799,6 +4423,14 @@
         delete p;
         setAutoBufferSwap(auto_swap);
-        d->glcx->d_func()->clear_on_painter_begin = true;
-    }
+        d->disable_clear_on_painter_begin = false;
+    }
+    qgl_engine_selector()->setPreferredPaintEngine(oldEngineType);
+#else // QT_OPENGL_ES
+    Q_UNUSED(x);
+    Q_UNUSED(y);
+    Q_UNUSED(str);
+    Q_UNUSED(font);
+    qWarning("QGLWidget::renderText is not supported under OpenGL/ES");
+#endif
 }
 
@@ -3809,7 +4441,18 @@
     can be useful if you want to annotate models with text labels and
     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)
 {
+#ifndef QT_OPENGL_ES
     Q_D(QGLWidget);
     if (str.isEmpty() || !isValid())
@@ -3822,9 +4465,7 @@
     GLdouble model[4][4], proj[4][4];
     GLint view[4];
-#ifndef QT_OPENGL_ES
     glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
     glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
     glGetIntegerv(GL_VIEWPORT, &view[0]);
-#endif
     GLdouble win_x = 0, win_y = 0, win_z = 0;
     qgluProject(x, y, z, &model[0][0], &proj[0][0], &view[0],
@@ -3832,14 +4473,11 @@
     win_y = height - win_y; // y is inverted
 
+    QPaintEngine::Type oldEngineType = qgl_engine_selector()->preferredPaintEngine();
+    qgl_engine_selector()->setPreferredPaintEngine(QPaintEngine::OpenGL);
     QPaintEngine *engine = paintEngine();
     QPainter *p;
     bool reuse_painter = false;
-#ifndef QT_OPENGL_ES
     bool use_depth_testing = glIsEnabled(GL_DEPTH_TEST);
     bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST);
-#else
-    bool use_depth_testing = false;
-    bool use_scissor_testing = false;
-#endif
 
     if (engine->isActive()) {
@@ -3850,5 +4488,5 @@
         setAutoBufferSwap(false);
         // disable glClear() as a result of QPainter::begin()
-        d->glcx->d_func()->clear_on_painter_begin = false;
+        d->disable_clear_on_painter_begin = true;
         p = new QPainter(this);
     }
@@ -3861,13 +4499,8 @@
         glEnable(GL_SCISSOR_TEST);
     }
-#if !defined(QT_OPENGL_ES_2)
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     glViewport(0, 0, width, height);
-#ifndef QT_OPENGL_ES
     glOrtho(0, width, height, 0, 0, 1);
-#else
-    glOrthof(0, width, height, 0, 0, 1);
-#endif
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
@@ -3876,10 +4509,5 @@
     if (use_depth_testing)
         glEnable(GL_DEPTH_TEST);
-#ifndef QT_OPENGL_ES
     glTranslated(0, 0, -win_z);
-#else
-    glTranslatef(0, 0, -win_z);
-#endif
-#endif // !defined(QT_OPENGL_ES_2)
     qt_gl_draw_text(p, qRound(win_x), qRound(win_y), str, font);
 
@@ -3890,6 +4518,15 @@
         delete p;
         setAutoBufferSwap(auto_swap);
-        d->glcx->d_func()->clear_on_painter_begin = true;
-    }
+        d->disable_clear_on_painter_begin = false;
+    }
+    qgl_engine_selector()->setPreferredPaintEngine(oldEngineType);
+#else // QT_OPENGL_ES
+    Q_UNUSED(x);
+    Q_UNUSED(y);
+    Q_UNUSED(z);
+    Q_UNUSED(str);
+    Q_UNUSED(font);
+    qWarning("QGLWidget::renderText is not supported under OpenGL/ES");
+#endif
 }
 
@@ -3932,7 +4569,27 @@
 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);
-}
+    return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption);
+}
+
+/*!
+  \overload
+  \since 4.6
+
+  The binding \a options are a set of options used to decide how to
+  bind the texture to the context.
+ */
+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);
+}
+
 
 #ifdef Q_MAC_COMPAT_GL_FUNCTIONS
@@ -3940,6 +4597,19 @@
 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));
+   return d->glcx->bindTexture(image, GLenum(target), GLint(format), QGLContext::DefaultBindOption);
+}
+
+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);
 }
 #endif
@@ -3953,6 +4623,26 @@
 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);
+    return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption);
+}
+
+/*!
+  \overload
+  \since 4.6
+
+  Generates and binds a 2D GL texture to the current context, based
+  on \a pixmap. The generated texture id is returned and can be used in
+
+  The binding \a options are a set of options used to decide how to
+  bind the texture to the context.
+ */
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
+                              QGLContext::BindOptions options)
+{
+    Q_D(QGLWidget);
+    return d->glcx->bindTexture(pixmap, target, format, options);
 }
 
@@ -3962,5 +4652,12 @@
 {
     Q_D(QGLWidget);
-    return d->glcx->bindTexture(pixmap, target, format);
+    return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption);
+}
+
+GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format,
+                              QGLContext::BindOptions options)
+{
+    Q_D(QGLWidget);
+    return d->glcx->bindTexture(pixmap, target, format, options);
 }
 #endif
@@ -4047,20 +4744,25 @@
 #endif
 
-#if defined(QT_OPENGL_ES_2)
-Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_engine)
+#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine)
+#endif
+
+#ifndef QT_OPENGL_ES_2
+Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine)
+#endif
+
+Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine()
+{
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+    return qt_gl_engine();
+#elif defined(QT_OPENGL_ES_2)
+    return qt_gl_2_engine();
 #else
-Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine)
-#endif
-
-#ifdef Q_WS_QWS
-Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine()
-{
-#if !defined(QT_OPENGL_ES_2)
-    return qt_gl_engine();
-#else
-    return 0; // XXX
-#endif
-}
-#endif
+    if (qt_gl_preferGL2Engine())
+        return qt_gl_2_engine();
+    else
+        return qt_gl_engine();
+#endif
+}
 
 /*!
@@ -4072,5 +4774,5 @@
 QPaintEngine *QGLWidget::paintEngine() const
 {
-    return qt_gl_engine();
+    return qt_qgl_paint_engine();
 }
 
@@ -4132,38 +4834,65 @@
 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;
-#endif
-
-    QGLContext cx(QGLFormat::defaultFormat());
-    if (glExtensions & TextureCompression) {
-        qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
-    }
+    glExtensions |= FragmentShader;
+#endif
+#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL)
+    if (extensions.match("GL_OES_framebuffer_object"))
+        glExtensions |= FramebufferObject;
+#endif
+#if defined(QT_OPENGL_ES)
+    if (extensions.match("GL_OES_packed_depth_stencil"))
+        glExtensions |= PackedDepthStencil;
+#endif
+    if (extensions.match("GL_ARB_framebuffer_object")) {
+        // ARB_framebuffer_object also includes EXT_framebuffer_blit.
+        glExtensions |= FramebufferObject;
+        glExtensions |= FramebufferBlit;
+    }
+
+    if (extensions.match("GL_EXT_framebuffer_blit"))
+        glExtensions |= FramebufferBlit;
+
+    if (extensions.match("GL_ARB_texture_non_power_of_two"))
+        glExtensions |= NPOTTextures;
+
+    if (extensions.match("GL_EXT_bgra"))
+        glExtensions |= BGRATextureFormat;
 }
 
@@ -4174,4 +4903,6 @@
 {
     Q_Q(QGLWidget);
+
+    glDevice.setWidget(q);
 
     QGLExtensions::init();
@@ -4190,5 +4921,5 @@
 
 #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
-Q_GLOBAL_STATIC(QString, qt_gl_lib_name);
+Q_GLOBAL_STATIC(QString, qt_gl_lib_name)
 
 Q_OPENGL_EXPORT void qt_set_gl_library_name(const QString& name)
@@ -4201,5 +4932,5 @@
     if (qt_gl_lib_name()->isNull()) {
 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
-        return QString(QLatin1String("GL"));
+        return QLatin1String("GL");
 #else // Q_WS_MAC
         return QLatin1String("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib");
@@ -4210,3 +4941,449 @@
 #endif
 
+void QGLShareRegister::addShare(const QGLContext *context, const QGLContext *share) {
+    Q_ASSERT(context && share);
+    if (context->d_ptr->group == share->d_ptr->group)
+        return;
+
+    // Make sure 'context' is not already shared with another group of contexts.
+    Q_ASSERT(context->d_ptr->group->m_refs == 1);
+
+    // Free 'context' group resources and make it use the same resources as 'share'.
+    QGLContextGroup *group = share->d_ptr->group;
+    delete context->d_ptr->group;
+    context->d_ptr->group = group;
+    group->m_refs.ref();
+
+    // Maintain a list of all the contexts in each group of sharing contexts.
+    // The list is empty if the "share" context wasn't sharing already.
+    if (group->m_shares.isEmpty())
+        group->m_shares.append(share);
+    group->m_shares.append(context);
+}
+
+QList<const QGLContext *> QGLShareRegister::shares(const QGLContext *context) {
+    return context->d_ptr->group->m_shares;
+}
+
+void QGLShareRegister::removeShare(const QGLContext *context) {
+    // Remove the context from the group.
+    QGLContextGroup *group = context->d_ptr->group;
+    if (group->m_shares.isEmpty())
+        return;
+    group->m_shares.removeAll(context);
+
+    // Update context group representative.
+    Q_ASSERT(group->m_shares.size() != 0);
+    if (group->m_context == context)
+        group->m_context = group->m_shares[0];
+
+    // If there is only one context left, then make the list empty.
+    if (group->m_shares.size() == 1)
+        group->m_shares.clear();
+}
+
+QGLContextResource::QGLContextResource(FreeFunc f)
+    : free(f), active(0)
+{
+}
+
+QGLContextResource::~QGLContextResource()
+{
+#ifndef QT_NO_DEBUG
+    if (active != 0) {
+        qWarning("QtOpenGL: Resources are still available at program shutdown.\n"
+                 "          This is possibly caused by a leaked QGLWidget, \n"
+                 "          QGLFramebufferObject or QGLPixelBuffer.");
+    }
+#endif
+}
+
+void QGLContextResource::insert(const QGLContext *key, void *value)
+{
+    QGLContextGroup *group = QGLContextPrivate::contextGroup(key);
+    Q_ASSERT(!group->m_resources.contains(this));
+    group->m_resources.insert(this, value);
+    active.ref();
+}
+
+void *QGLContextResource::value(const QGLContext *key)
+{
+    QGLContextGroup *group = QGLContextPrivate::contextGroup(key);
+    return group->m_resources.value(this, 0);
+}
+
+void QGLContextResource::cleanup(const QGLContext *ctx, void *value)
+{
+    QGLShareContextScope scope(ctx);
+    free(value);
+    active.deref();
+}
+
+void QGLContextGroup::cleanupResources(const QGLContext *ctx)
+{
+    // If there are still shares, then no cleanup to be done yet.
+    if (m_shares.size() > 1)
+        return;
+
+    // Iterate over all resources and free each in turn.
+    QHash<QGLContextResource *, void *>::ConstIterator it;
+    for (it = m_resources.begin(); it != m_resources.end(); ++it)
+        it.key()->cleanup(ctx, it.value());
+}
+
+QGLSharedResourceGuard::~QGLSharedResourceGuard()
+{
+    if (m_group)
+        m_group->removeGuard(this);
+}
+
+void QGLSharedResourceGuard::setContext(const QGLContext *context)
+{
+    if (m_group)
+        m_group->removeGuard(this);
+    if (context) {
+        m_group = QGLContextPrivate::contextGroup(context);
+        m_group->addGuard(this);
+    } else {
+        m_group = 0;
+    }
+}
+
+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
