util/src/opengl/qpaintengine_opengl.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <qdebug.h>
       
    43 #include <private/qfontengine_p.h>
       
    44 #include <qmath.h>
       
    45 #include <private/qmath_p.h>
       
    46 #include <private/qdrawhelper_p.h>
       
    47 #include <private/qpaintengine_p.h>
       
    48 #include "qapplication.h"
       
    49 #include "qbrush.h"
       
    50 #include "qgl.h"
       
    51 #include <private/qgl_p.h>
       
    52 #include <private/qglpaintdevice_p.h>
       
    53 #include <private/qpainter_p.h>
       
    54 #include "qmap.h"
       
    55 #include <private/qpaintengine_opengl_p.h>
       
    56 #include <private/qdatabuffer_p.h>
       
    57 #include "qpen.h"
       
    58 #include "qvarlengtharray.h"
       
    59 #include <private/qpainter_p.h>
       
    60 #include <private/qglpixelbuffer_p.h>
       
    61 #include <private/qbezier_p.h>
       
    62 #include <qglframebufferobject.h>
       
    63 
       
    64 #include "private/qtessellator_p.h"
       
    65 
       
    66 #include "util/fragmentprograms_p.h"
       
    67 
       
    68 #ifdef Q_WS_QWS
       
    69 #include "private/qglwindowsurface_qws_p.h"
       
    70 #include "qwsmanager_qws.h"
       
    71 #include "private/qwsmanager_p.h"
       
    72 #endif
       
    73 
       
    74 #ifdef QT_OPENGL_ES_1_CL
       
    75 #include "qgl_cl_p.h"
       
    76 #endif
       
    77 
       
    78 #define QGL_FUNC_CONTEXT QGLContext *ctx = const_cast<QGLContext *>(device->context());
       
    79 
       
    80 #include <stdlib.h>
       
    81 #include "qpaintengine_opengl_p.h"
       
    82 
       
    83 QT_BEGIN_NAMESPACE
       
    84 
       
    85 extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp
       
    86 #ifdef QT_MAC_USE_COCOA
       
    87 extern void *qt_current_nsopengl_context(); // qgl_mac.mm
       
    88 #endif
       
    89 
       
    90 #define QREAL_MAX 9e100
       
    91 #define QREAL_MIN -9e100
       
    92 
       
    93 extern int qt_next_power_of_two(int v);
       
    94 
       
    95 #define DISABLE_DEBUG_ONCE
       
    96 
       
    97 //#define DEBUG_DISPLAY_MASK_TEXTURE
       
    98 
       
    99 #ifdef DISABLE_DEBUG_ONCE
       
   100 #define DEBUG_OVERRIDE(state) ;
       
   101 #define DEBUG_ONCE_STR(str) ;
       
   102 #define DEBUG_ONCE if (0)
       
   103 #else
       
   104 static int DEBUG_OVERRIDE_FLAG = 0;
       
   105 static bool DEBUG_TEMP_FLAG;
       
   106 #define DEBUG_OVERRIDE(state) { state ? ++DEBUG_OVERRIDE_FLAG : --DEBUG_OVERRIDE_FLAG; }
       
   107 #define DEBUG_ONCE if ((DEBUG_TEMP_FLAG = DEBUG_OVERRIDE_FLAG) && 0) ; else for (static int DEBUG_ONCE_FLAG = false; !DEBUG_ONCE_FLAG || DEBUG_TEMP_FLAG; DEBUG_ONCE_FLAG = true, DEBUG_TEMP_FLAG = false)
       
   108 #define DEBUG_ONCE_STR(str) DEBUG_ONCE qDebug() << (str);
       
   109 #endif
       
   110 
       
   111 #ifdef Q_WS_X11
       
   112 static bool qt_nvidiaFboNeedsFinish = false;
       
   113 #endif
       
   114 
       
   115 static inline void qt_glColor4ubv(unsigned char *col)
       
   116 {
       
   117     glColor4f(col[0]/255.0f, col[1]/255.0f, col[2]/255.0f, col[3]/255.0f);
       
   118 }
       
   119 
       
   120 struct QT_PointF {
       
   121     qreal x;
       
   122     qreal y;
       
   123 };
       
   124 
       
   125 struct QGLTrapezoid
       
   126 {
       
   127     QGLTrapezoid()
       
   128     {}
       
   129 
       
   130     QGLTrapezoid(qreal top_, qreal bottom_, qreal topLeftX_, qreal topRightX_, qreal bottomLeftX_, qreal bottomRightX_)
       
   131         : top(top_),
       
   132           bottom(bottom_),
       
   133           topLeftX(topLeftX_),
       
   134           topRightX(topRightX_),
       
   135           bottomLeftX(bottomLeftX_),
       
   136           bottomRightX(bottomRightX_)
       
   137     {}
       
   138 
       
   139     const QGLTrapezoid translated(const QPointF &delta) const;
       
   140 
       
   141     qreal top;
       
   142     qreal bottom;
       
   143     qreal topLeftX;
       
   144     qreal topRightX;
       
   145     qreal bottomLeftX;
       
   146     qreal bottomRightX;
       
   147 };
       
   148 
       
   149 const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const
       
   150 {
       
   151     QGLTrapezoid trap(*this);
       
   152     trap.top += delta.y();
       
   153     trap.bottom += delta.y();
       
   154     trap.topLeftX += delta.x();
       
   155     trap.topRightX += delta.x();
       
   156     trap.bottomLeftX += delta.x();
       
   157     trap.bottomRightX += delta.x();
       
   158     return trap;
       
   159 }
       
   160 
       
   161 
       
   162 class QOpenGLImmediateModeTessellator;
       
   163 class QGLMaskGenerator;
       
   164 class QGLOffscreen;
       
   165 
       
   166 class QGLMaskTextureCache
       
   167 {
       
   168 public:
       
   169     void setOffscreenSize(const QSize &offscreenSize);
       
   170     void setDrawableSize(const QSize &drawableSize);
       
   171 
       
   172     struct CacheLocation {
       
   173         QRect rect;
       
   174         int channel;
       
   175 
       
   176         QRect screen_rect;
       
   177     };
       
   178 
       
   179     struct CacheInfo {
       
   180         inline CacheInfo(const QPainterPath &p, const QTransform &m, qreal w = -1) :
       
   181             path(p), matrix(m), stroke_width(w), age(0) {}
       
   182 
       
   183         QPainterPath path;
       
   184         QTransform matrix;
       
   185         qreal stroke_width;
       
   186 
       
   187         CacheLocation loc;
       
   188 
       
   189         int age;
       
   190     };
       
   191 
       
   192     struct QuadTreeNode {
       
   193         quint64 key;
       
   194 
       
   195         int largest_available_block;
       
   196         int largest_used_block;
       
   197     };
       
   198 
       
   199     CacheLocation getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *engine);
       
   200 
       
   201     typedef QMultiHash<quint64, CacheInfo> QGLTextureCacheHash;
       
   202 
       
   203     enum {block_size = 64};
       
   204 
       
   205     // throw out keys that are too old
       
   206     void maintainCache();
       
   207     void clearCache();
       
   208 
       
   209 private:
       
   210     quint64 hash(const QPainterPath &p, const QTransform &m, qreal w);
       
   211 
       
   212     void createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator);
       
   213 
       
   214     QSize offscreenSize;
       
   215     QSize drawableSize;
       
   216 
       
   217     QGLTextureCacheHash cache;
       
   218 
       
   219     QVector<QuadTreeNode> occupied_quadtree[4];
       
   220 
       
   221     void quadtreeUpdate(int channel, int node, int current_block_size);
       
   222     void quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel);
       
   223 
       
   224     bool quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel);
       
   225     void quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel);
       
   226 
       
   227     void quadtreeInsert(int channel, quint64 key, const QRect &rect, int node = 0);
       
   228     void quadtreeClear(int channel, const QRect &rect, int node = 0);
       
   229 
       
   230     int quadtreeBlocksize(int node);
       
   231     QPoint quadtreeLocation(int node);
       
   232 
       
   233     QOpenGLPaintEnginePrivate *engine;
       
   234 };
       
   235 
       
   236 Q_GLOBAL_STATIC(QGLMaskTextureCache, qt_mask_texture_cache)
       
   237 
       
   238 class QGLOffscreen : public QObject
       
   239 {
       
   240     Q_OBJECT
       
   241 public:
       
   242     QGLOffscreen()
       
   243         : QObject(),
       
   244           offscreen(0),
       
   245           ctx(0),
       
   246           mask_dim(0),
       
   247           activated(false),
       
   248           bound(false)
       
   249     {
       
   250         connect(QGLSignalProxy::instance(),
       
   251                 SIGNAL(aboutToDestroyContext(const QGLContext*)),
       
   252                 SLOT(cleanupGLContextRefs(const QGLContext*)));
       
   253     }
       
   254 
       
   255     inline void setDevice(QPaintDevice *pdev);
       
   256 
       
   257     void begin();
       
   258     void end();
       
   259 
       
   260     inline void bind();
       
   261     inline void release();
       
   262 
       
   263     inline bool isBound() const;
       
   264 
       
   265     inline QSize drawableSize() const;
       
   266     inline QSize offscreenSize() const;
       
   267 
       
   268     inline GLuint offscreenTexture() const;
       
   269 
       
   270     QGLContext *context() const;
       
   271 
       
   272     static bool isSupported();
       
   273 
       
   274     inline void initialize();
       
   275 
       
   276     inline bool isValid() const;
       
   277 
       
   278 public Q_SLOTS:
       
   279     void cleanupGLContextRefs(const QGLContext *context) {
       
   280         if (context == ctx) {
       
   281             delete offscreen;
       
   282             ctx = 0;
       
   283             offscreen = 0;
       
   284             mask_dim = 0;
       
   285         }
       
   286     }
       
   287 
       
   288 private:
       
   289     QGLPaintDevice* device;
       
   290 
       
   291     QGLFramebufferObject *offscreen;
       
   292     QGLContext *ctx;
       
   293 
       
   294     // dimensions of mask texture (square)
       
   295     int mask_dim;
       
   296     QSize last_failed_size;
       
   297 
       
   298     bool drawable_fbo;
       
   299 
       
   300     bool activated;
       
   301     bool initialized;
       
   302 
       
   303     bool bound;
       
   304 };
       
   305 
       
   306 inline void QGLOffscreen::setDevice(QPaintDevice *pdev)
       
   307 {
       
   308     if (pdev->devType() == QInternal::OpenGL)
       
   309         device = static_cast<QGLPaintDevice*>(pdev);
       
   310     else
       
   311         device = QGLPaintDevice::getDevice(pdev);
       
   312 
       
   313     if (!device)
       
   314         return;
       
   315 
       
   316     drawable_fbo = (pdev->devType() == QInternal::FramebufferObject);
       
   317 }
       
   318 
       
   319 void QGLOffscreen::begin()
       
   320 {
       
   321 #ifndef QT_OPENGL_ES
       
   322     initialized = false;
       
   323 
       
   324     if (activated)
       
   325         initialize();
       
   326 #endif
       
   327 }
       
   328 
       
   329 void QGLOffscreen::initialize()
       
   330 {
       
   331 #ifndef QT_OPENGL_ES
       
   332     if (initialized)
       
   333         return;
       
   334 
       
   335     activated = true;
       
   336     initialized = true;
       
   337 
       
   338     int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height()))));
       
   339 
       
   340     bool shared_context = QGLContext::areSharing(device->context(), ctx);
       
   341     bool would_fail = last_failed_size.isValid() &&
       
   342                       (device->size().width() >= last_failed_size.width() ||
       
   343                        device->size().height() >= last_failed_size.height());
       
   344     bool needs_refresh = dim > mask_dim || !shared_context;
       
   345 
       
   346     if (needs_refresh && !would_fail) {
       
   347         DEBUG_ONCE qDebug() << "QGLOffscreen::initialize(): creating offscreen of size" << dim;
       
   348         delete offscreen;
       
   349         offscreen = new QGLFramebufferObject(dim, dim, GLenum(GL_TEXTURE_2D));
       
   350         mask_dim = dim;
       
   351 
       
   352         if (!offscreen->isValid()) {
       
   353             qWarning("QGLOffscreen: Invalid offscreen fbo (size %dx%d)", mask_dim, mask_dim);
       
   354             delete offscreen;
       
   355             offscreen = 0;
       
   356             mask_dim = 0;
       
   357             last_failed_size = device->size();
       
   358         }
       
   359     }
       
   360 
       
   361     qt_mask_texture_cache()->setOffscreenSize(offscreenSize());
       
   362     qt_mask_texture_cache()->setDrawableSize(device->size());
       
   363     ctx = device->context();
       
   364 #endif
       
   365 }
       
   366 
       
   367 inline bool QGLOffscreen::isValid() const
       
   368 {
       
   369     return offscreen;
       
   370 }
       
   371 
       
   372 void QGLOffscreen::end()
       
   373 {
       
   374     if (bound)
       
   375         release();
       
   376 #ifdef DEBUG_DISPLAY_MASK_TEXTURE
       
   377     glReadBuffer(GL_BACK);
       
   378     glDrawBuffer(GL_BACK);
       
   379     glMatrixMode(GL_MODELVIEW);
       
   380     glLoadIdentity();
       
   381     glColor4f(1, 1, 1, 1);
       
   382     glDisable(GL_DEPTH_TEST);
       
   383     glBlendFunc(GL_ONE, GL_ZERO);
       
   384     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
   385     glEnable(GL_TEXTURE_2D);
       
   386     glBindTexture(GL_TEXTURE_2D, offscreen->texture());
       
   387 
       
   388     glBegin(GL_QUADS);
       
   389     glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0);
       
   390     glTexCoord2f(1.0, 1.0); glVertex2f(drawable.size().width(), 0.0);
       
   391     glTexCoord2f(1.0, 0.0); glVertex2f(drawable.size().width(), drawable.size().height());
       
   392     glTexCoord2f(0.0, 0.0); glVertex2f(0.0, drawable.size().height());
       
   393     glEnd();
       
   394 
       
   395     glBindTexture(GL_TEXTURE_2D, 0);
       
   396     glDisable(GL_TEXTURE_2D);
       
   397 #endif
       
   398 }
       
   399 
       
   400 inline void QGLOffscreen::bind()
       
   401 {
       
   402 #ifndef QT_OPENGL_ES
       
   403     Q_ASSERT(initialized);
       
   404 
       
   405     if (!offscreen || bound)
       
   406         return;
       
   407 
       
   408     DEBUG_ONCE qDebug() << "QGLOffscreen: binding offscreen";
       
   409     offscreen->bind();
       
   410 
       
   411     bound = true;
       
   412 
       
   413     glViewport(0, 0, offscreenSize().width(), offscreenSize().height());
       
   414 
       
   415     glMatrixMode(GL_PROJECTION);
       
   416     glLoadIdentity();
       
   417     glOrtho(0, offscreenSize().width(), offscreenSize().height(), 0, -999999, 999999);
       
   418     glMatrixMode(GL_MODELVIEW);
       
   419 #endif
       
   420 }
       
   421 
       
   422 inline void QGLOffscreen::release()
       
   423 {
       
   424 #ifndef QT_OPENGL_ES
       
   425     if (!offscreen || !bound)
       
   426         return;
       
   427 
       
   428 #ifdef Q_WS_X11
       
   429     // workaround for bug in nvidia driver versions 9x.xx
       
   430     if (qt_nvidiaFboNeedsFinish)
       
   431         glFinish();
       
   432 #endif
       
   433 
       
   434     DEBUG_ONCE_STR("QGLOffscreen: releasing offscreen");
       
   435 
       
   436     if (drawable_fbo)
       
   437         device->ensureActiveTarget(); //###
       
   438     else
       
   439         offscreen->release();
       
   440 
       
   441     QSize sz(device->size());
       
   442     glViewport(0, 0, sz.width(), sz.height());
       
   443 
       
   444     glMatrixMode(GL_PROJECTION);
       
   445     glLoadIdentity();
       
   446 #ifndef QT_OPENGL_ES
       
   447     glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
       
   448 #else
       
   449     glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
       
   450 #endif
       
   451     glMatrixMode(GL_MODELVIEW);
       
   452 
       
   453     bound = false;
       
   454 #endif
       
   455 }
       
   456 
       
   457 inline bool QGLOffscreen::isBound() const
       
   458 {
       
   459     return bound;
       
   460 }
       
   461 
       
   462 inline QSize QGLOffscreen::drawableSize() const
       
   463 {
       
   464     return device->size();
       
   465 }
       
   466 
       
   467 inline QSize QGLOffscreen::offscreenSize() const
       
   468 {
       
   469     return QSize(mask_dim, mask_dim);
       
   470 }
       
   471 
       
   472 inline GLuint QGLOffscreen::offscreenTexture() const
       
   473 {
       
   474     return offscreen ? offscreen->texture() : 0;
       
   475 }
       
   476 
       
   477 inline QGLContext *QGLOffscreen::context() const
       
   478 {
       
   479     return ctx;
       
   480 }
       
   481 
       
   482 bool QGLOffscreen::isSupported()
       
   483 {
       
   484     return (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject); // for fbo
       
   485 }
       
   486 
       
   487 struct QDrawQueueItem
       
   488 {
       
   489     QDrawQueueItem(qreal _opacity,
       
   490                    QBrush _brush,
       
   491                    const QPointF &_brush_origion,
       
   492                    QPainter::CompositionMode _composition_mode,
       
   493                    const QTransform &_matrix,
       
   494                    QGLMaskTextureCache::CacheLocation _location)
       
   495         : opacity(_opacity),
       
   496           brush(_brush),
       
   497           brush_origin(_brush_origion),
       
   498           composition_mode(_composition_mode),
       
   499           matrix(_matrix),
       
   500           location(_location) {}
       
   501     qreal opacity;
       
   502     QBrush brush;
       
   503     QPointF brush_origin;
       
   504     QPainter::CompositionMode composition_mode;
       
   505 
       
   506     QTransform matrix;
       
   507     QGLMaskTextureCache::CacheLocation location;
       
   508 };
       
   509 
       
   510 ////////// GL program cache: start
       
   511 
       
   512 typedef struct {
       
   513     int brush; // brush index or mask index
       
   514     int mode;  // composition mode index
       
   515     bool mask;
       
   516     GLuint program;
       
   517 } GLProgram;
       
   518 
       
   519 typedef QMultiHash<const QGLContext *, GLProgram> QGLProgramHash;
       
   520 
       
   521 class QGLProgramCache : public QObject
       
   522 {
       
   523     Q_OBJECT
       
   524 public:
       
   525     QGLProgramCache() {
       
   526         // we have to know when a context is deleted so we can free
       
   527         // any program handles it holds
       
   528         connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
       
   529                 SLOT(cleanupPrograms(const QGLContext*)));
       
   530 
       
   531     }
       
   532     ~QGLProgramCache() {
       
   533         // at this point the cache should contain 0 elements
       
   534         // Q_ASSERT(program.size() == 0);
       
   535     }
       
   536 
       
   537     GLuint getProgram(const QGLContext *ctx, int brush, int mode, bool mask_mode)
       
   538     {
       
   539         // 1. see if we have an entry for the ctx context
       
   540         QList<GLProgram> progs = programs.values(ctx);
       
   541         for (int i=0; i<progs.size(); ++i) {
       
   542             const GLProgram &prg = progs.at(i);
       
   543             if (mask_mode) {
       
   544                 if (prg.mask && prg.brush == brush)
       
   545                     return prg.program;
       
   546             } else {
       
   547                 if (!prg.mask && prg.brush == brush && prg.mode == mode)
       
   548                     return prg.program;
       
   549             }
       
   550         }
       
   551 
       
   552         // 2. try to find a match in a shared context, and update the
       
   553         // hash with the entry found
       
   554         QList<const QGLContext *> contexts = programs.uniqueKeys();
       
   555         for (int i=0; i<contexts.size(); ++i) {
       
   556             const QGLContext *cx = contexts.at(i);
       
   557             if (cx != ctx && QGLContext::areSharing(cx, ctx)) {
       
   558                 QList<GLProgram> progs = programs.values(cx);
       
   559                 for (int k=0; k<progs.size(); ++k) {
       
   560                     const GLProgram &prg = progs.at(k);
       
   561                     if (mask_mode) {
       
   562                         if (prg.mask && prg.brush == brush) {
       
   563                             programs.insert(ctx, prg);
       
   564                             return prg.program;
       
   565                         }
       
   566                     } else {
       
   567                         if (!prg.mask && prg.brush == brush && prg.mode == mode) {
       
   568                             programs.insert(ctx, prg);
       
   569                             return prg.program;
       
   570                         }
       
   571                     }
       
   572                 }
       
   573             }
       
   574         }
       
   575 
       
   576         // 3. compile a new program and place it into the cache
       
   577         // NB! assumes ctx is the current GL context
       
   578         GLProgram prg;
       
   579         prg.brush = brush;
       
   580         prg.mode = mode;
       
   581         prg.mask = mask_mode;
       
   582         glGenProgramsARB(1, &prg.program);
       
   583         glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prg.program);
       
   584         const char *src = mask_mode
       
   585                           ? mask_fragment_program_sources[brush]
       
   586                           : painter_fragment_program_sources[brush][mode];
       
   587         // necessary for .NET 2002, apparently
       
   588         const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(src);
       
   589 
       
   590         while (glGetError() != GL_NO_ERROR) {} // reset error state
       
   591         glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
       
   592                            int(strlen(src)), gl_src);
       
   593         if (glGetError() != GL_NO_ERROR) {
       
   594 //            qDebug() << "QGLProgramCache: Unable to compile fragment program.";
       
   595             glDeleteProgramsARB(1, &prg.program);
       
   596             return 0;
       
   597         }
       
   598 
       
   599 //        qDebug() << "QGLProgramCache: Creating GL program:" << prg.program << hex << ctx;
       
   600         programs.insert(ctx, prg);
       
   601         return prg.program;
       
   602     }
       
   603 
       
   604 public Q_SLOTS:
       
   605     void cleanupPrograms(const QGLContext *context)
       
   606     {
       
   607         QGLProgramHash::iterator it = programs.begin();
       
   608         while (it != programs.end()) {
       
   609             if (it.key() == context) {
       
   610                 if (!context->isSharing()) {
       
   611                     // the ctx variable below is needed for the glDeleteProgramARB call
       
   612                     // since it is resolved from our extension system
       
   613                     // NB! assumes context is the current GL context
       
   614                     const QGLContext *ctx = context;
       
   615                     // qDebug() << "QGLProgramHash: Deleting GL program:" << it.value().program << hex << it.key();
       
   616                     glDeleteProgramsARB(1, &it.value().program);
       
   617                 }
       
   618                 it = programs.erase(it);
       
   619             } else {
       
   620                 ++it;
       
   621             }
       
   622         }
       
   623     }
       
   624 
       
   625 private:
       
   626     QGLProgramHash programs;
       
   627 };
       
   628 
       
   629 Q_GLOBAL_STATIC(QGLProgramCache, qt_gl_program_cache)
       
   630 
       
   631 ////////// GL program cache: end
       
   632 
       
   633 class QOpenGLPaintEnginePrivate;
       
   634 class QGLPrivateCleanup : public QObject
       
   635 {
       
   636     Q_OBJECT
       
   637 public:
       
   638     QGLPrivateCleanup(QOpenGLPaintEnginePrivate *priv)
       
   639         : p(priv)
       
   640     {
       
   641         connect(QGLSignalProxy::instance(),
       
   642                 SIGNAL(aboutToDestroyContext(const QGLContext*)),
       
   643                 SLOT(cleanupGLContextRefs(const QGLContext*)));
       
   644     }
       
   645 
       
   646 public Q_SLOTS:
       
   647     void cleanupGLContextRefs(const QGLContext *context);
       
   648 
       
   649 private:
       
   650     QOpenGLPaintEnginePrivate *p;
       
   651 };
       
   652 
       
   653 class QOpenGLPaintEnginePrivate : public QPaintEngineExPrivate
       
   654 {
       
   655     Q_DECLARE_PUBLIC(QOpenGLPaintEngine)
       
   656 public:
       
   657     QOpenGLPaintEnginePrivate()
       
   658         : opacity(1)
       
   659         , composition_mode(QPainter::CompositionMode_SourceOver)
       
   660         , has_fast_pen(false)
       
   661         , use_stencil_method(false)
       
   662         , dirty_drawable_texture(false)
       
   663         , has_stencil_face_ext(false)
       
   664         , use_fragment_programs(false)
       
   665         , high_quality_antialiasing(false)
       
   666         , use_smooth_pixmap_transform(false)
       
   667         , use_emulation(false)
       
   668         , txop(QTransform::TxNone)
       
   669         , inverseScale(1)
       
   670         , moveToCount(0)
       
   671         , last_created_state(0)
       
   672         , shader_ctx(0)
       
   673         , grad_palette(0)
       
   674         , drawable_texture(0)
       
   675         , ref_cleaner(this)
       
   676         {}
       
   677 
       
   678     inline void setGLPen(const QColor &c) {
       
   679         uint alpha = qRound(c.alpha() * opacity);
       
   680         pen_color[0] = qt_div_255(c.red() * alpha);
       
   681         pen_color[1] = qt_div_255(c.green() * alpha);
       
   682         pen_color[2] = qt_div_255(c.blue() * alpha);
       
   683         pen_color[3] = alpha;
       
   684     }
       
   685 
       
   686     inline void setGLBrush(const QColor &c) {
       
   687         uint alpha = qRound(c.alpha() * opacity);
       
   688         brush_color[0] = qt_div_255(c.red() * alpha);
       
   689         brush_color[1] = qt_div_255(c.green() * alpha);
       
   690         brush_color[2] = qt_div_255(c.blue() * alpha);
       
   691         brush_color[3] = alpha;
       
   692     }
       
   693 
       
   694     inline void setGradientOps(const QBrush &brush, const QRectF &bounds);
       
   695     void createGradientPaletteTexture(const QGradient& g);
       
   696 
       
   697     void updateGradient(const QBrush &brush, const QRectF &bounds);
       
   698 
       
   699     inline void lineToStencil(qreal x, qreal y);
       
   700     inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
       
   701     void pathToVertexArrays(const QPainterPath &path);
       
   702     void fillVertexArray(Qt::FillRule fillRule);
       
   703     void drawVertexArrays();
       
   704     void fillPath(const QPainterPath &path);
       
   705     void fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
       
   706                          Qt::FillRule fill);
       
   707 
       
   708     void drawFastRect(const QRectF &rect);
       
   709     void strokePath(const QPainterPath &path, bool use_cache);
       
   710     void strokePathFastPen(const QPainterPath &path, bool needsResolving);
       
   711     void strokeLines(const QPainterPath &path);
       
   712 
       
   713     void updateDepthClip();
       
   714     void systemStateChanged();
       
   715 
       
   716     void cleanupGLContextRefs(const QGLContext *context) {
       
   717         if (context == shader_ctx)
       
   718             shader_ctx = 0;
       
   719     }
       
   720 
       
   721     inline void updateFastPen() {
       
   722         qreal pen_width = cpen.widthF();
       
   723         has_fast_pen =
       
   724             ((pen_width == 0 || (pen_width <= 1 && matrix.type() <= QTransform::TxTranslate))
       
   725              || cpen.isCosmetic())
       
   726             && cpen.style() == Qt::SolidLine
       
   727             && cpen.isSolid();
       
   728 
       
   729     }
       
   730 
       
   731     void disableClipping();
       
   732     void enableClipping();
       
   733     void ensureDrawableTexture();
       
   734 
       
   735     QPen cpen;
       
   736     QBrush cbrush;
       
   737     Qt::BrushStyle brush_style;
       
   738     QPointF brush_origin;
       
   739     Qt::BrushStyle pen_brush_style;
       
   740     qreal opacity;
       
   741     QPainter::CompositionMode composition_mode;
       
   742 
       
   743     Qt::BrushStyle current_style;
       
   744 
       
   745     uint has_pen : 1;
       
   746     uint has_brush : 1;
       
   747     uint has_fast_pen : 1;
       
   748     uint use_stencil_method : 1;
       
   749     uint dirty_drawable_texture : 1;
       
   750     uint has_stencil_face_ext : 1;
       
   751     uint use_fragment_programs : 1;
       
   752     uint high_quality_antialiasing : 1;
       
   753     uint has_antialiasing : 1;
       
   754     uint has_fast_composition_mode : 1;
       
   755     uint use_smooth_pixmap_transform : 1;
       
   756     uint use_system_clip : 1;
       
   757     uint use_emulation : 1;
       
   758 
       
   759     QRegion dirty_stencil;
       
   760 
       
   761     void updateUseEmulation();
       
   762 
       
   763     QTransform matrix;
       
   764     GLubyte pen_color[4];
       
   765     GLubyte brush_color[4];
       
   766     QTransform::TransformationType txop;
       
   767     QGLPaintDevice* device;
       
   768     QGLOffscreen offscreen;
       
   769 
       
   770     qreal inverseScale;
       
   771 
       
   772     int moveToCount;
       
   773     QPointF path_start;
       
   774 
       
   775     bool isFastRect(const QRectF &r);
       
   776 
       
   777     void drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr);
       
   778     void drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy, const QPointF &offset);
       
   779 
       
   780     void drawOffscreenPath(const QPainterPath &path);
       
   781 
       
   782     void composite(const QRectF &rect, const QPoint &maskOffset = QPoint());
       
   783     void composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset = QPoint());
       
   784 
       
   785     bool createFragmentPrograms();
       
   786     void deleteFragmentPrograms();
       
   787     void updateFragmentProgramData(int locations[]);
       
   788 
       
   789     void cacheItemErased(int channel, const QRect &rect);
       
   790 
       
   791     void addItem(const QGLMaskTextureCache::CacheLocation &location);
       
   792     void drawItem(const QDrawQueueItem &item);
       
   793     void flushDrawQueue();
       
   794 
       
   795     void copyDrawable(const QRectF &rect);
       
   796 
       
   797     void updateGLMatrix() const;
       
   798 
       
   799     mutable QPainterState *last_created_state;
       
   800 
       
   801     QGLContext *shader_ctx;
       
   802     GLuint grad_palette;
       
   803 
       
   804     GLuint painter_fragment_programs[num_fragment_brushes][num_fragment_composition_modes];
       
   805     GLuint mask_fragment_programs[num_fragment_masks];
       
   806 
       
   807     float inv_matrix_data[3][4];
       
   808     float fmp_data[4];
       
   809     float fmp2_m_radius2_data[4];
       
   810     float angle_data[4];
       
   811     float linear_data[4];
       
   812 
       
   813     float porterduff_ab_data[4];
       
   814     float porterduff_xyz_data[4];
       
   815 
       
   816     float mask_offset_data[4];
       
   817     float mask_channel_data[4];
       
   818 
       
   819     FragmentBrushType fragment_brush;
       
   820     FragmentCompositionModeType fragment_composition_mode;
       
   821 
       
   822     void setPorterDuffData(float a, float b, float x, float y, float z);
       
   823     void setInvMatrixData(const QTransform &inv_matrix);
       
   824 
       
   825     qreal max_x;
       
   826     qreal max_y;
       
   827     qreal min_x;
       
   828     qreal min_y;
       
   829 
       
   830     QDataBuffer<QPointF> tess_points;
       
   831     QVector<int> tess_points_stops;
       
   832 
       
   833     GLdouble projection_matrix[4][4];
       
   834 
       
   835 #if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2)
       
   836     GLfloat mv_matrix[4][4];
       
   837 #else
       
   838     GLdouble mv_matrix[4][4];
       
   839 #endif
       
   840 
       
   841     QList<QDrawQueueItem> drawQueue;
       
   842 
       
   843     GLuint drawable_texture;
       
   844     QSize drawable_texture_size;
       
   845 
       
   846     int max_texture_size;
       
   847 
       
   848     QGLPrivateCleanup ref_cleaner;
       
   849     friend class QGLMaskTextureCache;
       
   850 };
       
   851 
       
   852 class QOpenGLCoordinateOffset
       
   853 {
       
   854 public:
       
   855     QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d);
       
   856     ~QOpenGLCoordinateOffset();
       
   857 
       
   858     static void enableOffset(QOpenGLPaintEnginePrivate *d);
       
   859     static void disableOffset(QOpenGLPaintEnginePrivate *d);
       
   860 
       
   861 private:
       
   862     QOpenGLPaintEnginePrivate *d;
       
   863 };
       
   864 
       
   865 QOpenGLCoordinateOffset::QOpenGLCoordinateOffset(QOpenGLPaintEnginePrivate *d_)
       
   866     : d(d_)
       
   867 {
       
   868     enableOffset(d);
       
   869 }
       
   870 
       
   871 void QOpenGLCoordinateOffset::enableOffset(QOpenGLPaintEnginePrivate *d)
       
   872 {
       
   873     if (!d->has_antialiasing) {
       
   874         glMatrixMode(GL_MODELVIEW);
       
   875         glPushMatrix();
       
   876         d->mv_matrix[3][0] += 0.5;
       
   877         d->mv_matrix[3][1] += 0.5;
       
   878         d->updateGLMatrix();
       
   879     }
       
   880 }
       
   881 
       
   882 QOpenGLCoordinateOffset::~QOpenGLCoordinateOffset()
       
   883 {
       
   884     disableOffset(d);
       
   885 }
       
   886 
       
   887 void QOpenGLCoordinateOffset::disableOffset(QOpenGLPaintEnginePrivate *d)
       
   888 {
       
   889     if (!d->has_antialiasing) {
       
   890         glMatrixMode(GL_MODELVIEW);
       
   891         glPopMatrix();
       
   892         d->mv_matrix[3][0] -= 0.5;
       
   893         d->mv_matrix[3][1] -= 0.5;
       
   894     }
       
   895 }
       
   896 
       
   897 void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context)
       
   898 {
       
   899     p->cleanupGLContextRefs(context);
       
   900 }
       
   901 
       
   902 
       
   903 static inline void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform)
       
   904 {
       
   905     if (smoothPixmapTransform) {
       
   906         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       
   907         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
       
   908     } else {
       
   909         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
   910         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
   911     }
       
   912     glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
       
   913     glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
       
   914 }
       
   915 
       
   916 static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) {
       
   917     QPainterPathStroker stroker;
       
   918     if (cpen.style() == Qt::CustomDashLine)
       
   919         stroker.setDashPattern(cpen.dashPattern());
       
   920     else
       
   921         stroker.setDashPattern(cpen.style());
       
   922 
       
   923     stroker.setCapStyle(cpen.capStyle());
       
   924     stroker.setJoinStyle(cpen.joinStyle());
       
   925     stroker.setMiterLimit(cpen.miterLimit());
       
   926 
       
   927     qreal width = cpen.widthF();
       
   928     if (width == 0)
       
   929         stroker.setWidth(1);
       
   930     else
       
   931         stroker.setWidth(width);
       
   932 
       
   933     QPainterPath stroke = stroker.createStroke(path);
       
   934     stroke.setFillRule(Qt::WindingFill);
       
   935     return stroke;
       
   936 }
       
   937 
       
   938 class QGLStrokeCache
       
   939 {
       
   940     struct CacheInfo
       
   941     {
       
   942         inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) :
       
   943             path(p), stroked_path(sp), pen(stroke_pen) {}
       
   944         QPainterPath path;
       
   945         QPainterPath stroked_path;
       
   946         QPen pen;
       
   947     };
       
   948 
       
   949     typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;
       
   950 
       
   951 public:
       
   952     inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) {
       
   953         quint64 hash_val = 0;
       
   954 
       
   955         for (int i = 0; i < path.elementCount() && i <= 2; i++) {
       
   956             hash_val += quint64(path.elementAt(i).x);
       
   957             hash_val += quint64(path.elementAt(i).y);
       
   958         }
       
   959 
       
   960         QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val);
       
   961 
       
   962         if (it == cache.constEnd())
       
   963             return addCacheElement(hash_val, path, pen);
       
   964         else {
       
   965             do {
       
   966                 const CacheInfo &cache_info = it.value();
       
   967                 if (cache_info.path == path && cache_info.pen == pen)
       
   968                     return cache_info.stroked_path;
       
   969                 ++it;
       
   970             } while (it != cache.constEnd() && it.key() == hash_val);
       
   971             // an exact match for this path was not found, create new cache element
       
   972             return addCacheElement(hash_val, path, pen);
       
   973         }
       
   974     }
       
   975 
       
   976 protected:
       
   977     inline int maxCacheSize() const { return 500; }
       
   978     QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) {
       
   979         if (cache.size() == maxCacheSize()) {
       
   980             int elem_to_remove = qrand() % maxCacheSize();
       
   981             cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK
       
   982         }
       
   983         QPainterPath stroke = strokeForPath(path, pen);
       
   984         CacheInfo cache_entry(path, stroke, pen);
       
   985         return cache.insert(hash_val, cache_entry).value().stroked_path;
       
   986     }
       
   987 
       
   988     QGLStrokeTableHash cache;
       
   989 };
       
   990 
       
   991 Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)
       
   992 
       
   993 class QGLGradientCache : public QObject
       
   994 {
       
   995     Q_OBJECT
       
   996     struct CacheInfo
       
   997     {
       
   998         inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
       
   999             stops(s), opacity(op), interpolationMode(mode) {}
       
  1000 
       
  1001         GLuint texId;
       
  1002         QGradientStops stops;
       
  1003         qreal opacity;
       
  1004         QGradient::InterpolationMode interpolationMode;
       
  1005     };
       
  1006 
       
  1007     typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;
       
  1008 
       
  1009 public:
       
  1010     QGLGradientCache() : QObject(), buffer_ctx(0)
       
  1011     {
       
  1012         connect(QGLSignalProxy::instance(),
       
  1013                 SIGNAL(aboutToDestroyContext(const QGLContext*)),
       
  1014                 SLOT(cleanupGLContextRefs(const QGLContext*)));
       
  1015     }
       
  1016 
       
  1017     inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) {
       
  1018         if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx))
       
  1019             cleanCache();
       
  1020 
       
  1021         buffer_ctx = ctx;
       
  1022 
       
  1023         quint64 hash_val = 0;
       
  1024 
       
  1025         QGradientStops stops = gradient.stops();
       
  1026         for (int i = 0; i < stops.size() && i <= 2; i++)
       
  1027             hash_val += stops[i].second.rgba();
       
  1028 
       
  1029         QGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
       
  1030 
       
  1031         if (it == cache.constEnd())
       
  1032             return addCacheElement(hash_val, gradient, opacity);
       
  1033         else {
       
  1034             do {
       
  1035                 const CacheInfo &cache_info = it.value();
       
  1036                 if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode()) {
       
  1037                     return cache_info.texId;
       
  1038                 }
       
  1039                 ++it;
       
  1040             } while (it != cache.constEnd() && it.key() == hash_val);
       
  1041             // an exact match for these stops and opacity was not found, create new cache
       
  1042             return addCacheElement(hash_val, gradient, opacity);
       
  1043         }
       
  1044     }
       
  1045 
       
  1046     inline int paletteSize() const { return 1024; }
       
  1047 
       
  1048 protected:
       
  1049     inline int maxCacheSize() const { return 60; }
       
  1050     inline void generateGradientColorTable(const QGradient& g,
       
  1051                                            uint *colorTable,
       
  1052                                            int size, qreal opacity) const;
       
  1053     GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) {
       
  1054         if (cache.size() == maxCacheSize()) {
       
  1055             int elem_to_remove = qrand() % maxCacheSize();
       
  1056             quint64 key = cache.keys()[elem_to_remove];
       
  1057 
       
  1058             // need to call glDeleteTextures on each removed cache entry:
       
  1059             QGLGradientColorTableHash::const_iterator it = cache.constFind(key);
       
  1060             do {
       
  1061                 glDeleteTextures(1, &it.value().texId);
       
  1062             } while (++it != cache.constEnd() && it.key() == key);
       
  1063             cache.remove(key); // may remove more than 1, but OK
       
  1064         }
       
  1065         CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
       
  1066         uint buffer[1024];
       
  1067         generateGradientColorTable(gradient, buffer, paletteSize(), opacity);
       
  1068         glGenTextures(1, &cache_entry.texId);
       
  1069 #ifndef QT_OPENGL_ES
       
  1070         glBindTexture(GL_TEXTURE_1D, cache_entry.texId);
       
  1071         glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(),
       
  1072                      0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
       
  1073 #else
       
  1074         // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives
       
  1075 #endif
       
  1076         return cache.insert(hash_val, cache_entry).value().texId;
       
  1077     }
       
  1078 
       
  1079     void cleanCache() {
       
  1080         QGLShareContextScope scope(buffer_ctx);
       
  1081         QGLGradientColorTableHash::const_iterator it = cache.constBegin();
       
  1082         for (; it != cache.constEnd(); ++it) {
       
  1083             const CacheInfo &cache_info = it.value();
       
  1084             glDeleteTextures(1, &cache_info.texId);
       
  1085         }
       
  1086         cache.clear();
       
  1087     }
       
  1088 
       
  1089     QGLGradientColorTableHash cache;
       
  1090 
       
  1091     QGLContext *buffer_ctx;
       
  1092 
       
  1093 public Q_SLOTS:
       
  1094     void cleanupGLContextRefs(const QGLContext *context) {
       
  1095         if (context == buffer_ctx) {
       
  1096             cleanCache();
       
  1097             buffer_ctx = 0;
       
  1098         }
       
  1099     }
       
  1100 };
       
  1101 
       
  1102 static inline uint endianColor(uint c)
       
  1103 {
       
  1104 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
       
  1105     return c;
       
  1106 #else
       
  1107     return ( (c << 24) & 0xff000000)
       
  1108            | ((c >> 24) & 0x000000ff)
       
  1109            | ((c << 8) & 0x00ff0000)
       
  1110            | ((c >> 8) & 0x0000ff00);
       
  1111 #endif // Q_BYTE_ORDER
       
  1112 }
       
  1113 
       
  1114 void QGLGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const
       
  1115 {
       
  1116     int pos = 0;
       
  1117     QGradientStops s = gradient.stops();
       
  1118     QVector<uint> colors(s.size());
       
  1119 
       
  1120     for (int i = 0; i < s.size(); ++i)
       
  1121         colors[i] = s[i].second.rgba();
       
  1122 
       
  1123     bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
       
  1124 
       
  1125     uint alpha = qRound(opacity * 256);
       
  1126     uint current_color = ARGB_COMBINE_ALPHA(colors[0], alpha);
       
  1127     qreal incr = 1.0 / qreal(size);
       
  1128     qreal fpos = 1.5 * incr;
       
  1129     colorTable[pos++] = endianColor(PREMUL(current_color));
       
  1130 
       
  1131     while (fpos <= s.first().first) {
       
  1132         colorTable[pos] = colorTable[pos - 1];
       
  1133         pos++;
       
  1134         fpos += incr;
       
  1135     }
       
  1136 
       
  1137     if (colorInterpolation)
       
  1138         current_color = PREMUL(current_color);
       
  1139 
       
  1140     for (int i = 0; i < s.size() - 1; ++i) {
       
  1141         qreal delta = 1/(s[i+1].first - s[i].first);
       
  1142         uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha);
       
  1143         if (colorInterpolation)
       
  1144             next_color = PREMUL(next_color);
       
  1145 
       
  1146         while (fpos < s[i+1].first && pos < size) {
       
  1147             int dist = int(256 * ((fpos - s[i].first) * delta));
       
  1148             int idist = 256 - dist;
       
  1149             if (colorInterpolation)
       
  1150                 colorTable[pos] = endianColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
       
  1151             else
       
  1152                 colorTable[pos] = endianColor(PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
       
  1153             ++pos;
       
  1154             fpos += incr;
       
  1155         }
       
  1156         current_color = next_color;
       
  1157     }
       
  1158 
       
  1159     Q_ASSERT(s.size() > 0);
       
  1160 
       
  1161     uint last_color = endianColor(PREMUL(ARGB_COMBINE_ALPHA(colors[s.size() - 1], alpha)));
       
  1162     for (;pos < size; ++pos)
       
  1163         colorTable[pos] = last_color;
       
  1164 
       
  1165     // Make sure the last color stop is represented at the end of the table
       
  1166     colorTable[size-1] = last_color;
       
  1167 }
       
  1168 
       
  1169 #ifndef Q_WS_QWS
       
  1170 Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)
       
  1171 #endif
       
  1172 
       
  1173 void QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g)
       
  1174 {
       
  1175 #ifdef QT_OPENGL_ES //###
       
  1176     Q_UNUSED(g);
       
  1177 #else
       
  1178     GLuint texId = qt_opengl_gradient_cache()->getBuffer(g, opacity, device->context());
       
  1179     glBindTexture(GL_TEXTURE_1D, texId);
       
  1180     grad_palette = texId;
       
  1181     if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient)
       
  1182         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
       
  1183     else if (g.spread() == QGradient::ReflectSpread)
       
  1184         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM);
       
  1185     else
       
  1186         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
       
  1187 
       
  1188     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
       
  1189     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       
  1190 
       
  1191 #endif
       
  1192 }
       
  1193 
       
  1194 
       
  1195 inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds)
       
  1196 {
       
  1197     current_style = brush.style();
       
  1198 
       
  1199     if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) {
       
  1200         setGLBrush(brush.color());
       
  1201         qt_glColor4ubv(brush_color);
       
  1202     }
       
  1203 
       
  1204     updateGradient(brush, bounds);
       
  1205 
       
  1206 #ifndef QT_OPENGL_ES //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients
       
  1207     glDisable(GL_TEXTURE_GEN_S);
       
  1208     glDisable(GL_TEXTURE_1D);
       
  1209 
       
  1210     if (current_style == Qt::LinearGradientPattern) {
       
  1211         if (high_quality_antialiasing || !has_fast_composition_mode) {
       
  1212             fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR;
       
  1213         } else {
       
  1214             glEnable(GL_TEXTURE_GEN_S);
       
  1215             glEnable(GL_TEXTURE_1D);
       
  1216         }
       
  1217     } else {
       
  1218         if (use_fragment_programs) {
       
  1219             if (current_style == Qt::RadialGradientPattern)
       
  1220                 fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL;
       
  1221             else if (current_style == Qt::ConicalGradientPattern)
       
  1222                 fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL;
       
  1223             else if (current_style == Qt::SolidPattern)
       
  1224                 fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID;
       
  1225             else if (current_style == Qt::TexturePattern && !brush.texture().isQBitmap())
       
  1226                 fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE;
       
  1227             else
       
  1228                 fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN;
       
  1229         }
       
  1230     }
       
  1231 #endif
       
  1232 }
       
  1233 
       
  1234 QOpenGLPaintEngine::QOpenGLPaintEngine()
       
  1235     : QPaintEngineEx(*(new QOpenGLPaintEnginePrivate))
       
  1236 {
       
  1237 }
       
  1238 
       
  1239 QOpenGLPaintEngine::~QOpenGLPaintEngine()
       
  1240 {
       
  1241 }
       
  1242 
       
  1243 bool QOpenGLPaintEngine::begin(QPaintDevice *pdev)
       
  1244 {
       
  1245     Q_D(QOpenGLPaintEngine);
       
  1246 
       
  1247     if (pdev->devType() == QInternal::OpenGL)
       
  1248         d->device = static_cast<QGLPaintDevice*>(pdev);
       
  1249     else
       
  1250         d->device = QGLPaintDevice::getDevice(pdev);
       
  1251 
       
  1252     if (!d->device)
       
  1253         return false;
       
  1254 
       
  1255     d->offscreen.setDevice(pdev);
       
  1256     d->has_fast_pen = false;
       
  1257     d->inverseScale = 1;
       
  1258     d->opacity = 1;
       
  1259     d->device->beginPaint();
       
  1260     d->matrix = QTransform();
       
  1261     d->has_antialiasing = false;
       
  1262     d->high_quality_antialiasing = false;
       
  1263 
       
  1264     QSize sz(d->device->size());
       
  1265     d->dirty_stencil = QRect(0, 0, sz.width(), sz.height());
       
  1266 
       
  1267     d->use_emulation = false;
       
  1268 
       
  1269     for (int i = 0; i < 4; ++i)
       
  1270         for (int j = 0; j < 4; ++j)
       
  1271             d->mv_matrix[i][j] = (i == j ? qreal(1) : qreal(0));
       
  1272 
       
  1273     bool has_frag_program = (QGLExtensions::glExtensions() & QGLExtensions::FragmentProgram)
       
  1274                             && (pdev->devType() != QInternal::Pixmap);
       
  1275 
       
  1276     QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
       
  1277     if (!ctx) {
       
  1278         qWarning() << "QOpenGLPaintEngine: paint device doesn't have a valid GL context.";
       
  1279         return false;
       
  1280     }
       
  1281 
       
  1282     if (has_frag_program)
       
  1283         has_frag_program = qt_resolve_frag_program_extensions(ctx) && qt_resolve_version_1_3_functions(ctx);
       
  1284 
       
  1285     d->use_stencil_method = d->device->format().stencil()
       
  1286                             && (QGLExtensions::glExtensions() & QGLExtensions::StencilWrap);
       
  1287     if (d->device->format().directRendering()
       
  1288         && (d->use_stencil_method && QGLExtensions::glExtensions() & QGLExtensions::StencilTwoSide))
       
  1289         d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx);
       
  1290 
       
  1291 #ifdef Q_WS_X11
       
  1292     static bool nvidia_workaround_needs_init = true;
       
  1293     if (nvidia_workaround_needs_init) {
       
  1294         // nvidia 9x.xx unix drivers contain a bug which requires us to
       
  1295         // call glFinish before releasing an fbo to avoid painting
       
  1296         // artifacts
       
  1297         const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
       
  1298         const int pos = versionString.indexOf("NVIDIA");
       
  1299         if (pos >= 0) {
       
  1300             const float nvidiaDriverVersion = versionString.mid(pos + strlen("NVIDIA")).toFloat();
       
  1301             qt_nvidiaFboNeedsFinish = nvidiaDriverVersion >= 90.0 && nvidiaDriverVersion < 100.0;
       
  1302         }
       
  1303         nvidia_workaround_needs_init = false;
       
  1304     }
       
  1305 #endif
       
  1306 
       
  1307 #ifndef QT_OPENGL_ES
       
  1308     if (!ctx->d_ptr->internal_context) {
       
  1309         glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
       
  1310         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
       
  1311         glPushAttrib(GL_ALL_ATTRIB_BITS);
       
  1312 
       
  1313         glDisableClientState(GL_EDGE_FLAG_ARRAY);
       
  1314         glDisableClientState(GL_INDEX_ARRAY);
       
  1315         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
       
  1316         glDisable(GL_TEXTURE_1D);
       
  1317         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
       
  1318         glPixelTransferi(GL_MAP_COLOR, false);
       
  1319         glPixelTransferi(GL_MAP_STENCIL, false);
       
  1320         glDisable(GL_TEXTURE_GEN_S);
       
  1321 
       
  1322         glPixelStorei(GL_PACK_SWAP_BYTES, false);
       
  1323         glPixelStorei(GL_PACK_LSB_FIRST, false);
       
  1324         glPixelStorei(GL_PACK_ROW_LENGTH, 0);
       
  1325         glPixelStorei(GL_PACK_SKIP_ROWS, 0);
       
  1326         glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
       
  1327         glPixelStorei(GL_PACK_ALIGNMENT, 4);
       
  1328 
       
  1329         glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
       
  1330         glPixelStorei(GL_UNPACK_LSB_FIRST, false);
       
  1331         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
       
  1332         glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       
  1333         glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
       
  1334         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
       
  1335 
       
  1336         if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
       
  1337             glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
       
  1338             glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
       
  1339             glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
       
  1340             glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
       
  1341         }
       
  1342     }
       
  1343 #endif
       
  1344 
       
  1345     if (!ctx->d_ptr->internal_context) {
       
  1346         glMatrixMode(GL_MODELVIEW);
       
  1347         glPushMatrix();
       
  1348         glMatrixMode(GL_TEXTURE);
       
  1349         glPushMatrix();
       
  1350         glLoadIdentity();
       
  1351         glDisableClientState(GL_COLOR_ARRAY);
       
  1352         glDisableClientState(GL_NORMAL_ARRAY);
       
  1353         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  1354         glDisableClientState(GL_VERTEX_ARRAY);
       
  1355 
       
  1356         if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
       
  1357             glDisable(GL_MULTISAMPLE);
       
  1358         glDisable(GL_TEXTURE_2D);
       
  1359         if (QGLExtensions::glExtensions() & QGLExtensions::TextureRectangle)
       
  1360             glDisable(GL_TEXTURE_RECTANGLE_NV);
       
  1361         glDisable(GL_STENCIL_TEST);
       
  1362         glDisable(GL_CULL_FACE);
       
  1363         glDisable(GL_LIGHTING);
       
  1364         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  1365     }
       
  1366 
       
  1367     d->offscreen.begin();
       
  1368 
       
  1369     glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
       
  1370     glMatrixMode(GL_PROJECTION);
       
  1371     glLoadIdentity();
       
  1372 #ifdef QT_OPENGL_ES
       
  1373     glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
       
  1374 #else
       
  1375     glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
       
  1376 #endif
       
  1377     glMatrixMode(GL_MODELVIEW);
       
  1378     glLoadIdentity();
       
  1379     glEnable(GL_BLEND);
       
  1380     d->composition_mode = QPainter::CompositionMode_SourceOver;
       
  1381 
       
  1382 #ifdef QT_OPENGL_ES
       
  1383     d->max_texture_size = ctx->d_func()->maxTextureSize();
       
  1384 #else
       
  1385     bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
       
  1386 
       
  1387     if (shared_ctx) {
       
  1388         d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
       
  1389     } else {
       
  1390         d->max_texture_size = ctx->d_func()->maxTextureSize();
       
  1391 
       
  1392         if (d->shader_ctx) {
       
  1393             d->shader_ctx->makeCurrent();
       
  1394             glBindTexture(GL_TEXTURE_1D, 0);
       
  1395             glDeleteTextures(1, &d->grad_palette);
       
  1396 
       
  1397             if (has_frag_program && d->use_fragment_programs)
       
  1398                 glDeleteTextures(1, &d->drawable_texture);
       
  1399             ctx->makeCurrent();
       
  1400         }
       
  1401         d->shader_ctx = d->device->context();
       
  1402         glGenTextures(1, &d->grad_palette);
       
  1403 
       
  1404         qt_mask_texture_cache()->clearCache();
       
  1405         d->use_fragment_programs = has_frag_program;
       
  1406     }
       
  1407 
       
  1408     if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
       
  1409                                      || sz.height() > d->drawable_texture_size.height()))
       
  1410     {
       
  1411         // delete old texture if size has increased, otherwise it was deleted earlier
       
  1412         if (shared_ctx)
       
  1413             glDeleteTextures(1, &d->drawable_texture);
       
  1414 
       
  1415         d->dirty_drawable_texture = true;
       
  1416         d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
       
  1417                                          qt_next_power_of_two(sz.height()));
       
  1418     }
       
  1419 #endif
       
  1420 
       
  1421     updateClipRegion(QRegion(), Qt::NoClip);
       
  1422     penChanged();
       
  1423     brushChanged();
       
  1424     opacityChanged();
       
  1425     compositionModeChanged();
       
  1426     renderHintsChanged();
       
  1427     transformChanged();
       
  1428     return true;
       
  1429 }
       
  1430 
       
  1431 bool QOpenGLPaintEngine::end()
       
  1432 {
       
  1433     Q_D(QOpenGLPaintEngine);
       
  1434     d->flushDrawQueue();
       
  1435     d->offscreen.end();
       
  1436     QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
       
  1437     if (!ctx->d_ptr->internal_context) {
       
  1438         glMatrixMode(GL_TEXTURE);
       
  1439         glPopMatrix();
       
  1440         glMatrixMode(GL_MODELVIEW);
       
  1441         glPopMatrix();
       
  1442     }
       
  1443 #ifndef QT_OPENGL_ES
       
  1444     if (ctx->d_ptr->internal_context) {
       
  1445         glDisable(GL_SCISSOR_TEST);
       
  1446     } else {
       
  1447         glMatrixMode(GL_PROJECTION);
       
  1448         glLoadMatrixd(&d->projection_matrix[0][0]);
       
  1449         glPopAttrib();
       
  1450         glPopClientAttrib();
       
  1451     }
       
  1452 #endif
       
  1453     d->device->endPaint();
       
  1454     qt_mask_texture_cache()->maintainCache();
       
  1455 
       
  1456     return true;
       
  1457 }
       
  1458 
       
  1459 void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
       
  1460 {
       
  1461     Q_D(QOpenGLPaintEngine);
       
  1462     QPaintEngine::DirtyFlags flags = state.state();
       
  1463 
       
  1464     bool update_fast_pen = false;
       
  1465 
       
  1466     if (flags & DirtyOpacity) {
       
  1467         update_fast_pen = true;
       
  1468         d->opacity = state.opacity();
       
  1469         if (d->opacity > 1.0f)
       
  1470             d->opacity = 1.0f;
       
  1471         if (d->opacity < 0.f)
       
  1472             d->opacity = 0.f;
       
  1473         // force update
       
  1474         flags |= DirtyPen;
       
  1475         flags |= DirtyBrush;
       
  1476     }
       
  1477 
       
  1478     if (flags & DirtyTransform) {
       
  1479         update_fast_pen = true;
       
  1480         updateMatrix(state.transform());
       
  1481         // brush setup depends on transform state
       
  1482         if (state.brush().style() != Qt::NoBrush)
       
  1483             flags |= DirtyBrush;
       
  1484     }
       
  1485 
       
  1486     if (flags & DirtyPen) {
       
  1487         update_fast_pen = true;
       
  1488         updatePen(state.pen());
       
  1489     }
       
  1490 
       
  1491     if (flags & (DirtyBrush | DirtyBrushOrigin)) {
       
  1492         updateBrush(state.brush(), state.brushOrigin());
       
  1493     }
       
  1494 
       
  1495     if (flags & DirtyFont) {
       
  1496         updateFont(state.font());
       
  1497     }
       
  1498 
       
  1499     if (state.state() & DirtyClipEnabled) {
       
  1500         if (state.isClipEnabled())
       
  1501             updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
       
  1502         else
       
  1503             updateClipRegion(QRegion(), Qt::NoClip);
       
  1504     }
       
  1505 
       
  1506     if (flags & DirtyClipPath) {
       
  1507         updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
       
  1508                                  state.clipPath().fillRule()),
       
  1509                          state.clipOperation());
       
  1510     }
       
  1511 
       
  1512     if (flags & DirtyClipRegion) {
       
  1513         updateClipRegion(state.clipRegion(), state.clipOperation());
       
  1514     }
       
  1515 
       
  1516     if (flags & DirtyHints) {
       
  1517         updateRenderHints(state.renderHints());
       
  1518     }
       
  1519 
       
  1520     if (flags & DirtyCompositionMode) {
       
  1521         updateCompositionMode(state.compositionMode());
       
  1522     }
       
  1523 
       
  1524     if (update_fast_pen) {
       
  1525         Q_D(QOpenGLPaintEngine);
       
  1526         qreal pen_width = d->cpen.widthF();
       
  1527         d->has_fast_pen =
       
  1528             ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
       
  1529              || d->cpen.isCosmetic())
       
  1530             && d->cpen.style() == Qt::SolidLine
       
  1531             && d->cpen.isSolid();
       
  1532     }
       
  1533 }
       
  1534 
       
  1535 
       
  1536 void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
       
  1537 {
       
  1538     inv_matrix_data[0][0] = inv_matrix.m11();
       
  1539     inv_matrix_data[1][0] = inv_matrix.m21();
       
  1540     inv_matrix_data[2][0] = inv_matrix.m31();
       
  1541 
       
  1542     inv_matrix_data[0][1] = inv_matrix.m12();
       
  1543     inv_matrix_data[1][1] = inv_matrix.m22();
       
  1544     inv_matrix_data[2][1] = inv_matrix.m32();
       
  1545 
       
  1546     inv_matrix_data[0][2] = inv_matrix.m13();
       
  1547     inv_matrix_data[1][2] = inv_matrix.m23();
       
  1548     inv_matrix_data[2][2] = inv_matrix.m33();
       
  1549 }
       
  1550 
       
  1551 
       
  1552 void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
       
  1553 {
       
  1554 #ifdef QT_OPENGL_ES
       
  1555     Q_UNUSED(brush);
       
  1556 #else
       
  1557     bool has_mirrored_repeat = QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat;
       
  1558     Qt::BrushStyle style = brush.style();
       
  1559 
       
  1560     QTransform m = brush.transform();
       
  1561 
       
  1562     if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
       
  1563         const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
       
  1564         QTransform m = brush.transform();
       
  1565         QPointF realStart = g->start();
       
  1566         QPointF realFinal = g->finalStop();
       
  1567         QPointF start = m.map(realStart);
       
  1568         QPointF stop;
       
  1569 
       
  1570         if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
       
  1571             // It is a simple uniform scale and/or translation
       
  1572             stop = m.map(realFinal);
       
  1573         } else {
       
  1574             // It is not enough to just transform the endpoints.
       
  1575             // We have to make sure the _pattern_ is transformed correctly.
       
  1576 
       
  1577             qreal odx = realFinal.x() - realStart.x();
       
  1578             qreal ody = realFinal.y() - realStart.y();
       
  1579 
       
  1580             // nx, ny and dx, dy are normal and gradient direction after transform:
       
  1581             qreal nx = m.m11()*ody - m.m21()*odx;
       
  1582             qreal ny = m.m12()*ody - m.m22()*odx;
       
  1583 
       
  1584             qreal dx = m.m11()*odx + m.m21()*ody;
       
  1585             qreal dy = m.m12()*odx + m.m22()*ody;
       
  1586 
       
  1587             qreal lx = 1 / (dx - dy*nx/ny);
       
  1588             qreal ly = 1 / (dy - dx*ny/nx);
       
  1589             qreal l = 1 / qSqrt(lx*lx+ly*ly);
       
  1590 
       
  1591             stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
       
  1592         }
       
  1593 
       
  1594         float tr[4], f;
       
  1595         tr[0] = stop.x() - start.x();
       
  1596         tr[1] = stop.y() - start.y();
       
  1597         f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
       
  1598         tr[0] *= f;
       
  1599         tr[1] *= f;
       
  1600         tr[2] = 0;
       
  1601         tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
       
  1602         brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
       
  1603         qt_glColor4ubv(brush_color);
       
  1604         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
       
  1605         glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
       
  1606     }
       
  1607 
       
  1608     if (use_fragment_programs) {
       
  1609         if (style == Qt::RadialGradientPattern) {
       
  1610             const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
       
  1611             QPointF realCenter = g->center();
       
  1612             QPointF realFocal  = g->focalPoint();
       
  1613             qreal   realRadius = g->radius();
       
  1614             QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
       
  1615             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1616             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1617             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1618 
       
  1619             setInvMatrixData(inv_matrix);
       
  1620 
       
  1621             fmp_data[0] = realCenter.x() - realFocal.x();
       
  1622             fmp_data[1] = realCenter.y() - realFocal.y();
       
  1623 
       
  1624             fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
       
  1625         } else if (style == Qt::ConicalGradientPattern) {
       
  1626             const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
       
  1627             QPointF realCenter = g->center();
       
  1628             QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
       
  1629             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1630             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1631             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1632 
       
  1633             setInvMatrixData(inv_matrix);
       
  1634 
       
  1635             angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
       
  1636         } else if (style == Qt::LinearGradientPattern) {
       
  1637             const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
       
  1638 
       
  1639             QPointF realStart = g->start();
       
  1640             QPointF realFinal = g->finalStop();
       
  1641             QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
       
  1642             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1643             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1644             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1645 
       
  1646             setInvMatrixData(inv_matrix);
       
  1647 
       
  1648             QPointF l = realFinal - realStart;
       
  1649 
       
  1650             linear_data[0] = l.x();
       
  1651             linear_data[1] = l.y();
       
  1652 
       
  1653             linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
       
  1654         } else if (style != Qt::SolidPattern) {
       
  1655             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1656             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1657             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted();
       
  1658 
       
  1659             setInvMatrixData(inv_matrix);
       
  1660         }
       
  1661     }
       
  1662 
       
  1663     if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
       
  1664         createGradientPaletteTexture(*brush.gradient());
       
  1665     }
       
  1666 #endif
       
  1667 }
       
  1668 
       
  1669 
       
  1670 class QOpenGLTessellator : public QTessellator
       
  1671 {
       
  1672 public:
       
  1673     QOpenGLTessellator() {}
       
  1674     ~QOpenGLTessellator() { }
       
  1675     QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
       
  1676 };
       
  1677 
       
  1678 QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
       
  1679 {
       
  1680     QGLTrapezoid t;
       
  1681 
       
  1682     t.top = Q27Dot5ToDouble(trap.top);
       
  1683     t.bottom = Q27Dot5ToDouble(trap.bottom);
       
  1684 
       
  1685     Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
       
  1686 
       
  1687     qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
       
  1688 
       
  1689     qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
       
  1690     qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
       
  1691     t.topLeftX = tx + m * (topLeftY - t.top);
       
  1692     t.bottomLeftX = tx + m * (topLeftY - t.bottom);
       
  1693 
       
  1694     y = trap.topRight->y - trap.bottomRight->y;
       
  1695 
       
  1696     qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
       
  1697 
       
  1698     tx = Q27Dot5ToDouble(trap.topRight->x);
       
  1699     m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
       
  1700 
       
  1701     t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
       
  1702     t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
       
  1703 
       
  1704     return t;
       
  1705 }
       
  1706 
       
  1707 class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
       
  1708 {
       
  1709 public:
       
  1710     void addTrap(const Trapezoid &trap);
       
  1711     void tessellate(const QPointF *points, int nPoints, bool winding) {
       
  1712         trapezoids.reserve(trapezoids.size() + nPoints);
       
  1713         setWinding(winding);
       
  1714         QTessellator::tessellate(points, nPoints);
       
  1715     }
       
  1716 
       
  1717     QVector<QGLTrapezoid> trapezoids;
       
  1718 };
       
  1719 
       
  1720 void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
       
  1721 {
       
  1722     trapezoids.append(toGLTrapezoid(trap));
       
  1723 }
       
  1724 
       
  1725 #ifndef QT_OPENGL_ES
       
  1726 static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
       
  1727 {
       
  1728     qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
       
  1729     qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
       
  1730 
       
  1731     if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
       
  1732         (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
       
  1733         return;
       
  1734 
       
  1735     const qreal xpadding = 1.0;
       
  1736     const qreal ypadding = 1.0;
       
  1737 
       
  1738     qreal topDist = offscreenHeight - trap.top;
       
  1739     qreal bottomDist = offscreenHeight - trap.bottom;
       
  1740 
       
  1741     qreal reciprocal = bottomDist / (bottomDist - topDist);
       
  1742 
       
  1743     qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
       
  1744     qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
       
  1745 
       
  1746     const bool topZero = qFuzzyIsNull(topDist);
       
  1747 
       
  1748     reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
       
  1749 
       
  1750     qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
       
  1751     qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
       
  1752 
       
  1753     qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA;
       
  1754     qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA;
       
  1755 
       
  1756     // fragment program needs the negative of invRightA as it mirrors the line
       
  1757     glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
       
  1758     glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
       
  1759 
       
  1760     qreal topY = trap.top - ypadding;
       
  1761     qreal bottomY = trap.bottom + ypadding;
       
  1762 
       
  1763     qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
       
  1764     qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
       
  1765     qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
       
  1766     qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
       
  1767 
       
  1768     QPointF leftNormal(1, -leftA);
       
  1769     leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
       
  1770     QPointF rightNormal(1, -rightA);
       
  1771     rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
       
  1772 
       
  1773     qreal left_padding = xpadding / qAbs(leftNormal.x());
       
  1774     qreal right_padding = xpadding / qAbs(rightNormal.x());
       
  1775 
       
  1776     glVertex2d(bounds_topLeftX - left_padding, topY);
       
  1777     glVertex2d(bounds_topRightX + right_padding, topY);
       
  1778     glVertex2d(bounds_bottomRightX + right_padding, bottomY);
       
  1779     glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
       
  1780 
       
  1781     glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
       
  1782 }
       
  1783 #endif // !Q_WS_QWS
       
  1784 
       
  1785 class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
       
  1786 {
       
  1787 public:
       
  1788     QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
       
  1789     ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
       
  1790     q_vertexType *vertices;
       
  1791     int allocated;
       
  1792     int size;
       
  1793     QRectF bounds;
       
  1794     void addTrap(const Trapezoid &trap);
       
  1795     void tessellate(const QPointF *points, int nPoints, bool winding) {
       
  1796         size = 0;
       
  1797         setWinding(winding);
       
  1798         bounds = QTessellator::tessellate(points, nPoints);
       
  1799     }
       
  1800 };
       
  1801 
       
  1802 void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
       
  1803 {
       
  1804     // On OpenGL ES we convert the trap to 2 triangles
       
  1805 #ifndef QT_OPENGL_ES
       
  1806     if (size > allocated - 8) {
       
  1807 #else
       
  1808     if (size > allocated - 12) {
       
  1809 #endif
       
  1810         allocated = qMax(2*allocated, 512);
       
  1811         vertices = (q_vertexType *)realloc(vertices, allocated * sizeof(q_vertexType));
       
  1812     }
       
  1813 
       
  1814     QGLTrapezoid t = toGLTrapezoid(trap);
       
  1815 
       
  1816 #ifndef QT_OPENGL_ES
       
  1817     vertices[size++] = f2vt(t.topLeftX);
       
  1818     vertices[size++] = f2vt(t.top);
       
  1819     vertices[size++] = f2vt(t.topRightX);
       
  1820     vertices[size++] = f2vt(t.top);
       
  1821     vertices[size++] = f2vt(t.bottomRightX);
       
  1822     vertices[size++] = f2vt(t.bottom);
       
  1823     vertices[size++] = f2vt(t.bottomLeftX);
       
  1824     vertices[size++] = f2vt(t.bottom);
       
  1825 #else
       
  1826     // First triangle
       
  1827     vertices[size++] = f2vt(t.topLeftX);
       
  1828     vertices[size++] = f2vt(t.top);
       
  1829     vertices[size++] = f2vt(t.topRightX);
       
  1830     vertices[size++] = f2vt(t.top);
       
  1831     vertices[size++] = f2vt(t.bottomRightX);
       
  1832     vertices[size++] = f2vt(t.bottom);
       
  1833 
       
  1834     // Second triangle
       
  1835     vertices[size++] = f2vt(t.bottomLeftX);
       
  1836     vertices[size++] = f2vt(t.bottom);
       
  1837     vertices[size++] = f2vt(t.topLeftX);
       
  1838     vertices[size++] = f2vt(t.top);
       
  1839     vertices[size++] = f2vt(t.bottomRightX);
       
  1840     vertices[size++] = f2vt(t.bottom);
       
  1841 #endif
       
  1842 }
       
  1843 
       
  1844 
       
  1845 void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
       
  1846                                                 Qt::FillRule fill)
       
  1847 {
       
  1848     QOpenGLTrapezoidToArrayTessellator tessellator;
       
  1849     tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
       
  1850 
       
  1851     DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
       
  1852 
       
  1853     setGradientOps(cbrush, tessellator.bounds);
       
  1854 
       
  1855     bool fast_style = current_style == Qt::LinearGradientPattern
       
  1856                       || current_style == Qt::SolidPattern;
       
  1857 
       
  1858 #ifndef QT_OPENGL_ES
       
  1859     GLenum geometry_mode = GL_QUADS;
       
  1860 #else
       
  1861     GLenum geometry_mode = GL_TRIANGLES;
       
  1862 #endif
       
  1863 
       
  1864     if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
       
  1865         composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
       
  1866     } else {
       
  1867         glVertexPointer(2, q_vertexTypeEnum, 0, tessellator.vertices);
       
  1868         glEnableClientState(GL_VERTEX_ARRAY);
       
  1869         glDrawArrays(geometry_mode, 0, tessellator.size/2);
       
  1870         glDisableClientState(GL_VERTEX_ARRAY);
       
  1871     }
       
  1872 }
       
  1873 
       
  1874 
       
  1875 inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
       
  1876 {
       
  1877     tess_points.add(QPointF(x, y));
       
  1878 
       
  1879     if (x > max_x)
       
  1880         max_x = x;
       
  1881     else if (x < min_x)
       
  1882         min_x = x;
       
  1883     if (y > max_y)
       
  1884         max_y = y;
       
  1885     else if (y < min_y)
       
  1886         min_y = y;
       
  1887 }
       
  1888 
       
  1889 inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
       
  1890 {
       
  1891     qreal inverseScaleHalf = inverseScale / 2;
       
  1892 
       
  1893     QBezier beziers[32];
       
  1894     beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
       
  1895     QBezier *b = beziers;
       
  1896     while (b >= beziers) {
       
  1897         // check if we can pop the top bezier curve from the stack
       
  1898         qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  1899         qreal d;
       
  1900         if (l > inverseScale) {
       
  1901             d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  1902                 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  1903             d /= l;
       
  1904         } else {
       
  1905             d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  1906                 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  1907         }
       
  1908         if (d < inverseScaleHalf || b == beziers + 31) {
       
  1909             // good enough, we pop it off and add the endpoint
       
  1910             lineToStencil(b->x4, b->y4);
       
  1911             --b;
       
  1912         } else {
       
  1913             // split, second half of the polygon goes lower into the stack
       
  1914             b->split(b+1, b);
       
  1915            ++b;
       
  1916         }
       
  1917     }
       
  1918 }
       
  1919 
       
  1920 
       
  1921 void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
       
  1922 {
       
  1923     const QPainterPath::Element &first = path.elementAt(0);
       
  1924     min_x = max_x = first.x;
       
  1925     min_y = max_y = first.y;
       
  1926 
       
  1927     tess_points.reset();
       
  1928     tess_points_stops.clear();
       
  1929     lineToStencil(first.x, first.y);
       
  1930 
       
  1931     for (int i=1; i<path.elementCount(); ++i) {
       
  1932         const QPainterPath::Element &e = path.elementAt(i);
       
  1933         switch (e.type) {
       
  1934         case QPainterPath::MoveToElement:
       
  1935             tess_points_stops.append(tess_points.size());
       
  1936             lineToStencil(e.x, e.y);
       
  1937             break;
       
  1938         case QPainterPath::LineToElement:
       
  1939             lineToStencil(e.x, e.y);
       
  1940             break;
       
  1941         case QPainterPath::CurveToElement:
       
  1942             curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
       
  1943             i+=2;
       
  1944             break;
       
  1945         default:
       
  1946             break;
       
  1947         }
       
  1948     }
       
  1949     lineToStencil(first.x, first.y);
       
  1950     tess_points_stops.append(tess_points.size());
       
  1951 }
       
  1952 
       
  1953 
       
  1954 void QOpenGLPaintEnginePrivate::drawVertexArrays()
       
  1955 {
       
  1956     glEnableClientState(GL_VERTEX_ARRAY);
       
  1957     glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
       
  1958     int previous_stop = 0;
       
  1959     foreach(int stop, tess_points_stops) {
       
  1960         glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
       
  1961         previous_stop = stop;
       
  1962     }
       
  1963     glDisableClientState(GL_VERTEX_ARRAY);
       
  1964 }
       
  1965 
       
  1966 void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
       
  1967 {
       
  1968     Q_Q(QOpenGLPaintEngine);
       
  1969 
       
  1970     QRect rect = dirty_stencil.boundingRect();
       
  1971 
       
  1972     if (use_system_clip)
       
  1973         rect = q->systemClip().intersected(dirty_stencil).boundingRect();
       
  1974 
       
  1975     glStencilMask(~0);
       
  1976 
       
  1977     if (!rect.isEmpty()) {
       
  1978         disableClipping();
       
  1979 
       
  1980         glEnable(GL_SCISSOR_TEST);
       
  1981 
       
  1982         const int left = rect.left();
       
  1983         const int width = rect.width();
       
  1984         const int bottom = device->size().height() - (rect.bottom() + 1);
       
  1985         const int height = rect.height();
       
  1986 
       
  1987         glScissor(left, bottom, width, height);
       
  1988 
       
  1989         glClearStencil(0);
       
  1990         glClear(GL_STENCIL_BUFFER_BIT);
       
  1991         dirty_stencil -= rect;
       
  1992 
       
  1993         glDisable(GL_SCISSOR_TEST);
       
  1994 
       
  1995         enableClipping();
       
  1996     }
       
  1997 
       
  1998     // Enable stencil.
       
  1999     glEnable(GL_STENCIL_TEST);
       
  2000 
       
  2001     // Disable color writes.
       
  2002     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
       
  2003 
       
  2004     GLuint stencilMask = 0;
       
  2005 
       
  2006     if (fillRule == Qt::OddEvenFill) {
       
  2007         stencilMask = 1;
       
  2008 
       
  2009         // Enable stencil writes.
       
  2010         glStencilMask(stencilMask);
       
  2011 
       
  2012         // Set stencil xor mode.
       
  2013         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
       
  2014 
       
  2015         // Disable stencil func.
       
  2016         glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2017 
       
  2018         drawVertexArrays();
       
  2019     } else if (fillRule == Qt::WindingFill) {
       
  2020         stencilMask = ~0;
       
  2021 
       
  2022         if (has_stencil_face_ext) {
       
  2023             QGL_FUNC_CONTEXT;
       
  2024             glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
       
  2025 
       
  2026             glActiveStencilFaceEXT(GL_BACK);
       
  2027             glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
       
  2028             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2029 
       
  2030             glActiveStencilFaceEXT(GL_FRONT);
       
  2031             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
       
  2032             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2033 
       
  2034             drawVertexArrays();
       
  2035 
       
  2036             glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
       
  2037         } else {
       
  2038             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2039             glEnable(GL_CULL_FACE);
       
  2040 
       
  2041             glCullFace(GL_BACK);
       
  2042             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
       
  2043             drawVertexArrays();
       
  2044 
       
  2045             glCullFace(GL_FRONT);
       
  2046             glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
       
  2047             drawVertexArrays();
       
  2048 
       
  2049             glDisable(GL_CULL_FACE);
       
  2050         }
       
  2051     }
       
  2052 
       
  2053     // Enable color writes.
       
  2054     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2055     glStencilMask(stencilMask);
       
  2056 
       
  2057     setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
       
  2058 
       
  2059     bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
       
  2060 
       
  2061     if (use_fragment_programs && !fast_fill) {
       
  2062         DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
       
  2063         QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
       
  2064 
       
  2065         // Enable stencil func.
       
  2066         glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
       
  2067         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
       
  2068         composite(rect);
       
  2069     } else {
       
  2070         DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
       
  2071 
       
  2072         // Enable stencil func.
       
  2073         glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
       
  2074         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
       
  2075 #ifndef QT_OPENGL_ES
       
  2076         glBegin(GL_QUADS);
       
  2077         glVertex2f(min_x, min_y);
       
  2078         glVertex2f(max_x, min_y);
       
  2079         glVertex2f(max_x, max_y);
       
  2080         glVertex2f(min_x, max_y);
       
  2081         glEnd();
       
  2082 #endif
       
  2083     }
       
  2084 
       
  2085     // Disable stencil writes.
       
  2086     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
       
  2087     glStencilMask(0);
       
  2088     glDisable(GL_STENCIL_TEST);
       
  2089 }
       
  2090 
       
  2091 void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
       
  2092 {
       
  2093     if (path.isEmpty())
       
  2094         return;
       
  2095 
       
  2096     if (use_stencil_method && !high_quality_antialiasing) {
       
  2097         pathToVertexArrays(path);
       
  2098         fillVertexArray(path.fillRule());
       
  2099         return;
       
  2100     }
       
  2101 
       
  2102     glMatrixMode(GL_MODELVIEW);
       
  2103     glLoadIdentity();
       
  2104 
       
  2105     if (high_quality_antialiasing)
       
  2106         drawOffscreenPath(path);
       
  2107     else {
       
  2108         QPolygonF poly = path.toFillPolygon(matrix);
       
  2109         fillPolygon_dev(poly.data(), poly.count(),
       
  2110                         path.fillRule());
       
  2111     }
       
  2112 
       
  2113     updateGLMatrix();
       
  2114 }
       
  2115 
       
  2116 
       
  2117 static inline bool needsEmulation(Qt::BrushStyle style)
       
  2118 {
       
  2119     return !(style == Qt::SolidPattern
       
  2120              || (style == Qt::LinearGradientPattern
       
  2121                  && (QGLExtensions::glExtensions() & QGLExtensions::MirroredRepeat)));
       
  2122 }
       
  2123 
       
  2124 void QOpenGLPaintEnginePrivate::updateUseEmulation()
       
  2125 {
       
  2126     use_emulation = !use_fragment_programs
       
  2127                     && ((has_pen && needsEmulation(pen_brush_style))
       
  2128                         || (has_brush && needsEmulation(brush_style)));
       
  2129 }
       
  2130 
       
  2131 void QOpenGLPaintEngine::updatePen(const QPen &pen)
       
  2132 {
       
  2133     Q_D(QOpenGLPaintEngine);
       
  2134     Qt::PenStyle pen_style = pen.style();
       
  2135     d->pen_brush_style = pen.brush().style();
       
  2136     d->cpen = pen;
       
  2137     d->has_pen = (pen_style != Qt::NoPen) && (d->pen_brush_style != Qt::NoBrush);
       
  2138     d->updateUseEmulation();
       
  2139 
       
  2140     if (pen.isCosmetic()) {
       
  2141         GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
       
  2142         glLineWidth(width);
       
  2143         glPointSize(width);
       
  2144     }
       
  2145 
       
  2146     if (d->pen_brush_style >= Qt::LinearGradientPattern
       
  2147         && d->pen_brush_style <= Qt::ConicalGradientPattern)
       
  2148     {
       
  2149         d->setGLPen(Qt::white);
       
  2150     } else {
       
  2151         d->setGLPen(pen.color());
       
  2152     }
       
  2153 
       
  2154     d->updateFastPen();
       
  2155 }
       
  2156 
       
  2157 void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
       
  2158 {
       
  2159     Q_D(QOpenGLPaintEngine);
       
  2160     d->cbrush = brush;
       
  2161     d->brush_style = brush.style();
       
  2162     d->brush_origin = origin;
       
  2163     d->has_brush = (d->brush_style != Qt::NoBrush);
       
  2164     d->updateUseEmulation();
       
  2165 }
       
  2166 
       
  2167 void QOpenGLPaintEngine::updateFont(const QFont &)
       
  2168 {
       
  2169 }
       
  2170 
       
  2171 void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
       
  2172 {
       
  2173     Q_D(QOpenGLPaintEngine);
       
  2174 
       
  2175     d->matrix = mtx;
       
  2176 
       
  2177     d->mv_matrix[0][0] = mtx.m11();
       
  2178     d->mv_matrix[0][1] = mtx.m12();
       
  2179     d->mv_matrix[0][2] = 0;
       
  2180     d->mv_matrix[0][3] = mtx.m13();
       
  2181 
       
  2182     d->mv_matrix[1][0] = mtx.m21();
       
  2183     d->mv_matrix[1][1] = mtx.m22();
       
  2184     d->mv_matrix[1][2] = 0;
       
  2185     d->mv_matrix[1][3] = mtx.m23();
       
  2186 
       
  2187     d->mv_matrix[2][0] = 0;
       
  2188     d->mv_matrix[2][1] = 0;
       
  2189     d->mv_matrix[2][2] = 1;
       
  2190     d->mv_matrix[2][3] = 0;
       
  2191 
       
  2192     d->mv_matrix[3][0] = mtx.dx();
       
  2193     d->mv_matrix[3][1] = mtx.dy();
       
  2194     d->mv_matrix[3][2] = 0;
       
  2195     d->mv_matrix[3][3] = mtx.m33();
       
  2196 
       
  2197     d->txop = mtx.type();
       
  2198 
       
  2199     // 1/10000 == 0.0001, so we have good enough res to cover curves
       
  2200     // that span the entire widget...
       
  2201     d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
       
  2202                                      qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
       
  2203                            qreal(0.0001));
       
  2204 
       
  2205     d->updateGLMatrix();
       
  2206     d->updateFastPen();
       
  2207 }
       
  2208 
       
  2209 void QOpenGLPaintEnginePrivate::updateGLMatrix() const
       
  2210 {
       
  2211     glMatrixMode(GL_MODELVIEW);
       
  2212 #ifndef QT_OPENGL_ES
       
  2213     glLoadMatrixd(&mv_matrix[0][0]);
       
  2214 #else
       
  2215     glLoadMatrixf(&mv_matrix[0][0]);
       
  2216 #endif
       
  2217 }
       
  2218 
       
  2219 void QOpenGLPaintEnginePrivate::disableClipping()
       
  2220 {
       
  2221     glDisable(GL_DEPTH_TEST);
       
  2222     glDisable(GL_SCISSOR_TEST);
       
  2223 }
       
  2224 
       
  2225 void QOpenGLPaintEnginePrivate::enableClipping()
       
  2226 {
       
  2227     Q_Q(QOpenGLPaintEngine);
       
  2228     if (!q->state()->hasClipping)
       
  2229         return;
       
  2230 
       
  2231     if (q->state()->fastClip.isEmpty())
       
  2232         glEnable(GL_DEPTH_TEST);
       
  2233     else
       
  2234         updateDepthClip(); // this will enable the scissor test
       
  2235 }
       
  2236 
       
  2237 void QOpenGLPaintEnginePrivate::updateDepthClip()
       
  2238 {
       
  2239     Q_Q(QOpenGLPaintEngine);
       
  2240 
       
  2241     ++q->state()->depthClipId;
       
  2242 
       
  2243     glDisable(GL_DEPTH_TEST);
       
  2244     glDisable(GL_SCISSOR_TEST);
       
  2245 
       
  2246     if (!q->state()->hasClipping)
       
  2247         return;
       
  2248 
       
  2249     QRect fastClip;
       
  2250     if (q->state()->clipEnabled) {
       
  2251         fastClip = q->state()->fastClip;
       
  2252     } else if (use_system_clip && q->systemClip().rects().count() == 1) {
       
  2253         fastClip = q->systemClip().rects().at(0);
       
  2254     }
       
  2255 
       
  2256     if (!fastClip.isEmpty()) {
       
  2257         glEnable(GL_SCISSOR_TEST);
       
  2258 
       
  2259         const int left = fastClip.left();
       
  2260         const int width = fastClip.width();
       
  2261         const int bottom = device->size().height() - (fastClip.bottom() + 1);
       
  2262         const int height = fastClip.height();
       
  2263 
       
  2264         glScissor(left, bottom, width, height);
       
  2265         return;
       
  2266     }
       
  2267 
       
  2268 #if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_1_CL)
       
  2269     glClearDepthf(0.0f);
       
  2270 #else
       
  2271     glClearDepth(0.0f);
       
  2272 #endif
       
  2273 
       
  2274     glEnable(GL_DEPTH_TEST);
       
  2275     glDepthMask(GL_TRUE);
       
  2276     glClear(GL_DEPTH_BUFFER_BIT);
       
  2277 
       
  2278     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
       
  2279     glDepthFunc(GL_ALWAYS);
       
  2280 
       
  2281     const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
       
  2282 
       
  2283     // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
       
  2284     QDataBuffer<q_vertexType> clipVertex(rects.size()*2*3*2);
       
  2285     for (int i = 0; i < rects.size(); ++i) {
       
  2286         q_vertexType x = i2vt(rects.at(i).left());
       
  2287         q_vertexType w = i2vt(rects.at(i).width());
       
  2288         q_vertexType h = i2vt(rects.at(i).height());
       
  2289         q_vertexType y = i2vt(rects.at(i).top());
       
  2290 
       
  2291         // First triangle
       
  2292         clipVertex.add(x);
       
  2293         clipVertex.add(y);
       
  2294 
       
  2295         clipVertex.add(x);
       
  2296         clipVertex.add(y + h);
       
  2297 
       
  2298         clipVertex.add(x + w);
       
  2299         clipVertex.add(y);
       
  2300 
       
  2301         // Second triangle
       
  2302         clipVertex.add(x);
       
  2303         clipVertex.add(y + h);
       
  2304 
       
  2305         clipVertex.add(x + w);
       
  2306         clipVertex.add(y + h);
       
  2307 
       
  2308         clipVertex.add (x + w);
       
  2309         clipVertex.add(y);
       
  2310     }
       
  2311 
       
  2312     if (rects.size()) {
       
  2313         glMatrixMode(GL_MODELVIEW);
       
  2314         glLoadIdentity();
       
  2315 
       
  2316         glEnableClientState(GL_VERTEX_ARRAY);
       
  2317         glVertexPointer(2, q_vertexTypeEnum, 0, clipVertex.data());
       
  2318 
       
  2319         glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
       
  2320         glDisableClientState(GL_VERTEX_ARRAY);
       
  2321         updateGLMatrix();
       
  2322     }
       
  2323 
       
  2324     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2325     glDepthMask(GL_FALSE);
       
  2326     glDepthFunc(GL_LEQUAL);
       
  2327 }
       
  2328 
       
  2329 void QOpenGLPaintEnginePrivate::systemStateChanged()
       
  2330 {
       
  2331     Q_Q(QOpenGLPaintEngine);
       
  2332     if (q->painter()->hasClipping())
       
  2333         q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
       
  2334     else
       
  2335         q->updateClipRegion(QRegion(), Qt::NoClip);
       
  2336 }
       
  2337 
       
  2338 void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
       
  2339 {
       
  2340     Q_D(QOpenGLPaintEngine);
       
  2341 
       
  2342     // clipping is only supported when a stencil or depth buffer is
       
  2343     // available
       
  2344     if (!d->device->format().depth())
       
  2345         return;
       
  2346 
       
  2347     d->use_system_clip = false;
       
  2348     QRegion sysClip = systemClip();
       
  2349     if (!sysClip.isEmpty()) {
       
  2350         if (d->pdev->devType() != QInternal::Widget) {
       
  2351             d->use_system_clip = true;
       
  2352         } else {
       
  2353 #ifndef Q_WS_QWS
       
  2354             // Only use the system clip if we're currently rendering a widget with a GL painter.
       
  2355             if (d->currentClipWidget) {
       
  2356                 QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
       
  2357                 d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
       
  2358             }
       
  2359 #endif
       
  2360         }
       
  2361     }
       
  2362 
       
  2363     d->flushDrawQueue();
       
  2364 
       
  2365     if (op == Qt::NoClip && !d->use_system_clip) {
       
  2366         state()->hasClipping = false;
       
  2367         state()->clipRegion = QRegion();
       
  2368         d->updateDepthClip();
       
  2369         return;
       
  2370     }
       
  2371 
       
  2372     bool isScreenClip = false;
       
  2373     if (!d->use_system_clip) {
       
  2374         QVector<QRect> untransformedRects = clipRegion.rects();
       
  2375 
       
  2376         if (untransformedRects.size() == 1) {
       
  2377             QPainterPath path;
       
  2378             path.addRect(untransformedRects[0]);
       
  2379             path = d->matrix.map(path);
       
  2380 
       
  2381             if (path.contains(QRectF(QPointF(), d->device->size())))
       
  2382                 isScreenClip = true;
       
  2383         }
       
  2384     }
       
  2385 
       
  2386     QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
       
  2387     switch (op) {
       
  2388     case Qt::NoClip:
       
  2389         if (!d->use_system_clip)
       
  2390             break;
       
  2391         state()->clipRegion = sysClip;
       
  2392         break;
       
  2393     case Qt::IntersectClip:
       
  2394         if (isScreenClip)
       
  2395             return;
       
  2396         if (state()->hasClipping) {
       
  2397             state()->clipRegion &= region;
       
  2398             break;
       
  2399         }
       
  2400         // fall through
       
  2401     case Qt::ReplaceClip:
       
  2402         if (d->use_system_clip)
       
  2403             state()->clipRegion = region & sysClip;
       
  2404         else
       
  2405             state()->clipRegion = region;
       
  2406         break;
       
  2407     case Qt::UniteClip:
       
  2408         state()->clipRegion |= region;
       
  2409         if (d->use_system_clip)
       
  2410             state()->clipRegion &= sysClip;
       
  2411         break;
       
  2412     default:
       
  2413         break;
       
  2414     }
       
  2415 
       
  2416     if (isScreenClip) {
       
  2417         state()->hasClipping = false;
       
  2418         state()->clipRegion = QRegion();
       
  2419     } else {
       
  2420         state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
       
  2421     }
       
  2422 
       
  2423     if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
       
  2424         state()->fastClip = state()->clipRegion.rects().at(0);
       
  2425     else
       
  2426         state()->fastClip = QRect();
       
  2427 
       
  2428     d->updateDepthClip();
       
  2429 }
       
  2430 
       
  2431 void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
       
  2432 {
       
  2433     Q_D(QOpenGLPaintEngine);
       
  2434 
       
  2435     d->flushDrawQueue();
       
  2436     d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
       
  2437     if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
       
  2438         if (d->use_fragment_programs && QGLOffscreen::isSupported()
       
  2439             && (hints & QPainter::HighQualityAntialiasing)) {
       
  2440             d->high_quality_antialiasing = true;
       
  2441         } else {
       
  2442             d->high_quality_antialiasing = false;
       
  2443             if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
       
  2444                 glEnable(GL_MULTISAMPLE);
       
  2445         }
       
  2446     } else {
       
  2447         d->high_quality_antialiasing = false;
       
  2448         if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
       
  2449             glDisable(GL_MULTISAMPLE);
       
  2450     }
       
  2451 
       
  2452     if (d->high_quality_antialiasing) {
       
  2453         d->offscreen.initialize();
       
  2454 
       
  2455         if (!d->offscreen.isValid()) {
       
  2456             DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
       
  2457             d->high_quality_antialiasing = false;
       
  2458             if (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers)
       
  2459                 glEnable(GL_MULTISAMPLE);
       
  2460         }
       
  2461     }
       
  2462 
       
  2463     d->has_antialiasing = d->high_quality_antialiasing
       
  2464                           || ((hints & QPainter::Antialiasing)
       
  2465                               && (QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers));
       
  2466 }
       
  2467 
       
  2468 
       
  2469 void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
       
  2470 {
       
  2471     porterduff_ab_data[0] = a;
       
  2472     porterduff_ab_data[1] = b;
       
  2473 
       
  2474     porterduff_xyz_data[0] = x;
       
  2475     porterduff_xyz_data[1] = y;
       
  2476     porterduff_xyz_data[2] = z;
       
  2477 }
       
  2478 
       
  2479 
       
  2480 void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
       
  2481 {
       
  2482     Q_D(QOpenGLPaintEngine);
       
  2483 
       
  2484     if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
       
  2485         composition_mode = QPainter::CompositionMode_SourceOver;
       
  2486 
       
  2487     d->composition_mode = composition_mode;
       
  2488 
       
  2489     d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
       
  2490                                    || composition_mode == QPainter::CompositionMode_SourceOver
       
  2491                                    || composition_mode == QPainter::CompositionMode_Destination
       
  2492                                    || composition_mode == QPainter::CompositionMode_DestinationOver
       
  2493                                    || composition_mode == QPainter::CompositionMode_DestinationOut
       
  2494                                    || composition_mode == QPainter::CompositionMode_SourceAtop
       
  2495                                    || composition_mode == QPainter::CompositionMode_Xor
       
  2496                                    || composition_mode == QPainter::CompositionMode_Plus;
       
  2497 
       
  2498     if (d->has_fast_composition_mode)
       
  2499         d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
       
  2500     else if (composition_mode <= QPainter::CompositionMode_Plus)
       
  2501         d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
       
  2502     else
       
  2503         switch (composition_mode) {
       
  2504         case QPainter::CompositionMode_Multiply:
       
  2505             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
       
  2506             break;
       
  2507         case QPainter::CompositionMode_Screen:
       
  2508             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
       
  2509             break;
       
  2510         case QPainter::CompositionMode_Overlay:
       
  2511             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
       
  2512             break;
       
  2513         case QPainter::CompositionMode_Darken:
       
  2514             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
       
  2515             break;
       
  2516         case QPainter::CompositionMode_Lighten:
       
  2517             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
       
  2518             break;
       
  2519         case QPainter::CompositionMode_ColorDodge:
       
  2520             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
       
  2521             break;
       
  2522         case QPainter::CompositionMode_ColorBurn:
       
  2523             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
       
  2524             break;
       
  2525         case QPainter::CompositionMode_HardLight:
       
  2526             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
       
  2527             break;
       
  2528         case QPainter::CompositionMode_SoftLight:
       
  2529             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
       
  2530             break;
       
  2531         case QPainter::CompositionMode_Difference:
       
  2532             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
       
  2533             break;
       
  2534         case QPainter::CompositionMode_Exclusion:
       
  2535             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
       
  2536             break;
       
  2537         default:
       
  2538             Q_ASSERT(false);
       
  2539         }
       
  2540 
       
  2541     switch(composition_mode) {
       
  2542     case QPainter::CompositionMode_DestinationOver:
       
  2543         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
       
  2544         d->setPorterDuffData(0, 1, 1, 1, 1);
       
  2545         break;
       
  2546     case QPainter::CompositionMode_Clear:
       
  2547         glBlendFunc(GL_ZERO, GL_ZERO);
       
  2548         d->setPorterDuffData(0, 0, 0, 0, 0);
       
  2549         break;
       
  2550     case QPainter::CompositionMode_Source:
       
  2551         glBlendFunc(GL_ONE, GL_ZERO);
       
  2552         d->setPorterDuffData(1, 0, 1, 1, 0);
       
  2553         break;
       
  2554     case QPainter::CompositionMode_Destination:
       
  2555         glBlendFunc(GL_ZERO, GL_ONE);
       
  2556         d->setPorterDuffData(0, 1, 1, 0, 1);
       
  2557         break;
       
  2558     case QPainter::CompositionMode_SourceIn:
       
  2559         glBlendFunc(GL_DST_ALPHA, GL_ZERO);
       
  2560         d->setPorterDuffData(1, 0, 1, 0, 0);
       
  2561         break;
       
  2562     case QPainter::CompositionMode_DestinationIn:
       
  2563         glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
       
  2564         d->setPorterDuffData(0, 1, 1, 0, 0);
       
  2565         break;
       
  2566     case QPainter::CompositionMode_SourceOut:
       
  2567         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
       
  2568         d->setPorterDuffData(0, 0, 0, 1, 0);
       
  2569         break;
       
  2570     case QPainter::CompositionMode_DestinationOut:
       
  2571         glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
       
  2572         d->setPorterDuffData(0, 0, 0, 0, 1);
       
  2573         break;
       
  2574     case QPainter::CompositionMode_SourceAtop:
       
  2575         glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
  2576         d->setPorterDuffData(1, 0, 1, 0, 1);
       
  2577         break;
       
  2578     case QPainter::CompositionMode_DestinationAtop:
       
  2579         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
       
  2580         d->setPorterDuffData(0, 1, 1, 1, 0);
       
  2581         break;
       
  2582     case QPainter::CompositionMode_Xor:
       
  2583         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
  2584         d->setPorterDuffData(0, 0, 0, 1, 1);
       
  2585         break;
       
  2586     case QPainter::CompositionMode_SourceOver:
       
  2587         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
       
  2588         d->setPorterDuffData(1, 0, 1, 1, 1);
       
  2589         break;
       
  2590     case QPainter::CompositionMode_Plus:
       
  2591         glBlendFunc(GL_ONE, GL_ONE);
       
  2592         d->setPorterDuffData(1, 1, 1, 1, 1);
       
  2593         break;
       
  2594     default:
       
  2595         break;
       
  2596     }
       
  2597 }
       
  2598 
       
  2599 class QGLMaskGenerator
       
  2600 {
       
  2601 public:
       
  2602     QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
       
  2603         : p(path),
       
  2604           m(matrix),
       
  2605           w(stroke_width)
       
  2606     {
       
  2607     }
       
  2608 
       
  2609     virtual QRect screenRect() = 0;
       
  2610     virtual void drawMask(const QRect &rect) = 0;
       
  2611 
       
  2612     QPainterPath path() const { return p; }
       
  2613     QTransform matrix() const { return m; }
       
  2614     qreal strokeWidth() const { return w; }
       
  2615 
       
  2616     virtual ~QGLMaskGenerator() {}
       
  2617 
       
  2618 private:
       
  2619     QPainterPath p;
       
  2620     QTransform m;
       
  2621     qreal w;
       
  2622 };
       
  2623 
       
  2624 void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
       
  2625 {
       
  2626     Q_ASSERT(sz.width() == sz.height());
       
  2627 
       
  2628     if (offscreenSize != sz) {
       
  2629         offscreenSize = sz;
       
  2630         clearCache();
       
  2631     }
       
  2632 }
       
  2633 
       
  2634 void QGLMaskTextureCache::clearCache()
       
  2635 {
       
  2636     cache.clear();
       
  2637 
       
  2638     int quad_tree_size = 1;
       
  2639 
       
  2640     for (int i = block_size; i < offscreenSize.width(); i *= 2)
       
  2641         quad_tree_size += quad_tree_size * 4;
       
  2642 
       
  2643     for (int i = 0; i < 4; ++i) {
       
  2644         occupied_quadtree[i].resize(quad_tree_size);
       
  2645 
       
  2646         occupied_quadtree[i][0].key = 0;
       
  2647         occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
       
  2648         occupied_quadtree[i][0].largest_used_block = 0;
       
  2649 
       
  2650         DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
       
  2651     }
       
  2652 }
       
  2653 
       
  2654 void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
       
  2655 {
       
  2656     drawableSize = sz;
       
  2657 }
       
  2658 
       
  2659 void QGLMaskTextureCache::maintainCache()
       
  2660 {
       
  2661     QGLTextureCacheHash::iterator it = cache.begin();
       
  2662     QGLTextureCacheHash::iterator end = cache.end();
       
  2663 
       
  2664     while (it != end) {
       
  2665         CacheInfo &cache_info = it.value();
       
  2666         ++cache_info.age;
       
  2667 
       
  2668         if (cache_info.age > 1) {
       
  2669             quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
       
  2670             it = cache.erase(it);
       
  2671         } else {
       
  2672             ++it;
       
  2673         }
       
  2674     }
       
  2675 }
       
  2676 
       
  2677 //#define DISABLE_MASK_CACHE
       
  2678 
       
  2679 QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
       
  2680 {
       
  2681 #ifndef DISABLE_MASK_CACHE
       
  2682     engine = e;
       
  2683 
       
  2684     quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
       
  2685 
       
  2686     if (key == 0)
       
  2687         key = 1;
       
  2688 
       
  2689     CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
       
  2690 
       
  2691     QGLTextureCacheHash::iterator it = cache.find(key);
       
  2692 
       
  2693     while (it != cache.end() && it.key() == key) {
       
  2694         CacheInfo &cache_info = it.value();
       
  2695         if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
       
  2696             DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
       
  2697 
       
  2698             cache_info.age = 0;
       
  2699             return cache_info.loc;
       
  2700         }
       
  2701         ++it;
       
  2702     }
       
  2703 
       
  2704     // mask was not found, create new mask
       
  2705 
       
  2706     DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
       
  2707 
       
  2708     createMask(key, info, maskGenerator);
       
  2709 
       
  2710     cache.insert(key, info);
       
  2711 
       
  2712     return info.loc;
       
  2713 #else
       
  2714     CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
       
  2715     createMask(0, info, maskGenerator);
       
  2716     return info.loc;
       
  2717 #endif
       
  2718 }
       
  2719 
       
  2720 #ifndef FloatToQuint64
       
  2721 #define FloatToQuint64(i) (quint64)((i) * 32)
       
  2722 #endif
       
  2723 
       
  2724 quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
       
  2725 {
       
  2726     Q_ASSERT(sizeof(quint64) == 8);
       
  2727 
       
  2728     quint64 h = 0;
       
  2729 
       
  2730     for (int i = 0; i < p.elementCount(); ++i) {
       
  2731         h += FloatToQuint64(p.elementAt(i).x) << 32;
       
  2732         h += FloatToQuint64(p.elementAt(i).y);
       
  2733         h += p.elementAt(i).type;
       
  2734     }
       
  2735 
       
  2736     h += FloatToQuint64(m.m11());
       
  2737 #ifndef Q_OS_WINCE    //  ###
       
  2738     //Compiler crashes for arm on WinCE
       
  2739     h += FloatToQuint64(m.m12()) << 4;
       
  2740     h += FloatToQuint64(m.m13()) << 8;
       
  2741     h += FloatToQuint64(m.m21()) << 12;
       
  2742     h += FloatToQuint64(m.m22()) << 16;
       
  2743     h += FloatToQuint64(m.m23()) << 20;
       
  2744     h += FloatToQuint64(m.m31()) << 24;
       
  2745     h += FloatToQuint64(m.m32()) << 28;
       
  2746 #endif
       
  2747     h += FloatToQuint64(m.m33()) << 32;
       
  2748 
       
  2749     h += FloatToQuint64(w);
       
  2750 
       
  2751     return h;
       
  2752 }
       
  2753 
       
  2754 void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
       
  2755 {
       
  2756     info.loc.screen_rect = maskGenerator.screenRect();
       
  2757 
       
  2758     if (info.loc.screen_rect.isEmpty()) {
       
  2759         info.loc.channel = 0;
       
  2760         info.loc.rect = QRect();
       
  2761         return;
       
  2762     }
       
  2763 
       
  2764     quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
       
  2765 
       
  2766     int ch = info.loc.channel;
       
  2767     glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
       
  2768 
       
  2769     maskGenerator.drawMask(info.loc.rect);
       
  2770 
       
  2771     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2772 }
       
  2773 
       
  2774 int QGLMaskTextureCache::quadtreeBlocksize(int node)
       
  2775 {
       
  2776     DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
       
  2777 
       
  2778     int blocksize = offscreenSize.width();
       
  2779 
       
  2780     while (node) {
       
  2781         node = (node - 1) / 4;
       
  2782         blocksize /= 2;
       
  2783     }
       
  2784 
       
  2785     return blocksize;
       
  2786 }
       
  2787 
       
  2788 QPoint QGLMaskTextureCache::quadtreeLocation(int node)
       
  2789 {
       
  2790     QPoint location;
       
  2791     int blocksize = quadtreeBlocksize(node);
       
  2792 
       
  2793     while (node) {
       
  2794         --node;
       
  2795 
       
  2796         if (node & 1)
       
  2797             location.setX(location.x() + blocksize);
       
  2798 
       
  2799         if (node & 2)
       
  2800             location.setY(location.y() + blocksize);
       
  2801 
       
  2802         node /= 4;
       
  2803         blocksize *= 2;
       
  2804     }
       
  2805 
       
  2806     return location;
       
  2807 }
       
  2808 
       
  2809 void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
       
  2810 {
       
  2811     while (node) {
       
  2812         node = (node - 1) / 4;
       
  2813 
       
  2814         int first_child = node * 4 + 1;
       
  2815 
       
  2816         int largest_available = 0;
       
  2817         int largest_used = 0;
       
  2818 
       
  2819         bool all_empty = true;
       
  2820 
       
  2821         for (int i = 0; i < 4; ++i) {
       
  2822             largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
       
  2823             largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
       
  2824 
       
  2825             if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
       
  2826                 all_empty = false;
       
  2827         }
       
  2828 
       
  2829         current_block_size *= 2;
       
  2830 
       
  2831         if (all_empty) {
       
  2832             occupied_quadtree[channel][node].largest_available_block = current_block_size;
       
  2833             occupied_quadtree[channel][node].largest_used_block = 0;
       
  2834         } else {
       
  2835             occupied_quadtree[channel][node].largest_available_block = largest_available;
       
  2836             occupied_quadtree[channel][node].largest_used_block = largest_used;
       
  2837         }
       
  2838     }
       
  2839 }
       
  2840 
       
  2841 void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
       
  2842 {
       
  2843     int current_block_size = quadtreeBlocksize(node);
       
  2844     QPoint location = quadtreeLocation(node);
       
  2845     QRect relative = rect.translated(-location);
       
  2846 
       
  2847     if (relative.left() >= current_block_size || relative.top() >= current_block_size
       
  2848         || relative.right() < 0 || relative.bottom() < 0)
       
  2849         return;
       
  2850 
       
  2851     if (current_block_size == block_size // no more refining possible
       
  2852         || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
       
  2853             && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
       
  2854     {
       
  2855         if (key != 0) {
       
  2856             occupied_quadtree[channel][node].largest_available_block = 0;
       
  2857             occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
       
  2858         } else {
       
  2859             occupied_quadtree[channel][node].largest_available_block = current_block_size;
       
  2860             occupied_quadtree[channel][node].largest_used_block = 0;
       
  2861         }
       
  2862 
       
  2863         occupied_quadtree[channel][node].key = key;
       
  2864 
       
  2865         quadtreeUpdate(channel, node, current_block_size);
       
  2866     } else {
       
  2867         if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
       
  2868             // refining the quad tree, initialize child nodes
       
  2869             int half_block_size = current_block_size / 2;
       
  2870 
       
  2871             int temp = node * 4 + 1;
       
  2872             for (int sibling = 0; sibling < 4; ++sibling) {
       
  2873                 occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
       
  2874                 occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
       
  2875                 occupied_quadtree[channel][temp + sibling].key = 0;
       
  2876             }
       
  2877         }
       
  2878 
       
  2879         node = node * 4 + 1;
       
  2880 
       
  2881         for (int sibling = 0; sibling < 4; ++sibling)
       
  2882             quadtreeInsert(channel, key, rect, node + sibling);
       
  2883     }
       
  2884 }
       
  2885 
       
  2886 void QGLMaskTextureCache::quadtreeClear(int channel, const QRect &rect, int node)
       
  2887 {
       
  2888     const quint64 &key = occupied_quadtree[channel][node].key;
       
  2889 
       
  2890     int current_block_size = quadtreeBlocksize(node);
       
  2891     QPoint location = quadtreeLocation(node);
       
  2892 
       
  2893     QRect relative = rect.translated(-location);
       
  2894 
       
  2895     if (relative.left() >= current_block_size || relative.top() >= current_block_size
       
  2896         || relative.right() < 0 || relative.bottom() < 0)
       
  2897         return;
       
  2898 
       
  2899     if (key != 0) {
       
  2900         QGLTextureCacheHash::iterator it = cache.find(key);
       
  2901 
       
  2902         Q_ASSERT(it != cache.end());
       
  2903 
       
  2904         while (it != cache.end() && it.key() == key) {
       
  2905             const CacheInfo &cache_info = it.value();
       
  2906 
       
  2907             if (cache_info.loc.channel == channel
       
  2908                 && cache_info.loc.rect.left() <= location.x()
       
  2909                 && cache_info.loc.rect.top() <= location.y()
       
  2910                 && cache_info.loc.rect.right() >= location.x()
       
  2911                 && cache_info.loc.rect.bottom() >= location.y())
       
  2912             {
       
  2913                 quadtreeInsert(channel, 0, cache_info.loc.rect);
       
  2914                 engine->cacheItemErased(channel, cache_info.loc.rect);
       
  2915                 cache.erase(it);
       
  2916                 goto found;
       
  2917             } else {
       
  2918                 ++it;
       
  2919             }
       
  2920         }
       
  2921 
       
  2922         // if we don't find the key there's an error in the quadtree
       
  2923         Q_ASSERT(false);
       
  2924 found:
       
  2925         Q_ASSERT(occupied_quadtree[channel][node].key == 0);
       
  2926     } else if (occupied_quadtree[channel][node].largest_available_block < current_block_size) {
       
  2927         Q_ASSERT(current_block_size >= block_size);
       
  2928 
       
  2929         node = node * 4 + 1;
       
  2930 
       
  2931         for (int sibling = 0; sibling < 4; ++sibling)
       
  2932             quadtreeClear(channel, rect, node + sibling);
       
  2933     }
       
  2934 }
       
  2935 
       
  2936 bool QGLMaskTextureCache::quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel)
       
  2937 {
       
  2938     int needed_block_size = qMax(1, qMax(size.width(), size.height()));
       
  2939 
       
  2940     for (int i = 0; i < 4; ++i) {
       
  2941         int current_block_size = offscreenSize.width();
       
  2942 
       
  2943         if (occupied_quadtree[i][0].largest_available_block >= needed_block_size) {
       
  2944             int node = 0;
       
  2945 
       
  2946             while (current_block_size != occupied_quadtree[i][node].largest_available_block) {
       
  2947                 Q_ASSERT(current_block_size > block_size);
       
  2948                 Q_ASSERT(current_block_size > occupied_quadtree[i][node].largest_available_block);
       
  2949 
       
  2950                 node = node * 4 + 1;
       
  2951                 current_block_size /= 2;
       
  2952 
       
  2953                 int sibling = 0;
       
  2954 
       
  2955                 while (occupied_quadtree[i][node + sibling].largest_available_block < needed_block_size)
       
  2956                     ++sibling;
       
  2957 
       
  2958                 Q_ASSERT(sibling < 4);
       
  2959                 node += sibling;
       
  2960             }
       
  2961 
       
  2962             *channel = i;
       
  2963             *rect = QRect(quadtreeLocation(node), size);
       
  2964 
       
  2965             return true;
       
  2966         }
       
  2967     }
       
  2968 
       
  2969     return false;
       
  2970 }
       
  2971 
       
  2972 void QGLMaskTextureCache::quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel)
       
  2973 {
       
  2974     // try to pick small masks to throw out, as large masks are more expensive to recompute
       
  2975     *channel = qrand() % 4;
       
  2976     for (int i = 0; i < 4; ++i)
       
  2977         if (occupied_quadtree[i][0].largest_used_block < occupied_quadtree[*channel][0].largest_used_block)
       
  2978             *channel = i;
       
  2979 
       
  2980     int needed_block_size = qt_next_power_of_two(qMax(1, qMax(size.width(), size.height())));
       
  2981 
       
  2982     int node = 0;
       
  2983     int current_block_size = offscreenSize.width();
       
  2984 
       
  2985     while (current_block_size > block_size
       
  2986            && current_block_size >= needed_block_size * 2
       
  2987            && occupied_quadtree[*channel][node].key == 0)
       
  2988     {
       
  2989         node = node * 4 + 1;
       
  2990 
       
  2991         int sibling = 0;
       
  2992 
       
  2993         for (int i = 1; i < 4; ++i) {
       
  2994             if (occupied_quadtree[*channel][node + i].largest_used_block
       
  2995                 <= occupied_quadtree[*channel][node + sibling].largest_used_block)
       
  2996             {
       
  2997                 sibling = i;
       
  2998             }
       
  2999         }
       
  3000 
       
  3001         node += sibling;
       
  3002         current_block_size /= 2;
       
  3003     }
       
  3004 
       
  3005     *rect = QRect(quadtreeLocation(node), size);
       
  3006 }
       
  3007 
       
  3008 void QGLMaskTextureCache::quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel)
       
  3009 {
       
  3010 #ifndef DISABLE_MASK_CACHE
       
  3011     if (!quadtreeFindAvailableLocation(size, rect, channel)) {
       
  3012         quadtreeFindExistingLocation(size, rect, channel);
       
  3013         quadtreeClear(*channel, *rect);
       
  3014     }
       
  3015 
       
  3016     quadtreeInsert(*channel, key, *rect);
       
  3017 #else
       
  3018     *channel = 0;
       
  3019     *rect = QRect(QPoint(), size);
       
  3020 #endif
       
  3021 }
       
  3022 
       
  3023 class QGLTrapezoidMaskGenerator : public QGLMaskGenerator
       
  3024 {
       
  3025 public:
       
  3026     QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, qreal strokeWidth = -1.0);
       
  3027 
       
  3028     QRect screenRect();
       
  3029     void drawMask(const QRect &rect);
       
  3030 
       
  3031 private:
       
  3032     QRect screen_rect;
       
  3033     bool has_screen_rect;
       
  3034 
       
  3035     QGLOffscreen *offscreen;
       
  3036 
       
  3037     GLuint maskFragmentProgram;
       
  3038 
       
  3039     virtual QVector<QGLTrapezoid> generateTrapezoids() = 0;
       
  3040     virtual QRect computeScreenRect() = 0;
       
  3041 };
       
  3042 
       
  3043 class QGLPathMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3044 {
       
  3045 public:
       
  3046     QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3047 
       
  3048 private:
       
  3049     QVector<QGLTrapezoid> generateTrapezoids();
       
  3050     QRect computeScreenRect();
       
  3051 
       
  3052     QPolygonF poly;
       
  3053 };
       
  3054 
       
  3055 class QGLLineMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3056 {
       
  3057 public:
       
  3058     QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3059 
       
  3060 private:
       
  3061     QVector<QGLTrapezoid> generateTrapezoids();
       
  3062     QRect computeScreenRect();
       
  3063 
       
  3064     QPainterPath transformedPath;
       
  3065 };
       
  3066 
       
  3067 class QGLRectMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3068 {
       
  3069 public:
       
  3070     QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3071 
       
  3072 private:
       
  3073     QVector<QGLTrapezoid> generateTrapezoids();
       
  3074     QRect computeScreenRect();
       
  3075 
       
  3076     QPainterPath transformedPath;
       
  3077 };
       
  3078 
       
  3079 class QGLEllipseMaskGenerator : public QGLMaskGenerator
       
  3080 {
       
  3081 public:
       
  3082     QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, int *maskVariableLocations);
       
  3083 
       
  3084     QRect screenRect();
       
  3085     void drawMask(const QRect &rect);
       
  3086 
       
  3087 private:
       
  3088     QRect screen_rect;
       
  3089 
       
  3090     QRectF ellipseRect;
       
  3091 
       
  3092     QGLOffscreen *offscreen;
       
  3093 
       
  3094     GLuint maskFragmentProgram;
       
  3095 
       
  3096     int *maskVariableLocations;
       
  3097 
       
  3098     float vertexArray[4 * 2];
       
  3099 };
       
  3100 
       
  3101 QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program, qreal stroke_width)
       
  3102     : QGLMaskGenerator(path, matrix, stroke_width)
       
  3103     ,  has_screen_rect(false)
       
  3104     ,  offscreen(&offs)
       
  3105     ,  maskFragmentProgram(program)
       
  3106 {
       
  3107 }
       
  3108 
       
  3109 extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
       
  3110 extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array);
       
  3111 
       
  3112 void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
       
  3113 {
       
  3114 #ifdef QT_OPENGL_ES
       
  3115     Q_UNUSED(rect);
       
  3116 #else
       
  3117     glMatrixMode(GL_MODELVIEW);
       
  3118     glPushMatrix();
       
  3119     glLoadIdentity();
       
  3120 
       
  3121     QGLContext *ctx = offscreen->context();
       
  3122     offscreen->bind();
       
  3123 
       
  3124     glDisable(GL_TEXTURE_GEN_S);
       
  3125     glDisable(GL_TEXTURE_1D);
       
  3126 
       
  3127     GLfloat vertexArray[4 * 2];
       
  3128     qt_add_rect_to_array(rect, vertexArray);
       
  3129 
       
  3130     bool needs_scissor = rect != screen_rect;
       
  3131 
       
  3132     if (needs_scissor) {
       
  3133         glEnable(GL_SCISSOR_TEST);
       
  3134         glScissor(rect.left(), offscreen->offscreenSize().height() - rect.bottom() - 1, rect.width(), rect.height());
       
  3135     }
       
  3136 
       
  3137     QVector<QGLTrapezoid> trapezoids = generateTrapezoids();
       
  3138 
       
  3139     // clear mask
       
  3140     glBlendFunc(GL_ZERO, GL_ZERO); // clear
       
  3141     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3142     glEnableClientState(GL_VERTEX_ARRAY);
       
  3143     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3144     glDisableClientState(GL_VERTEX_ARRAY);
       
  3145 
       
  3146     glBlendFunc(GL_ONE, GL_ONE); // add mask
       
  3147     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  3148     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
       
  3149 
       
  3150     QPoint delta = rect.topLeft() - screen_rect.topLeft();
       
  3151     glBegin(GL_QUADS);
       
  3152     for (int i = 0; i < trapezoids.size(); ++i)
       
  3153         drawTrapezoid(trapezoids[i].translated(delta), offscreen->offscreenSize().height(), ctx);
       
  3154     glEnd();
       
  3155 
       
  3156     if (needs_scissor)
       
  3157         glDisable(GL_SCISSOR_TEST);
       
  3158 
       
  3159     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  3160 
       
  3161     glMatrixMode(GL_MODELVIEW);
       
  3162     glPopMatrix();
       
  3163 #endif
       
  3164 }
       
  3165 
       
  3166 QRect QGLTrapezoidMaskGenerator::screenRect()
       
  3167 {
       
  3168     if (!has_screen_rect) {
       
  3169         screen_rect = computeScreenRect();
       
  3170         has_screen_rect = true;
       
  3171     }
       
  3172 
       
  3173     screen_rect = screen_rect.intersected(QRect(QPoint(), offscreen->drawableSize()));
       
  3174 
       
  3175     return screen_rect;
       
  3176 }
       
  3177 
       
  3178 QGLPathMaskGenerator::QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
       
  3179     : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
       
  3180 {
       
  3181 }
       
  3182 
       
  3183 QRect QGLPathMaskGenerator::computeScreenRect()
       
  3184 {
       
  3185     poly = path().toFillPolygon(matrix());
       
  3186     return poly.boundingRect().toAlignedRect();
       
  3187 }
       
  3188 
       
  3189 QVector<QGLTrapezoid> QGLPathMaskGenerator::generateTrapezoids()
       
  3190 {
       
  3191     QOpenGLImmediateModeTessellator tessellator;
       
  3192     tessellator.tessellate(poly.data(), poly.count(), path().fillRule() == Qt::WindingFill);
       
  3193     return tessellator.trapezoids;
       
  3194 }
       
  3195 
       
  3196 QGLRectMaskGenerator::QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
       
  3197     : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
       
  3198 {
       
  3199 }
       
  3200 
       
  3201 QGLLineMaskGenerator::QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offs, GLuint program)
       
  3202     : QGLTrapezoidMaskGenerator(path, matrix, offs, program, width)
       
  3203 {
       
  3204 }
       
  3205 
       
  3206 QRect QGLRectMaskGenerator::computeScreenRect()
       
  3207 {
       
  3208     transformedPath = matrix().map(path());
       
  3209 
       
  3210     return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
       
  3211 }
       
  3212 
       
  3213 QRect QGLLineMaskGenerator::computeScreenRect()
       
  3214 {
       
  3215     transformedPath = matrix().map(path());
       
  3216 
       
  3217     return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
       
  3218 }
       
  3219 
       
  3220 QVector<QGLTrapezoid> QGLLineMaskGenerator::generateTrapezoids()
       
  3221 {
       
  3222     QOpenGLImmediateModeTessellator tessellator;
       
  3223     QPointF last;
       
  3224     for (int i = 0; i < transformedPath.elementCount(); ++i) {
       
  3225         QPainterPath::Element element = transformedPath.elementAt(i);
       
  3226 
       
  3227         Q_ASSERT(!element.isCurveTo());
       
  3228 
       
  3229         if (element.isLineTo())
       
  3230             tessellator.tessellateRect(last, element, strokeWidth());
       
  3231 
       
  3232         last = element;
       
  3233     }
       
  3234 
       
  3235     return tessellator.trapezoids;
       
  3236 }
       
  3237 
       
  3238 QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids()
       
  3239 {
       
  3240     Q_ASSERT(transformedPath.elementCount() == 5);
       
  3241 
       
  3242     QOpenGLImmediateModeTessellator tessellator;
       
  3243     if (matrix().type() <= QTransform::TxScale) {
       
  3244         QPointF a = transformedPath.elementAt(0);
       
  3245         QPointF b = transformedPath.elementAt(1);
       
  3246         QPointF c = transformedPath.elementAt(2);
       
  3247         QPointF d = transformedPath.elementAt(3);
       
  3248 
       
  3249         QPointF first = (a + d) * 0.5;
       
  3250         QPointF last = (b + c) * 0.5;
       
  3251 
       
  3252         QPointF delta = a - d;
       
  3253 
       
  3254         // manhattan distance (no rotation)
       
  3255         qreal width = qAbs(delta.x()) + qAbs(delta.y());
       
  3256 
       
  3257         Q_ASSERT(qFuzzyIsNull(delta.x()) || qFuzzyIsNull(delta.y()));
       
  3258 
       
  3259         tessellator.tessellateRect(first, last, width);
       
  3260     } else {
       
  3261         QPointF points[5];
       
  3262 
       
  3263         for (int i = 0; i < 5; ++i)
       
  3264             points[i] = transformedPath.elementAt(i);
       
  3265 
       
  3266         tessellator.tessellateConvex(points, 5);
       
  3267     }
       
  3268     return tessellator.trapezoids;
       
  3269 }
       
  3270 
       
  3271 static QPainterPath ellipseRectToPath(const QRectF &rect)
       
  3272 {
       
  3273     QPainterPath path;
       
  3274     path.addEllipse(rect);
       
  3275     return path;
       
  3276 }
       
  3277 
       
  3278 QGLEllipseMaskGenerator::QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offs, GLuint program, int *locations)
       
  3279     : QGLMaskGenerator(ellipseRectToPath(rect), matrix),
       
  3280       ellipseRect(rect),
       
  3281       offscreen(&offs),
       
  3282       maskFragmentProgram(program),
       
  3283       maskVariableLocations(locations)
       
  3284 {
       
  3285 }
       
  3286 
       
  3287 QRect QGLEllipseMaskGenerator::screenRect()
       
  3288 {
       
  3289     QPointF center = ellipseRect.center();
       
  3290 
       
  3291     QPointF points[] = {
       
  3292         QPointF(ellipseRect.left(), center.y()),
       
  3293         QPointF(ellipseRect.right(), center.y()),
       
  3294         QPointF(center.x(), ellipseRect.top()),
       
  3295         QPointF(center.x(), ellipseRect.bottom())
       
  3296     };
       
  3297 
       
  3298     qreal min_screen_delta_len = QREAL_MAX;
       
  3299 
       
  3300     for (int i = 0; i < 4; ++i) {
       
  3301         QPointF delta = points[i] - center;
       
  3302 
       
  3303         // normalize
       
  3304         delta /= qSqrt(delta.x() * delta.x() + delta.y() * delta.y());
       
  3305 
       
  3306         QPointF screen_delta(matrix().m11() * delta.x() + matrix().m21() * delta.y(),
       
  3307                              matrix().m12() * delta.x() + matrix().m22() * delta.y());
       
  3308 
       
  3309         min_screen_delta_len = qMin(min_screen_delta_len,
       
  3310                                     qreal(qSqrt(screen_delta.x() * screen_delta.x() + screen_delta.y() * screen_delta.y())));
       
  3311     }
       
  3312 
       
  3313     const qreal padding = 2.0f;
       
  3314 
       
  3315     qreal grow = padding / min_screen_delta_len;
       
  3316 
       
  3317     QRectF boundingRect = ellipseRect.adjusted(-grow, -grow, grow, grow);
       
  3318 
       
  3319     boundingRect = matrix().mapRect(boundingRect);
       
  3320 
       
  3321     QPointF p(0.5, 0.5);
       
  3322 
       
  3323     screen_rect = QRect((boundingRect.topLeft() - p).toPoint(),
       
  3324                         (boundingRect.bottomRight() + p).toPoint());
       
  3325 
       
  3326     return screen_rect;
       
  3327 }
       
  3328 
       
  3329 void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
       
  3330 {
       
  3331 #ifdef QT_OPENGL_ES
       
  3332     Q_UNUSED(rect);
       
  3333 #else
       
  3334     QGLContext *ctx = offscreen->context();
       
  3335     offscreen->bind();
       
  3336 
       
  3337     glDisable(GL_TEXTURE_GEN_S);
       
  3338     glDisable(GL_TEXTURE_1D);
       
  3339 
       
  3340     // fragment program needs the inverse radii of the ellipse
       
  3341     glTexCoord2f(1.0f / (ellipseRect.width() * 0.5f),
       
  3342                  1.0f / (ellipseRect.height() * 0.5f));
       
  3343 
       
  3344     QTransform translate(1, 0, 0, 1, -ellipseRect.center().x(), -ellipseRect.center().y());
       
  3345     QTransform gl_to_qt(1, 0, 0, -1, 0, offscreen->drawableSize().height());
       
  3346     QTransform inv_matrix = gl_to_qt * matrix().inverted() * translate;
       
  3347 
       
  3348     float m[3][4] = { { inv_matrix.m11(), inv_matrix.m12(), inv_matrix.m13() },
       
  3349                       { inv_matrix.m21(), inv_matrix.m22(), inv_matrix.m23() },
       
  3350                       { inv_matrix.m31(), inv_matrix.m32(), inv_matrix.m33() } };
       
  3351 
       
  3352     QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
       
  3353                                                 - (offscreen->offscreenSize().height() - rect.top()));
       
  3354 
       
  3355     // last component needs to be 1.0f to avoid Nvidia bug on linux
       
  3356     float ellipse_offset[4] = { offs.x(), offs.y(), 0.0f, 1.0f };
       
  3357 
       
  3358     GLfloat vertexArray[4 * 2];
       
  3359     qt_add_rect_to_array(rect, vertexArray);
       
  3360 
       
  3361     glBlendFunc(GL_ONE, GL_ZERO); // set mask
       
  3362     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  3363     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
       
  3364 
       
  3365     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M0], m[0]);
       
  3366     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M1], m[1]);
       
  3367     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M2], m[2]);
       
  3368 
       
  3369     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
       
  3370 
       
  3371     glEnableClientState(GL_VERTEX_ARRAY);
       
  3372     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3373     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3374     glDisableClientState(GL_VERTEX_ARRAY);
       
  3375     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  3376 #endif
       
  3377 }
       
  3378 
       
  3379 void QOpenGLPaintEnginePrivate::drawOffscreenPath(const QPainterPath &path)
       
  3380 {
       
  3381 #ifdef Q_WS_QWS
       
  3382     Q_UNUSED(path);
       
  3383 #else
       
  3384     DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::drawOffscreenPath()");
       
  3385 
       
  3386     disableClipping();
       
  3387 
       
  3388     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  3389                                                        FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3390     QGLPathMaskGenerator maskGenerator(path, matrix, offscreen, program);
       
  3391     addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
       
  3392 
       
  3393     enableClipping();
       
  3394 #endif
       
  3395 }
       
  3396 
       
  3397 void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
       
  3398 {
       
  3399     Q_Q(QOpenGLPaintEngine);
       
  3400     DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
       
  3401 
       
  3402     q_vertexType vertexArray[10];
       
  3403     qt_add_rect_to_array(r, vertexArray);
       
  3404 
       
  3405     if (has_pen)
       
  3406         QOpenGLCoordinateOffset::enableOffset(this);
       
  3407 
       
  3408     if (has_brush) {
       
  3409         flushDrawQueue();
       
  3410 
       
  3411         bool temp = high_quality_antialiasing;
       
  3412         high_quality_antialiasing = false;
       
  3413 
       
  3414         q->updateCompositionMode(composition_mode);
       
  3415 
       
  3416         setGradientOps(cbrush, r);
       
  3417 
       
  3418         bool fast_style = current_style == Qt::LinearGradientPattern
       
  3419                           || current_style == Qt::SolidPattern;
       
  3420 
       
  3421         if (fast_style && has_fast_composition_mode) {
       
  3422             glEnableClientState(GL_VERTEX_ARRAY);
       
  3423             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3424             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3425             glDisableClientState(GL_VERTEX_ARRAY);
       
  3426         } else {
       
  3427             composite(r);
       
  3428         }
       
  3429 
       
  3430         high_quality_antialiasing = temp;
       
  3431 
       
  3432         q->updateCompositionMode(composition_mode);
       
  3433     }
       
  3434 
       
  3435     if (has_pen) {
       
  3436         if (has_fast_pen && !high_quality_antialiasing) {
       
  3437             setGradientOps(cpen.brush(), r);
       
  3438 
       
  3439             vertexArray[8] = vertexArray[0];
       
  3440             vertexArray[9] = vertexArray[1];
       
  3441 
       
  3442             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3443             glEnableClientState(GL_VERTEX_ARRAY);
       
  3444             glDrawArrays(GL_LINE_STRIP, 0, 5);
       
  3445             glDisableClientState(GL_VERTEX_ARRAY);
       
  3446         } else {
       
  3447             QPainterPath path;
       
  3448             path.setFillRule(Qt::WindingFill);
       
  3449 
       
  3450             qreal left = r.left();
       
  3451             qreal right = r.right();
       
  3452             qreal top = r.top();
       
  3453             qreal bottom = r.bottom();
       
  3454 
       
  3455             path.moveTo(left, top);
       
  3456             path.lineTo(right, top);
       
  3457             path.lineTo(right, bottom);
       
  3458             path.lineTo(left, bottom);
       
  3459             path.lineTo(left, top);
       
  3460 
       
  3461             strokePath(path, false);
       
  3462         }
       
  3463 
       
  3464         QOpenGLCoordinateOffset::disableOffset(this);
       
  3465     }
       
  3466 }
       
  3467 
       
  3468 bool QOpenGLPaintEnginePrivate::isFastRect(const QRectF &rect)
       
  3469 {
       
  3470     if (matrix.type() < QTransform::TxRotate) {
       
  3471         QRectF r = matrix.mapRect(rect);
       
  3472         return r.topLeft().toPoint() == r.topLeft()
       
  3473             && r.bottomRight().toPoint() == r.bottomRight();
       
  3474     }
       
  3475 
       
  3476     return false;
       
  3477 }
       
  3478 
       
  3479 void QOpenGLPaintEngine::drawRects(const QRect *rects, int rectCount)
       
  3480 {
       
  3481     struct RectF {
       
  3482         qreal x;
       
  3483         qreal y;
       
  3484         qreal w;
       
  3485         qreal h;
       
  3486     };
       
  3487     Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
       
  3488     RectF fr[256];
       
  3489     while (rectCount) {
       
  3490         int i = 0;
       
  3491         while (i < rectCount && i < 256) {
       
  3492             fr[i].x = rects[i].x();
       
  3493             fr[i].y = rects[i].y();
       
  3494             fr[i].w = rects[i].width();
       
  3495             fr[i].h = rects[i].height();
       
  3496             ++i;
       
  3497         }
       
  3498         drawRects((QRectF *)(void *)fr, i);
       
  3499         rects += i;
       
  3500         rectCount -= i;
       
  3501     }
       
  3502 }
       
  3503 
       
  3504 void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
       
  3505 {
       
  3506     Q_D(QOpenGLPaintEngine);
       
  3507 
       
  3508     if (d->use_emulation) {
       
  3509         QPaintEngineEx::drawRects(rects, rectCount);
       
  3510         return;
       
  3511     }
       
  3512 
       
  3513     for (int i=0; i<rectCount; ++i) {
       
  3514         const QRectF &r = rects[i];
       
  3515 
       
  3516         // optimization for rects which can be drawn aliased
       
  3517         if (!d->high_quality_antialiasing || d->isFastRect(r)) {
       
  3518             d->drawFastRect(r);
       
  3519         } else {
       
  3520             QPainterPath path;
       
  3521             path.addRect(r);
       
  3522 
       
  3523             if (d->has_brush) {
       
  3524                 d->disableClipping();
       
  3525                 GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
       
  3526                                                                    FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3527 
       
  3528                 if (d->matrix.type() >= QTransform::TxProject) {
       
  3529                     QGLPathMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
       
  3530                     d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  3531                 } else {
       
  3532                     QGLRectMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
       
  3533                     d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  3534                 }
       
  3535 
       
  3536                 d->enableClipping();
       
  3537             }
       
  3538 
       
  3539             if (d->has_pen) {
       
  3540                 if (d->has_fast_pen)
       
  3541                     d->strokeLines(path);
       
  3542                 else
       
  3543                     d->strokePath(path, false);
       
  3544             }
       
  3545         }
       
  3546     }
       
  3547 }
       
  3548 
       
  3549 static void addQuadAsTriangle(q_vertexType *quad, q_vertexType *triangle)
       
  3550 {
       
  3551     triangle[0] = quad[0];
       
  3552     triangle[1] = quad[1];
       
  3553 
       
  3554     triangle[2] = quad[2];
       
  3555     triangle[3] = quad[3];
       
  3556 
       
  3557     triangle[4] = quad[4];
       
  3558     triangle[5] = quad[5];
       
  3559 
       
  3560     triangle[6] = quad[4];
       
  3561     triangle[7] = quad[5];
       
  3562 
       
  3563     triangle[8] = quad[6];
       
  3564     triangle[9] = quad[7];
       
  3565 
       
  3566     triangle[10] = quad[0];
       
  3567     triangle[11] = quad[1];
       
  3568 }
       
  3569 
       
  3570 void QOpenGLPaintEngine::drawPoints(const QPoint *points, int pointCount)
       
  3571 {
       
  3572     Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
       
  3573     QT_PointF fp[256];
       
  3574     while (pointCount) {
       
  3575         int i = 0;
       
  3576         while (i < pointCount && i < 256) {
       
  3577             fp[i].x = points[i].x();
       
  3578             fp[i].y = points[i].y();
       
  3579             ++i;
       
  3580         }
       
  3581         drawPoints((QPointF *)(void *)fp, i);
       
  3582         points += i;
       
  3583         pointCount -= i;
       
  3584     }
       
  3585 }
       
  3586 
       
  3587 void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
       
  3588 {
       
  3589     Q_D(QOpenGLPaintEngine);
       
  3590 
       
  3591     if (d->use_emulation) {
       
  3592         QPaintEngineEx::drawPoints(points, pointCount);
       
  3593         return;
       
  3594     }
       
  3595 
       
  3596     d->setGradientOps(d->cpen.brush(), QRectF());
       
  3597 
       
  3598     if (!d->cpen.isCosmetic() || d->high_quality_antialiasing) {
       
  3599         Qt::PenCapStyle capStyle = d->cpen.capStyle();
       
  3600         if (capStyle == Qt::FlatCap)
       
  3601             d->cpen.setCapStyle(Qt::SquareCap);
       
  3602         QPaintEngine::drawPoints(points, pointCount);
       
  3603         d->cpen.setCapStyle(capStyle);
       
  3604         return;
       
  3605     }
       
  3606 
       
  3607     d->flushDrawQueue();
       
  3608 
       
  3609     if (d->has_fast_pen) {
       
  3610         QVarLengthArray<q_vertexType> vertexArray(6 * pointCount);
       
  3611 
       
  3612         glMatrixMode(GL_MODELVIEW);
       
  3613         glPushMatrix();
       
  3614         glLoadIdentity();
       
  3615 
       
  3616         int j = 0;
       
  3617         for (int i = 0; i < pointCount; ++i) {
       
  3618             QPointF mapped = d->matrix.map(points[i]);
       
  3619 
       
  3620             qreal xf = qRound(mapped.x());
       
  3621             qreal yf = qRound(mapped.y());
       
  3622 
       
  3623             q_vertexType x = f2vt(xf);
       
  3624             q_vertexType y = f2vt(yf);
       
  3625 
       
  3626             vertexArray[j++] = x;
       
  3627             vertexArray[j++] = y - f2vt(0.5);
       
  3628 
       
  3629             vertexArray[j++] = x + f2vt(1.5);
       
  3630             vertexArray[j++] = y + f2vt(1.0);
       
  3631 
       
  3632             vertexArray[j++] = x;
       
  3633             vertexArray[j++] = y + f2vt(1.0);
       
  3634         }
       
  3635 
       
  3636         glEnableClientState(GL_VERTEX_ARRAY);
       
  3637 
       
  3638         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3639         glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
       
  3640 
       
  3641         glDisableClientState(GL_VERTEX_ARRAY);
       
  3642 
       
  3643         glPopMatrix();
       
  3644         return;
       
  3645     }
       
  3646 
       
  3647     const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
       
  3648 
       
  3649     if (sizeof(qreal) == sizeof(double)) {
       
  3650         Q_ASSERT(sizeof(QPointF) == 16);
       
  3651         glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
       
  3652     }
       
  3653     else {
       
  3654         Q_ASSERT(sizeof(QPointF) == 8);
       
  3655         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3656     }
       
  3657 
       
  3658     glEnableClientState(GL_VERTEX_ARRAY);
       
  3659     glDrawArrays(GL_POINTS, 0, pointCount);
       
  3660     glDisableClientState(GL_VERTEX_ARRAY);
       
  3661 }
       
  3662 
       
  3663 void QOpenGLPaintEngine::drawLines(const QLine *lines, int lineCount)
       
  3664 {
       
  3665     struct PointF {
       
  3666         qreal x;
       
  3667         qreal y;
       
  3668     };
       
  3669     struct LineF {
       
  3670         PointF p1;
       
  3671         PointF p2;
       
  3672     };
       
  3673     Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
       
  3674     Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
       
  3675     LineF fl[256];
       
  3676     while (lineCount) {
       
  3677         int i = 0;
       
  3678         while (i < lineCount && i < 256) {
       
  3679             fl[i].p1.x = lines[i].x1();
       
  3680             fl[i].p1.y = lines[i].y1();
       
  3681             fl[i].p2.x = lines[i].x2();
       
  3682             fl[i].p2.y = lines[i].y2();
       
  3683             ++i;
       
  3684         }
       
  3685         drawLines((QLineF *)(void *)fl, i);
       
  3686         lines += i;
       
  3687         lineCount -= i;
       
  3688     }
       
  3689 }
       
  3690 
       
  3691 void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
       
  3692 {
       
  3693     Q_D(QOpenGLPaintEngine);
       
  3694 
       
  3695     if (d->use_emulation) {
       
  3696         QPaintEngineEx::drawLines(lines, lineCount);
       
  3697         return;
       
  3698     }
       
  3699 
       
  3700     if (d->has_pen) {
       
  3701         QOpenGLCoordinateOffset offset(d);
       
  3702         if (d->has_fast_pen && !d->high_quality_antialiasing) {
       
  3703             //### gradient resolving on lines isn't correct
       
  3704             d->setGradientOps(d->cpen.brush(), QRectF());
       
  3705 
       
  3706             bool useRects = false;
       
  3707             // scale or 90 degree rotation?
       
  3708             if (d->matrix.type() <= QTransform::TxTranslate
       
  3709                 || (!d->cpen.isCosmetic()
       
  3710                     && (d->matrix.type() <= QTransform::TxScale
       
  3711                         || (d->matrix.type() == QTransform::TxRotate
       
  3712                             && d->matrix.m11() == 0 && d->matrix.m22() == 0)))) {
       
  3713                 useRects = true;
       
  3714                 for (int i = 0; i < lineCount; ++i) {
       
  3715                     if (lines[i].p1().x() != lines[i].p2().x()
       
  3716                         && lines[i].p1().y() != lines[i].p2().y()) {
       
  3717                         useRects = false;
       
  3718                         break;
       
  3719                     }
       
  3720                 }
       
  3721             }
       
  3722 
       
  3723             q_vertexType endCap = f2vt(d->cpen.capStyle() == Qt::FlatCap ? 0 : 0.5);
       
  3724             if (useRects) {
       
  3725                 QVarLengthArray<q_vertexType> vertexArray(12 * lineCount);
       
  3726 
       
  3727                 q_vertexType quad[8];
       
  3728                 for (int i = 0; i < lineCount; ++i) {
       
  3729                     q_vertexType x1 = f2vt(lines[i].x1());
       
  3730                     q_vertexType x2 = f2vt(lines[i].x2());
       
  3731                     q_vertexType y1 = f2vt(lines[i].y1());
       
  3732                     q_vertexType y2 = f2vt(lines[i].y2());
       
  3733 
       
  3734                     if (x1 == x2) {
       
  3735                         if (y1 > y2)
       
  3736                             qSwap(y1, y2);
       
  3737 
       
  3738                         quad[0] = x1 - f2vt(0.5);
       
  3739                         quad[1] = y1 - endCap;
       
  3740 
       
  3741                         quad[2] = x1 + f2vt(0.5);
       
  3742                         quad[3] = y1 - endCap;
       
  3743 
       
  3744                         quad[4] = x1 + f2vt(0.5);
       
  3745                         quad[5] = y2 + endCap;
       
  3746 
       
  3747                         quad[6] = x1 - f2vt(0.5);
       
  3748                         quad[7] = y2 + endCap;
       
  3749                     } else {
       
  3750                         if (x1 > x2)
       
  3751                             qSwap(x1, x2);
       
  3752 
       
  3753                         quad[0] = x1 - endCap;
       
  3754                         quad[1] = y1 + f2vt(0.5);
       
  3755 
       
  3756                         quad[2] = x1 - endCap;
       
  3757                         quad[3] = y1 - f2vt(0.5);
       
  3758 
       
  3759                         quad[4] = x2 + endCap;
       
  3760                         quad[5] = y1 - f2vt(0.5);
       
  3761 
       
  3762                         quad[6] = x2 + endCap;
       
  3763                         quad[7] = y1 + f2vt(0.5);
       
  3764                     }
       
  3765 
       
  3766                     addQuadAsTriangle(quad, &vertexArray[12*i]);
       
  3767                 }
       
  3768 
       
  3769                 glEnableClientState(GL_VERTEX_ARRAY);
       
  3770 
       
  3771                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3772                 glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
       
  3773 
       
  3774                 glDisableClientState(GL_VERTEX_ARRAY);
       
  3775             } else {
       
  3776                 QVarLengthArray<q_vertexType> vertexArray(4 * lineCount);
       
  3777                 for (int i = 0; i < lineCount; ++i) {
       
  3778                     const QPointF a = lines[i].p1();
       
  3779                     vertexArray[4*i]   = f2vt(lines[i].x1());
       
  3780                     vertexArray[4*i+1] = f2vt(lines[i].y1());
       
  3781                     vertexArray[4*i+2] = f2vt(lines[i].x2());
       
  3782                     vertexArray[4*i+3] = f2vt(lines[i].y2());
       
  3783                 }
       
  3784 
       
  3785                 glEnableClientState(GL_VERTEX_ARRAY);
       
  3786 
       
  3787                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3788                 glDrawArrays(GL_LINES, 0, lineCount*2);
       
  3789 
       
  3790                 glVertexPointer(2, q_vertexTypeEnum, 4*sizeof(q_vertexType), vertexArray.constData() + 2);
       
  3791                 glDrawArrays(GL_POINTS, 0, lineCount);
       
  3792 
       
  3793                 glDisableClientState(GL_VERTEX_ARRAY);
       
  3794             }
       
  3795         } else {
       
  3796             QPainterPath path;
       
  3797             path.setFillRule(Qt::WindingFill);
       
  3798             for (int i=0; i<lineCount; ++i) {
       
  3799                 const QLineF &l = lines[i];
       
  3800 
       
  3801                 if (l.p1() == l.p2()) {
       
  3802                     if (d->cpen.capStyle() != Qt::FlatCap) {
       
  3803                         QPointF p = l.p1();
       
  3804                         drawPoints(&p, 1);
       
  3805                     }
       
  3806                     continue;
       
  3807                 }
       
  3808 
       
  3809                 path.moveTo(l.x1(), l.y1());
       
  3810                 path.lineTo(l.x2(), l.y2());
       
  3811             }
       
  3812 
       
  3813             if (d->has_fast_pen && d->high_quality_antialiasing)
       
  3814                 d->strokeLines(path);
       
  3815             else
       
  3816                 d->strokePath(path, false);
       
  3817         }
       
  3818     }
       
  3819 }
       
  3820 
       
  3821 void QOpenGLPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
       
  3822 {
       
  3823     Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
       
  3824     QVarLengthArray<QT_PointF> p(pointCount);
       
  3825     for (int i=0; i<pointCount; ++i) {
       
  3826         p[i].x = points[i].x();
       
  3827         p[i].y = points[i].y();
       
  3828     }
       
  3829     drawPolygon((QPointF *)p.data(), pointCount, mode);
       
  3830 }
       
  3831 
       
  3832 void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
       
  3833 {
       
  3834     Q_D(QOpenGLPaintEngine);
       
  3835     if(pointCount < 2)
       
  3836         return;
       
  3837 
       
  3838     if (d->use_emulation) {
       
  3839         QPaintEngineEx::drawPolygon(points, pointCount, mode);
       
  3840         return;
       
  3841     }
       
  3842 
       
  3843     QRectF bounds;
       
  3844     if ((mode == ConvexMode && !d->high_quality_antialiasing && state()->brushNeedsResolving()) ||
       
  3845         ((d->has_fast_pen && !d->high_quality_antialiasing) && state()->penNeedsResolving())) {
       
  3846         qreal minx = points[0].x(), miny = points[0].y(),
       
  3847               maxx = points[0].x(), maxy = points[0].y();
       
  3848         for (int i = 1; i < pointCount; ++i) {
       
  3849             const QPointF &pt = points[i];
       
  3850             if (minx > pt.x())
       
  3851                 minx = pt.x();
       
  3852             if (miny > pt.y())
       
  3853                 miny = pt.y();
       
  3854             if (maxx < pt.x())
       
  3855                 maxx = pt.x();
       
  3856             if (maxy < pt.y())
       
  3857                 maxy = pt.y();
       
  3858         }
       
  3859         bounds = QRectF(minx, maxx, maxx-minx, maxy-miny);
       
  3860     }
       
  3861 
       
  3862     QOpenGLCoordinateOffset offset(d);
       
  3863 
       
  3864     if (d->has_brush && mode != PolylineMode) {
       
  3865         if (mode == ConvexMode && !d->high_quality_antialiasing) {
       
  3866             //### resolving on polygon from points isn't correct
       
  3867             d->setGradientOps(d->cbrush, bounds);
       
  3868 
       
  3869             const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
       
  3870 
       
  3871             if (sizeof(qreal) == sizeof(double)) {
       
  3872                 Q_ASSERT(sizeof(QPointF) == 16);
       
  3873                 glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
       
  3874             }
       
  3875             else {
       
  3876                 Q_ASSERT(sizeof(QPointF) == 8);
       
  3877                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3878             }
       
  3879 
       
  3880             glEnableClientState(GL_VERTEX_ARRAY);
       
  3881             glDrawArrays(GL_TRIANGLE_FAN, 0, pointCount);
       
  3882             glDisableClientState(GL_VERTEX_ARRAY);
       
  3883         } else {
       
  3884             QPainterPath path;
       
  3885             path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
       
  3886             path.moveTo(points[0]);
       
  3887             for (int i=1; i<pointCount; ++i)
       
  3888                 path.lineTo(points[i]);
       
  3889             d->fillPath(path);
       
  3890         }
       
  3891     }
       
  3892 
       
  3893     if (d->has_pen) {
       
  3894         if (d->has_fast_pen && !d->high_quality_antialiasing) {
       
  3895             d->setGradientOps(d->cpen.brush(), bounds);
       
  3896             QVarLengthArray<q_vertexType> vertexArray(pointCount*2 + 2);
       
  3897             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3898             int i;
       
  3899             for (i=0; i<pointCount; ++i) {
       
  3900                 vertexArray[i*2] = f2vt(points[i].x());
       
  3901                 vertexArray[i*2+1] = f2vt(points[i].y());
       
  3902             }
       
  3903 
       
  3904             glEnableClientState(GL_VERTEX_ARRAY);
       
  3905             if (mode != PolylineMode) {
       
  3906                 vertexArray[i*2] = vertexArray[0];
       
  3907                 vertexArray[i*2+1] = vertexArray[1];
       
  3908                 glDrawArrays(GL_LINE_STRIP, 0, pointCount+1);
       
  3909             } else {
       
  3910                 glDrawArrays(GL_LINE_STRIP, 0, pointCount);
       
  3911                 glDrawArrays(GL_POINTS, pointCount-1, 1);
       
  3912             }
       
  3913             glDisableClientState(GL_VERTEX_ARRAY);
       
  3914         } else {
       
  3915             QPainterPath path(points[0]);
       
  3916             for (int i = 1; i < pointCount; ++i)
       
  3917                 path.lineTo(points[i]);
       
  3918             if (mode != PolylineMode)
       
  3919                 path.lineTo(points[0]);
       
  3920 
       
  3921             if (d->has_fast_pen)
       
  3922                 d->strokeLines(path);
       
  3923             else
       
  3924                 d->strokePath(path, true);
       
  3925         }
       
  3926     }
       
  3927 }
       
  3928 
       
  3929 void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
       
  3930 {
       
  3931     DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::strokeLines()");
       
  3932 
       
  3933     qreal penWidth = cpen.widthF();
       
  3934 
       
  3935     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  3936                                                        FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3937     QGLLineMaskGenerator maskGenerator(path, matrix, penWidth == 0 ? 1.0 : penWidth,
       
  3938                                        offscreen, program);
       
  3939 
       
  3940     disableClipping();
       
  3941 
       
  3942     QBrush temp = cbrush;
       
  3943     QPointF origin = brush_origin;
       
  3944 
       
  3945     cbrush = cpen.brush();
       
  3946     brush_origin = QPointF();
       
  3947 
       
  3948     addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
       
  3949 
       
  3950     cbrush = temp;
       
  3951     brush_origin = origin;
       
  3952 
       
  3953     enableClipping();
       
  3954 }
       
  3955 
       
  3956 extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
       
  3957 
       
  3958 void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
       
  3959 {
       
  3960     QBrush old_brush = cbrush;
       
  3961     cbrush = cpen.brush();
       
  3962 
       
  3963     qreal txscale = 1;
       
  3964     if (cpen.isCosmetic() || (qt_scaleForTransform(matrix, &txscale) && txscale != 1)) {
       
  3965         QTransform temp = matrix;
       
  3966         matrix = QTransform();
       
  3967         glPushMatrix();
       
  3968 
       
  3969         if (has_antialiasing) {
       
  3970             glLoadIdentity();
       
  3971         } else {
       
  3972             float offs_matrix[] =
       
  3973               { 1, 0, 0, 0,
       
  3974                 0, 1, 0, 0,
       
  3975                 0, 0, 1, 0,
       
  3976                 0.5, 0.5, 0, 1 };
       
  3977             glLoadMatrixf(offs_matrix);
       
  3978         }
       
  3979 
       
  3980         QPen pen = cpen;
       
  3981         if (txscale != 1)
       
  3982             pen.setWidthF(pen.widthF() * txscale);
       
  3983         if (use_cache)
       
  3984             fillPath(qt_opengl_stroke_cache()->getStrokedPath(temp.map(path), pen));
       
  3985         else
       
  3986             fillPath(strokeForPath(temp.map(path), pen));
       
  3987 
       
  3988         glPopMatrix();
       
  3989         matrix = temp;
       
  3990     } else if (use_cache) {
       
  3991         fillPath(qt_opengl_stroke_cache()->getStrokedPath(path, cpen));
       
  3992     } else {
       
  3993         fillPath(strokeForPath(path, cpen));
       
  3994     }
       
  3995 
       
  3996     cbrush = old_brush;
       
  3997 }
       
  3998 
       
  3999 void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool needsResolving)
       
  4000 {
       
  4001 #ifndef QT_OPENGL_ES
       
  4002     QRectF bounds;
       
  4003     if (needsResolving)
       
  4004         bounds = path.controlPointRect();
       
  4005     setGradientOps(cpen.brush(), bounds);
       
  4006 
       
  4007     QBezier beziers[32];
       
  4008     for (int i=0; i<path.elementCount(); ++i) {
       
  4009         const QPainterPath::Element &e = path.elementAt(i);
       
  4010         switch (e.type) {
       
  4011         case QPainterPath::MoveToElement:
       
  4012             if (i != 0)
       
  4013                 glEnd(); // GL_LINE_STRIP
       
  4014             glBegin(GL_LINE_STRIP);
       
  4015             glVertex2d(e.x, e.y);
       
  4016 
       
  4017             break;
       
  4018         case QPainterPath::LineToElement:
       
  4019             glVertex2d(e.x, e.y);
       
  4020             break;
       
  4021 
       
  4022         case QPainterPath::CurveToElement:
       
  4023         {
       
  4024             QPointF sp = path.elementAt(i-1);
       
  4025             QPointF cp2 = path.elementAt(i+1);
       
  4026             QPointF ep = path.elementAt(i+2);
       
  4027             i+=2;
       
  4028 
       
  4029             qreal inverseScaleHalf = inverseScale / 2;
       
  4030             beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
       
  4031             QBezier *b = beziers;
       
  4032             while (b >= beziers) {
       
  4033                 // check if we can pop the top bezier curve from the stack
       
  4034                 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  4035                 qreal d;
       
  4036                 if (l > inverseScale) {
       
  4037                     d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
       
  4038                               - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  4039                         + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
       
  4040                                 - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  4041                     d /= l;
       
  4042                 } else {
       
  4043                     d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  4044                         qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  4045                 }
       
  4046                 if (d < inverseScaleHalf || b == beziers + 31) {
       
  4047                     // good enough, we pop it off and add the endpoint
       
  4048                     glVertex2d(b->x4, b->y4);
       
  4049                     --b;
       
  4050                 } else {
       
  4051                     // split, second half of the polygon goes lower into the stack
       
  4052                     b->split(b+1, b);
       
  4053                     ++b;
       
  4054                 }
       
  4055             }
       
  4056         } // case CurveToElement
       
  4057         default:
       
  4058             break;
       
  4059         } // end of switch
       
  4060     }
       
  4061     glEnd(); // GL_LINE_STRIP
       
  4062 #else
       
  4063     // have to use vertex arrays on embedded
       
  4064     QRectF bounds;
       
  4065     if (needsResolving)
       
  4066         bounds = path.controlPointRect();
       
  4067     setGradientOps(cpen.brush(), bounds);
       
  4068 
       
  4069     glEnableClientState(GL_VERTEX_ARRAY);
       
  4070     tess_points.reset();
       
  4071     QBezier beziers[32];
       
  4072     for (int i=0; i<path.elementCount(); ++i) {
       
  4073         const QPainterPath::Element &e = path.elementAt(i);
       
  4074         switch (e.type) {
       
  4075         case QPainterPath::MoveToElement:
       
  4076             if (i != 0) {
       
  4077                 glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
       
  4078                 glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
       
  4079                 tess_points.reset();
       
  4080             }
       
  4081             tess_points.add(QPointF(e.x, e.y));
       
  4082 
       
  4083             break;
       
  4084         case QPainterPath::LineToElement:
       
  4085             tess_points.add(QPointF(e.x, e.y));
       
  4086             break;
       
  4087 
       
  4088         case QPainterPath::CurveToElement:
       
  4089         {
       
  4090             QPointF sp = path.elementAt(i-1);
       
  4091             QPointF cp2 = path.elementAt(i+1);
       
  4092             QPointF ep = path.elementAt(i+2);
       
  4093             i+=2;
       
  4094 
       
  4095             qreal inverseScaleHalf = inverseScale / 2;
       
  4096             beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
       
  4097             QBezier *b = beziers;
       
  4098             while (b >= beziers) {
       
  4099                 // check if we can pop the top bezier curve from the stack
       
  4100                 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  4101                 qreal d;
       
  4102                 if (l > inverseScale) {
       
  4103                     d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
       
  4104                               - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  4105                         + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
       
  4106                                 - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  4107                     d /= l;
       
  4108                 } else {
       
  4109                     d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  4110                         qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  4111                 }
       
  4112                 if (d < inverseScaleHalf || b == beziers + 31) {
       
  4113                     // good enough, we pop it off and add the endpoint
       
  4114                     tess_points.add(QPointF(b->x4, b->y4));
       
  4115                     --b;
       
  4116                 } else {
       
  4117                     // split, second half of the polygon goes lower into the stack
       
  4118                     b->split(b+1, b);
       
  4119                     ++b;
       
  4120                 }
       
  4121             }
       
  4122         } // case CurveToElement
       
  4123         default:
       
  4124             break;
       
  4125         } // end of switch
       
  4126     }
       
  4127     glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
       
  4128     glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
       
  4129     glDisableClientState(GL_VERTEX_ARRAY);
       
  4130 #endif
       
  4131 }
       
  4132 
       
  4133 static bool pathClosed(const QPainterPath &path)
       
  4134 {
       
  4135     QPointF lastMoveTo = path.elementAt(0);
       
  4136     QPointF lastPoint = lastMoveTo;
       
  4137 
       
  4138     for (int i = 1; i < path.elementCount(); ++i) {
       
  4139         const QPainterPath::Element &e = path.elementAt(i);
       
  4140         switch (e.type) {
       
  4141         case QPainterPath::MoveToElement:
       
  4142             if (lastMoveTo != lastPoint)
       
  4143                 return false;
       
  4144             lastMoveTo = lastPoint = e;
       
  4145             break;
       
  4146         case QPainterPath::LineToElement:
       
  4147             lastPoint = e;
       
  4148             break;
       
  4149         case QPainterPath::CurveToElement:
       
  4150             lastPoint = path.elementAt(i + 2);
       
  4151             i+=2;
       
  4152             break;
       
  4153         default:
       
  4154             break;
       
  4155         }
       
  4156     }
       
  4157 
       
  4158     return lastMoveTo == lastPoint;
       
  4159 }
       
  4160 
       
  4161 void QOpenGLPaintEngine::drawPath(const QPainterPath &path)
       
  4162 {
       
  4163     Q_D(QOpenGLPaintEngine);
       
  4164 
       
  4165     if (path.isEmpty())
       
  4166         return;
       
  4167 
       
  4168     if (d->use_emulation) {
       
  4169         QPaintEngineEx::drawPath(path);
       
  4170         return;
       
  4171     }
       
  4172 
       
  4173     QOpenGLCoordinateOffset offset(d);
       
  4174 
       
  4175     if (d->has_brush) {
       
  4176         bool path_closed = pathClosed(path);
       
  4177 
       
  4178         bool has_thick_pen =
       
  4179             path_closed
       
  4180             && d->has_pen
       
  4181             && d->cpen.style() == Qt::SolidLine
       
  4182             && d->cpen.isSolid()
       
  4183             && d->cpen.color().alpha() == 255
       
  4184             && d->txop < QTransform::TxProject
       
  4185             && d->cpen.widthF() >= 2 / qSqrt(qMin(d->matrix.m11() * d->matrix.m11()
       
  4186                                                   + d->matrix.m21() * d->matrix.m21(),
       
  4187                                                   d->matrix.m12() * d->matrix.m12()
       
  4188                                                   + d->matrix.m22() * d->matrix.m22()));
       
  4189 
       
  4190         if (has_thick_pen) {
       
  4191             DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::drawPath(): Using thick pen optimization, style:" << d->cbrush.style();
       
  4192 
       
  4193             d->flushDrawQueue();
       
  4194 
       
  4195             bool temp = d->high_quality_antialiasing;
       
  4196             d->high_quality_antialiasing = false;
       
  4197 
       
  4198             updateCompositionMode(d->composition_mode);
       
  4199 
       
  4200             d->fillPath(path);
       
  4201 
       
  4202             d->high_quality_antialiasing = temp;
       
  4203             updateCompositionMode(d->composition_mode);
       
  4204         } else {
       
  4205             d->fillPath(path);
       
  4206         }
       
  4207     }
       
  4208 
       
  4209     if (d->has_pen) {
       
  4210         if (d->has_fast_pen && !d->high_quality_antialiasing)
       
  4211             d->strokePathFastPen(path, state()->penNeedsResolving());
       
  4212         else
       
  4213             d->strokePath(path, true);
       
  4214     }
       
  4215 }
       
  4216 
       
  4217 void QOpenGLPaintEnginePrivate::drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr)
       
  4218 {
       
  4219     QBrush old_brush = cbrush;
       
  4220     QPointF old_brush_origin = brush_origin;
       
  4221 
       
  4222     qreal scaleX = r.width() / sr.width();
       
  4223     qreal scaleY = r.height() / sr.height();
       
  4224 
       
  4225     QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
       
  4226     brush_matrix.scale(scaleX, scaleY);
       
  4227     brush_matrix.translate(-sr.left(), -sr.top());
       
  4228 
       
  4229     cbrush = QBrush(img);
       
  4230     cbrush.setTransform(brush_matrix);
       
  4231     brush_origin = QPointF();
       
  4232 
       
  4233     QPainterPath p;
       
  4234     p.addRect(r);
       
  4235     fillPath(p);
       
  4236 
       
  4237     cbrush = old_brush;
       
  4238     brush_origin = old_brush_origin;
       
  4239 }
       
  4240 
       
  4241 void QOpenGLPaintEnginePrivate::drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy,
       
  4242                                                      const QPointF &offset)
       
  4243 {
       
  4244     QBrush old_brush = cbrush;
       
  4245     QPointF old_brush_origin = brush_origin;
       
  4246 
       
  4247     QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
       
  4248     brush_matrix.scale(sx, sy);
       
  4249     brush_matrix.translate(-offset.x(), -offset.y());
       
  4250 
       
  4251     cbrush = QBrush(img);
       
  4252     cbrush.setTransform(brush_matrix);
       
  4253     brush_origin = QPointF();
       
  4254 
       
  4255     QPainterPath p;
       
  4256     p.addRect(r);
       
  4257     fillPath(p);
       
  4258 
       
  4259     cbrush = old_brush;
       
  4260     brush_origin = old_brush_origin;
       
  4261 }
       
  4262 
       
  4263 static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
       
  4264 {
       
  4265     return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
       
  4266 }
       
  4267 
       
  4268 template <typename T>
       
  4269 static const T qSubImage(const T &image, const QRectF &src, QRectF *srcNew)
       
  4270 {
       
  4271     const int sx1 = qMax(0, qFloor(src.left()));
       
  4272     const int sy1 = qMax(0, qFloor(src.top()));
       
  4273     const int sx2 = qMin(image.width(), qCeil(src.right()));
       
  4274     const int sy2 = qMin(image.height(), qCeil(src.bottom()));
       
  4275 
       
  4276     const T sub = image.copy(sx1, sy1, sx2 - sx1, sy2 - sy1);
       
  4277 
       
  4278     if (srcNew)
       
  4279         *srcNew = src.translated(-sx1, -sy1);
       
  4280 
       
  4281     return sub;
       
  4282 }
       
  4283 
       
  4284 void QOpenGLPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
       
  4285 {
       
  4286     Q_D(QOpenGLPaintEngine);
       
  4287     if (pm.depth() == 1) {
       
  4288         QPixmap tpx(pm.size());
       
  4289         tpx.fill(Qt::transparent);
       
  4290         QPainter p(&tpx);
       
  4291         p.setPen(d->cpen);
       
  4292         p.drawPixmap(0, 0, pm);
       
  4293         p.end();
       
  4294         drawPixmap(r, tpx, sr);
       
  4295         return;
       
  4296     }
       
  4297 
       
  4298     const int sz = d->max_texture_size;
       
  4299     if (pm.width() > sz || pm.height() > sz) {
       
  4300         QRectF subsr;
       
  4301         const QPixmap sub = qSubImage(pm, sr, &subsr);
       
  4302 
       
  4303         if (sub.width() <= sz && sub.height() <= sz) {
       
  4304             drawPixmap(r, sub, subsr);
       
  4305         } else {
       
  4306             const QPixmap scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
       
  4307             const qreal sx = scaled.width() / qreal(sub.width());
       
  4308             const qreal sy = scaled.height() / qreal(sub.height());
       
  4309 
       
  4310             drawPixmap(r, scaled, scaleRect(subsr, sx, sy));
       
  4311         }
       
  4312         return;
       
  4313     }
       
  4314 
       
  4315 
       
  4316     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
       
  4317         d->drawImageAsPath(r, pm.toImage(), sr);
       
  4318     else {
       
  4319         GLenum target = qt_gl_preferredTextureTarget();
       
  4320         d->flushDrawQueue();
       
  4321         QGLTexture *tex =
       
  4322             d->device->context()->d_func()->bindTexture(pm, target, GL_RGBA,
       
  4323                                                         QGLContext::InternalBindOption);
       
  4324         drawTextureRect(pm.width(), pm.height(), r, sr, target, tex);
       
  4325     }
       
  4326 }
       
  4327 
       
  4328 void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &offset)
       
  4329 {
       
  4330     Q_D(QOpenGLPaintEngine);
       
  4331     if (pm.depth() == 1) {
       
  4332         QPixmap tpx(pm.size());
       
  4333         tpx.fill(Qt::transparent);
       
  4334         QPainter p(&tpx);
       
  4335         p.setPen(d->cpen);
       
  4336         p.drawPixmap(0, 0, pm);
       
  4337         p.end();
       
  4338         drawTiledPixmap(r, tpx, offset);
       
  4339         return;
       
  4340     }
       
  4341 
       
  4342     QImage scaled;
       
  4343     const int sz = d->max_texture_size;
       
  4344     if (pm.width() > sz || pm.height() > sz) {
       
  4345         int rw = qCeil(r.width());
       
  4346         int rh = qCeil(r.height());
       
  4347         if (rw < pm.width() && rh < pm.height()) {
       
  4348             drawTiledPixmap(r, pm.copy(0, 0, rw, rh), offset);
       
  4349             return;
       
  4350         }
       
  4351 
       
  4352         scaled = pm.toImage().scaled(sz, sz, Qt::KeepAspectRatio);
       
  4353     }
       
  4354 
       
  4355     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r))) {
       
  4356         if (scaled.isNull())
       
  4357             d->drawTiledImageAsPath(r, pm.toImage(), 1, 1, offset);
       
  4358         else {
       
  4359             const qreal sx = pm.width() / qreal(scaled.width());
       
  4360             const qreal sy = pm.height() / qreal(scaled.height());
       
  4361             d->drawTiledImageAsPath(r, scaled, sx, sy, offset);
       
  4362         }
       
  4363     } else {
       
  4364         d->flushDrawQueue();
       
  4365 
       
  4366         QGLTexture *tex;
       
  4367         if (scaled.isNull())
       
  4368             tex = d->device->context()->d_func()->bindTexture(pm, GL_TEXTURE_2D, GL_RGBA,
       
  4369                                                               QGLContext::InternalBindOption);
       
  4370         else
       
  4371             tex = d->device->context()->d_func()->bindTexture(scaled, GL_TEXTURE_2D, GL_RGBA,
       
  4372                                                               QGLContext::InternalBindOption);
       
  4373         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, d->use_smooth_pixmap_transform);
       
  4374 
       
  4375 #ifndef QT_OPENGL_ES
       
  4376         glPushAttrib(GL_CURRENT_BIT);
       
  4377         glDisable(GL_TEXTURE_GEN_S);
       
  4378 #endif
       
  4379         glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
       
  4380         glEnable(GL_TEXTURE_2D);
       
  4381 
       
  4382         GLdouble tc_w = r.width()/pm.width();
       
  4383         GLdouble tc_h = r.height()/pm.height();
       
  4384 
       
  4385         // Rotate the texture so that it is aligned correctly and the
       
  4386         // wrapping is done correctly
       
  4387         if (tex->options & QGLContext::InvertedYBindOption) {
       
  4388             glMatrixMode(GL_TEXTURE);
       
  4389             glPushMatrix();
       
  4390             glRotatef(180.0, 0.0, 1.0, 0.0);
       
  4391             glRotatef(180.0, 0.0, 0.0, 1.0);
       
  4392         }
       
  4393 
       
  4394         q_vertexType vertexArray[4*2];
       
  4395         q_vertexType texCoordArray[4*2];
       
  4396 
       
  4397         double offset_x = offset.x() / pm.width();
       
  4398         double offset_y = offset.y() / pm.height();
       
  4399 
       
  4400         qt_add_rect_to_array(r, vertexArray);
       
  4401         qt_add_texcoords_to_array(offset_x, offset_y,
       
  4402                                   tc_w + offset_x, tc_h + offset_y, texCoordArray);
       
  4403 
       
  4404         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4405         glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4406 
       
  4407         glEnableClientState(GL_VERTEX_ARRAY);
       
  4408         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4409         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4410         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4411         glDisableClientState(GL_VERTEX_ARRAY);
       
  4412         if (tex->options & QGLContext::InvertedYBindOption)
       
  4413             glPopMatrix();
       
  4414 
       
  4415         glDisable(GL_TEXTURE_2D);
       
  4416 #ifndef QT_OPENGL_ES
       
  4417         glPopAttrib();
       
  4418 #endif
       
  4419     }
       
  4420 }
       
  4421 
       
  4422 void QOpenGLPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
       
  4423                                    Qt::ImageConversionFlags)
       
  4424 {
       
  4425     Q_D(QOpenGLPaintEngine);
       
  4426 
       
  4427     const int sz = d->max_texture_size;
       
  4428     if (image.width() > sz || image.height() > sz) {
       
  4429         QRectF subsr;
       
  4430         const QImage sub = qSubImage(image, sr, &subsr);
       
  4431 
       
  4432         if (sub.width() <= sz && sub.height() <= sz) {
       
  4433             drawImage(r, sub, subsr, 0);
       
  4434         } else {
       
  4435             const QImage scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
       
  4436             const qreal sx = scaled.width() / qreal(sub.width());
       
  4437             const qreal sy = scaled.height() / qreal(sub.height());
       
  4438 
       
  4439             drawImage(r, scaled, scaleRect(subsr, sx, sy), 0);
       
  4440         }
       
  4441         return;
       
  4442     }
       
  4443 
       
  4444     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
       
  4445         d->drawImageAsPath(r, image, sr);
       
  4446     else {
       
  4447         GLenum target = qt_gl_preferredTextureTarget();
       
  4448         d->flushDrawQueue();
       
  4449         QGLTexture *tex =
       
  4450             d->device->context()->d_func()->bindTexture(image, target, GL_RGBA,
       
  4451                                                         QGLContext::InternalBindOption);
       
  4452         drawTextureRect(image.width(), image.height(), r, sr, target, tex);
       
  4453     }
       
  4454 }
       
  4455 
       
  4456 void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRectF &r,
       
  4457                                          const QRectF &sr, GLenum target, QGLTexture *tex)
       
  4458 {
       
  4459     Q_D(QOpenGLPaintEngine);
       
  4460 #ifndef QT_OPENGL_ES
       
  4461     glPushAttrib(GL_CURRENT_BIT);
       
  4462     glDisable(GL_TEXTURE_GEN_S);
       
  4463 #endif
       
  4464     glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
       
  4465     glEnable(target);
       
  4466     updateTextureFilter(target, GL_CLAMP_TO_EDGE, d->use_smooth_pixmap_transform);
       
  4467 
       
  4468     qreal x1, x2, y1, y2;
       
  4469     if (target == GL_TEXTURE_2D) {
       
  4470         x1 = sr.x() / tx_width;
       
  4471         x2 = x1 + sr.width() / tx_width;
       
  4472         if (tex->options & QGLContext::InvertedYBindOption) {
       
  4473             y1 = 1 - (sr.bottom() / tx_height);
       
  4474             y2 = 1 - (sr.y() / tx_height);
       
  4475         } else {
       
  4476             y1 = sr.bottom() / tx_height;
       
  4477             y2 = sr.y() / tx_height;
       
  4478         }
       
  4479     } else {
       
  4480         x1 = sr.x();
       
  4481         x2 = sr.right();
       
  4482         y1 = sr.bottom();
       
  4483         y2 = sr.y();
       
  4484     }
       
  4485 
       
  4486     q_vertexType vertexArray[4*2];
       
  4487     q_vertexType texCoordArray[4*2];
       
  4488 
       
  4489     qt_add_rect_to_array(r, vertexArray);
       
  4490     qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
       
  4491 
       
  4492     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4493     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4494 
       
  4495     glEnableClientState(GL_VERTEX_ARRAY);
       
  4496     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4497     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4498     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4499     glDisableClientState(GL_VERTEX_ARRAY);
       
  4500 
       
  4501     glDisable(target);
       
  4502 #ifndef QT_OPENGL_ES
       
  4503     glPopAttrib();
       
  4504 #endif
       
  4505 }
       
  4506 
       
  4507 #ifdef Q_WS_WIN
       
  4508 HDC
       
  4509 #else
       
  4510 Qt::HANDLE
       
  4511 #endif
       
  4512 QOpenGLPaintEngine::handle() const
       
  4513 {
       
  4514     return 0;
       
  4515 }
       
  4516 
       
  4517 static const int x_margin = 1;
       
  4518 static const int y_margin = 0;
       
  4519 
       
  4520 struct QGLGlyphCoord {
       
  4521     // stores the offset and size of a glyph texture
       
  4522     qreal x;
       
  4523     qreal y;
       
  4524     qreal width;
       
  4525     qreal height;
       
  4526     qreal log_width;
       
  4527     qreal log_height;
       
  4528     QFixed x_offset;
       
  4529     QFixed y_offset;
       
  4530 };
       
  4531 
       
  4532 struct QGLFontTexture {
       
  4533     int x_offset; // glyph offset within the
       
  4534     int y_offset;
       
  4535     GLuint texture;
       
  4536     int width;
       
  4537     int height;
       
  4538 };
       
  4539 
       
  4540 typedef QHash<glyph_t, QGLGlyphCoord*>  QGLGlyphHash;
       
  4541 typedef QHash<QFontEngine*, QGLGlyphHash*> QGLFontGlyphHash;
       
  4542 typedef QHash<quint64, QGLFontTexture*> QGLFontTexHash;
       
  4543 typedef QHash<const QGLContext*, QGLFontGlyphHash*> QGLContextHash;
       
  4544 
       
  4545 static inline void qt_delete_glyph_hash(QGLGlyphHash *hash)
       
  4546 {
       
  4547     qDeleteAll(*hash);
       
  4548     delete hash;
       
  4549 }
       
  4550 
       
  4551 class QGLGlyphCache : public QObject
       
  4552 {
       
  4553     Q_OBJECT
       
  4554 public:
       
  4555     QGLGlyphCache() : QObject(0) { current_cache = 0; }
       
  4556     ~QGLGlyphCache();
       
  4557     QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
       
  4558     void cacheGlyphs(QGLContext *, const QTextItemInt &, const QVarLengthArray<glyph_t> &);
       
  4559     void cleanCache();
       
  4560     void allocTexture(int width, int height, GLuint texture);
       
  4561 
       
  4562 public slots:
       
  4563     void cleanupContext(const QGLContext *);
       
  4564     void fontEngineDestroyed(QObject *);
       
  4565     void widgetDestroyed(QObject *);
       
  4566 
       
  4567 protected:
       
  4568     QGLGlyphHash *current_cache;
       
  4569     QGLFontTexHash qt_font_textures;
       
  4570     QGLContextHash qt_context_cache;
       
  4571 };
       
  4572 
       
  4573 QGLGlyphCache::~QGLGlyphCache()
       
  4574 {
       
  4575 //     qDebug() << "cleaning out the QGLGlyphCache";
       
  4576     cleanCache();
       
  4577 }
       
  4578 
       
  4579 void QGLGlyphCache::fontEngineDestroyed(QObject *o)
       
  4580 {
       
  4581 //     qDebug() << "fontEngineDestroyed()";
       
  4582     QFontEngine *fe = static_cast<QFontEngine *>(o); // safe, since only the type is used
       
  4583     QList<const QGLContext *> keys = qt_context_cache.keys();
       
  4584     const QGLContext *ctx = 0;
       
  4585 
       
  4586     for (int i=0; i < keys.size(); ++i) {
       
  4587         QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
       
  4588         if (font_cache->find(fe) != font_cache->end()) {
       
  4589             ctx = keys.at(i);
       
  4590             QGLGlyphHash *cache = font_cache->take(fe);
       
  4591             qt_delete_glyph_hash(cache);
       
  4592             break;
       
  4593         }
       
  4594     }
       
  4595 
       
  4596     quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
       
  4597     QGLFontTexture *tex = qt_font_textures.take(font_key);
       
  4598     if (tex) {
       
  4599 #ifdef Q_WS_MAC
       
  4600         if (
       
  4601 #  ifndef QT_MAC_USE_COCOA
       
  4602             aglGetCurrentContext() != 0
       
  4603 #  else
       
  4604             qt_current_nsopengl_context() != 0
       
  4605 #  endif
       
  4606            )
       
  4607 #endif
       
  4608             glDeleteTextures(1, &tex->texture);
       
  4609         delete tex;
       
  4610     }
       
  4611 }
       
  4612 
       
  4613 void QGLGlyphCache::widgetDestroyed(QObject *)
       
  4614 {
       
  4615 //     qDebug() << "widget destroyed";
       
  4616     cleanCache(); // ###
       
  4617 }
       
  4618 
       
  4619 void QGLGlyphCache::cleanupContext(const QGLContext *ctx)
       
  4620 {
       
  4621 //     qDebug() << "==> cleaning for: " << hex << ctx;
       
  4622     QGLFontGlyphHash *font_cache = qt_context_cache.take(ctx);
       
  4623 
       
  4624     if (font_cache) {
       
  4625         QList<QFontEngine *> keys = font_cache->keys();
       
  4626         for (int i=0; i < keys.size(); ++i) {
       
  4627             QFontEngine *fe = keys.at(i);
       
  4628             qt_delete_glyph_hash(font_cache->take(fe));
       
  4629             quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
       
  4630             QGLFontTexture *font_tex = qt_font_textures.take(font_key);
       
  4631             if (font_tex) {
       
  4632 #ifdef Q_WS_MAC
       
  4633                 if (
       
  4634 #  ifndef QT_MAC_USE_COCOA
       
  4635             aglGetCurrentContext() == 0
       
  4636 #  else
       
  4637             qt_current_nsopengl_context() != 0
       
  4638 #  endif
       
  4639                    )
       
  4640 #endif
       
  4641                     glDeleteTextures(1, &font_tex->texture);
       
  4642                 delete font_tex;
       
  4643             }
       
  4644         }
       
  4645         delete font_cache;
       
  4646     }
       
  4647 //    qDebug() << "<=== done cleaning, num tex:" << qt_font_textures.size() << "num ctx:" << qt_context_cache.size();
       
  4648 }
       
  4649 
       
  4650 void QGLGlyphCache::cleanCache()
       
  4651 {
       
  4652     QGLFontTexHash::const_iterator it = qt_font_textures.constBegin();
       
  4653     if (QGLContext::currentContext()) {
       
  4654         while (it != qt_font_textures.constEnd()) {
       
  4655 #if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
       
  4656             if (qt_current_nsopengl_context() == 0)
       
  4657                 break;
       
  4658 #endif
       
  4659             glDeleteTextures(1, &it.value()->texture);
       
  4660             ++it;
       
  4661         }
       
  4662     }
       
  4663     qDeleteAll(qt_font_textures);
       
  4664     qt_font_textures.clear();
       
  4665 
       
  4666     QList<const QGLContext *> keys = qt_context_cache.keys();
       
  4667     for (int i=0; i < keys.size(); ++i) {
       
  4668         QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
       
  4669         QGLFontGlyphHash::Iterator it = font_cache->begin();
       
  4670         for (; it != font_cache->end(); ++it)
       
  4671             qt_delete_glyph_hash(it.value());
       
  4672         font_cache->clear();
       
  4673     }
       
  4674     qDeleteAll(qt_context_cache);
       
  4675     qt_context_cache.clear();
       
  4676 }
       
  4677 
       
  4678 void QGLGlyphCache::allocTexture(int width, int height, GLuint texture)
       
  4679 {
       
  4680     uchar *tex_data = (uchar *) malloc(width*height*2);
       
  4681     memset(tex_data, 0, width*height*2);
       
  4682     glBindTexture(GL_TEXTURE_2D, texture);
       
  4683 #ifndef QT_OPENGL_ES
       
  4684     glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8,
       
  4685                  width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4686 #else
       
  4687     glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
       
  4688                  width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4689 #endif
       
  4690     free(tex_data);
       
  4691 }
       
  4692 
       
  4693 #if 0
       
  4694 // useful for debugging the glyph cache
       
  4695 static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
       
  4696 {
       
  4697     ushort *old_tex_data = (ushort *) malloc(font_tex->width*font_tex->height*2);
       
  4698     glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4699     QImage im(font_tex->width, font_tex->height, QImage::Format_ARGB32);
       
  4700     for (int y=0; y<font_tex->height; ++y) {
       
  4701         for (int x=0; x<font_tex->width; ++x) {
       
  4702             im.setPixel(x, y, ((*(old_tex_data+x+y*font_tex->width)) << 24) | (0x00ffffff & color.rgb()));
       
  4703         }
       
  4704     }
       
  4705     delete old_tex_data;
       
  4706     return im;
       
  4707 }
       
  4708 #endif
       
  4709 
       
  4710 void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
       
  4711                                 const QVarLengthArray<glyph_t> &glyphs)
       
  4712 {
       
  4713     QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
       
  4714     QGLFontGlyphHash *font_cache = 0;
       
  4715     const QGLContext *context_key = 0;
       
  4716 
       
  4717     if (dev_it == qt_context_cache.constEnd()) {
       
  4718         // check for shared contexts
       
  4719         QList<const QGLContext *> contexts = qt_context_cache.keys();
       
  4720         for (int i=0; i<contexts.size(); ++i) {
       
  4721             const QGLContext *ctx = contexts.at(i);
       
  4722             if (ctx != context && QGLContext::areSharing(context, ctx)) {
       
  4723                 context_key = ctx;
       
  4724                 dev_it = qt_context_cache.constFind(context_key);
       
  4725                 break;
       
  4726             }
       
  4727         }
       
  4728     }
       
  4729 
       
  4730     if (dev_it == qt_context_cache.constEnd()) {
       
  4731         // no shared contexts either - create a new entry
       
  4732         font_cache = new QGLFontGlyphHash;
       
  4733 //         qDebug() << "new context" << context << font_cache;
       
  4734         qt_context_cache.insert(context, font_cache);
       
  4735         if (context->isValid() && context->device()->devType() == QInternal::Widget) {
       
  4736             QWidget *widget = static_cast<QWidget *>(context->device());
       
  4737             connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
       
  4738             connect(QGLSignalProxy::instance(),
       
  4739                     SIGNAL(aboutToDestroyContext(const QGLContext*)),
       
  4740                     SLOT(cleanupContext(const QGLContext*)));
       
  4741         }
       
  4742     } else {
       
  4743         font_cache = dev_it.value();
       
  4744     }
       
  4745     Q_ASSERT(font_cache != 0);
       
  4746 
       
  4747     QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(ti.fontEngine);
       
  4748     QGLGlyphHash *cache = 0;
       
  4749     if (cache_it == font_cache->constEnd()) {
       
  4750         cache = new QGLGlyphHash;
       
  4751         font_cache->insert(ti.fontEngine, cache);
       
  4752         connect(ti.fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
       
  4753     } else {
       
  4754         cache = cache_it.value();
       
  4755     }
       
  4756     current_cache = cache;
       
  4757 
       
  4758     quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
       
  4759                        | reinterpret_cast<quint64>(ti.fontEngine);
       
  4760     QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
       
  4761     QGLFontTexture *font_tex;
       
  4762     if (it == qt_font_textures.constEnd()) {
       
  4763         GLuint font_texture;
       
  4764         glGenTextures(1, &font_texture);
       
  4765         GLint tex_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
       
  4766         GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
       
  4767         GLint max_tex_size;
       
  4768         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
       
  4769         Q_ASSERT(max_tex_size > 0);
       
  4770         if (tex_width > max_tex_size)
       
  4771             tex_width = max_tex_size;
       
  4772         allocTexture(tex_width, tex_height, font_texture);
       
  4773         font_tex = new QGLFontTexture;
       
  4774         font_tex->texture = font_texture;
       
  4775         font_tex->x_offset = x_margin;
       
  4776         font_tex->y_offset = y_margin;
       
  4777         font_tex->width = tex_width;
       
  4778         font_tex->height = tex_height;
       
  4779 //         qDebug() << "new font tex - width:" << tex_width << "height:"<< tex_height
       
  4780 //                  << hex << "tex id:" << font_tex->texture << "key:" << font_key << "num cached:" << qt_font_textures.size();
       
  4781         qt_font_textures.insert(font_key, font_tex);
       
  4782     } else {
       
  4783         font_tex = it.value();
       
  4784         glBindTexture(GL_TEXTURE_2D, font_tex->texture);
       
  4785     }
       
  4786 
       
  4787     for (int i=0; i< glyphs.size(); ++i) {
       
  4788         QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
       
  4789         if (it == cache->constEnd()) {
       
  4790             // render new glyph and put it in the cache
       
  4791             glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
       
  4792             int glyph_width = qRound(metrics.width.toReal())+2;
       
  4793             int glyph_height = qRound(ti.ascent.toReal() + ti.descent.toReal())+2;
       
  4794 
       
  4795             if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
       
  4796                 int strip_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
       
  4797                 font_tex->x_offset = x_margin;
       
  4798                 font_tex->y_offset += strip_height;
       
  4799                 if (font_tex->y_offset >= font_tex->height) {
       
  4800                     // get hold of the old font texture
       
  4801                     uchar *old_tex_data = (uchar *) malloc(font_tex->width*font_tex->height*2);
       
  4802                     int old_tex_height = font_tex->height;
       
  4803 #ifndef QT_OPENGL_ES
       
  4804                     glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4805 #endif
       
  4806 
       
  4807                     // realloc a larger texture
       
  4808                     glDeleteTextures(1, &font_tex->texture);
       
  4809                     glGenTextures(1, &font_tex->texture);
       
  4810                     font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
       
  4811                     allocTexture(font_tex->width, font_tex->height, font_tex->texture);
       
  4812 
       
  4813                     // write back the old texture data
       
  4814                     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, font_tex->width, old_tex_height,
       
  4815                                     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4816                     free(old_tex_data);
       
  4817 
       
  4818                     // update the texture coords and the y offset for the existing glyphs in
       
  4819                     // the cache, because of the texture size change
       
  4820                     QGLGlyphHash::iterator it = cache->begin();
       
  4821                     while (it != cache->end()) {
       
  4822                         it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
       
  4823                         it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
       
  4824                         ++it;
       
  4825                     }
       
  4826                 }
       
  4827             }
       
  4828 
       
  4829             QImage glyph_im(ti.fontEngine->alphaMapForGlyph(glyphs[i]));
       
  4830             glyph_im = glyph_im.convertToFormat(QImage::Format_Indexed8);
       
  4831             glyph_width = glyph_im.width();
       
  4832             Q_ASSERT(glyph_width >= 0);
       
  4833             // pad the glyph width to an even number
       
  4834             if (glyph_width%2 != 0)
       
  4835                 ++glyph_width;
       
  4836 
       
  4837             QGLGlyphCoord *qgl_glyph = new QGLGlyphCoord;
       
  4838             qgl_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
       
  4839             qgl_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
       
  4840             qgl_glyph->width = qreal(glyph_width) / font_tex->width;
       
  4841             qgl_glyph->height = qreal(glyph_height) / font_tex->height;
       
  4842             qgl_glyph->log_width = qreal(glyph_width);
       
  4843             qgl_glyph->log_height = qgl_glyph->height * font_tex->height;
       
  4844 #ifdef Q_WS_MAC
       
  4845             qgl_glyph->x_offset = -metrics.x + 1;
       
  4846             qgl_glyph->y_offset = metrics.y - 2;
       
  4847 #else
       
  4848             qgl_glyph->x_offset = -metrics.x;
       
  4849             qgl_glyph->y_offset = metrics.y;
       
  4850 #endif
       
  4851 
       
  4852             if (!glyph_im.isNull()) {
       
  4853 
       
  4854                 int idx = 0;
       
  4855                 uchar *tex_data = (uchar *) malloc(glyph_width*glyph_im.height()*2);
       
  4856                 memset(tex_data, 0, glyph_width*glyph_im.height()*2);
       
  4857 
       
  4858                 for (int y=0; y<glyph_im.height(); ++y) {
       
  4859                     uchar *s = (uchar *) glyph_im.scanLine(y);
       
  4860                     for (int x=0; x<glyph_im.width(); ++x) {
       
  4861                         uchar alpha = qAlpha(glyph_im.color(*s));
       
  4862                         tex_data[idx] = alpha;
       
  4863                         tex_data[idx+1] = alpha;
       
  4864                         ++s;
       
  4865                         idx += 2;
       
  4866                     }
       
  4867                     if (glyph_im.width()%2 != 0)
       
  4868                         idx += 2;
       
  4869                 }
       
  4870                 glTexSubImage2D(GL_TEXTURE_2D, 0, font_tex->x_offset, font_tex->y_offset,
       
  4871                                 glyph_width, glyph_im.height(),
       
  4872                                 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4873                 free(tex_data);
       
  4874             }
       
  4875             if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
       
  4876                 font_tex->x_offset = x_margin;
       
  4877                 font_tex->y_offset += glyph_height + y_margin;
       
  4878             } else {
       
  4879                 font_tex->x_offset += glyph_width + x_margin;
       
  4880             }
       
  4881 
       
  4882             cache->insert(glyphs[i], qgl_glyph);
       
  4883         }
       
  4884     }
       
  4885 }
       
  4886 
       
  4887 QGLGlyphCoord *QGLGlyphCache::lookup(QFontEngine *, glyph_t g)
       
  4888 {
       
  4889     Q_ASSERT(current_cache != 0);
       
  4890     // ### careful here
       
  4891     QGLGlyphHash::const_iterator it = current_cache->constFind(g);
       
  4892     if (it == current_cache->constEnd())
       
  4893         return 0;
       
  4894     else
       
  4895         return it.value();
       
  4896 }
       
  4897 
       
  4898 Q_GLOBAL_STATIC(QGLGlyphCache, qt_glyph_cache)
       
  4899 
       
  4900 //
       
  4901 // assumption: the context that this is called for has to be the
       
  4902 // current context
       
  4903 //
       
  4904 void qgl_cleanup_glyph_cache(QGLContext *ctx)
       
  4905 {
       
  4906     qt_glyph_cache()->cleanupContext(ctx);
       
  4907 }
       
  4908 
       
  4909 void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
       
  4910 {
       
  4911     Q_D(QOpenGLPaintEngine);
       
  4912 
       
  4913     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
       
  4914 
       
  4915     // fall back to drawing a polygon if the scale factor is large, or
       
  4916     // we use a gradient pen
       
  4917     if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
       
  4918                                   && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
       
  4919         QPaintEngine::drawTextItem(p, textItem);
       
  4920         return;
       
  4921     }
       
  4922 
       
  4923     d->flushDrawQueue();
       
  4924 
       
  4925     // add the glyphs used to the glyph texture cache
       
  4926     QVarLengthArray<QFixedPoint> positions;
       
  4927     QVarLengthArray<glyph_t> glyphs;
       
  4928     QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(p.y()));
       
  4929     ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
       
  4930 
       
  4931     // make sure the glyphs we want to draw are in the cache
       
  4932     qt_glyph_cache()->cacheGlyphs(d->device->context(), ti, glyphs);
       
  4933 
       
  4934     d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
       
  4935     qt_glColor4ubv(d->pen_color);
       
  4936     glEnable(GL_TEXTURE_2D);
       
  4937 
       
  4938 #ifdef Q_WS_QWS
       
  4939     // XXX: it is necessary to disable alpha writes on GLES/embedded because we don't want
       
  4940     // text rendering to update the alpha in the window surface.
       
  4941     // XXX: This may not be needed as this behavior does seem to be caused by driver bug
       
  4942     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
       
  4943 #endif
       
  4944 
       
  4945     // do the actual drawing
       
  4946     q_vertexType vertexArray[4*2];
       
  4947     q_vertexType texCoordArray[4*2];
       
  4948 
       
  4949     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4950     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4951 
       
  4952     glEnableClientState(GL_VERTEX_ARRAY);
       
  4953     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4954     bool antialias = !(ti.fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
       
  4955                    && (d->matrix.type() > QTransform::TxTranslate);
       
  4956     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
       
  4957     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
       
  4958 
       
  4959     for (int i=0; i< glyphs.size(); ++i) {
       
  4960         QGLGlyphCoord *g = qt_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
       
  4961 
       
  4962         // we don't cache glyphs with no width/height
       
  4963         if (!g)
       
  4964             continue;
       
  4965 
       
  4966         qreal x1, x2, y1, y2;
       
  4967         x1 = g->x;
       
  4968         y1 = g->y;
       
  4969         x2 = x1 + g->width;
       
  4970         y2 = y1 + g->height;
       
  4971 
       
  4972         QPointF logical_pos((positions[i].x - g->x_offset).toReal(),
       
  4973                             (positions[i].y + g->y_offset).toReal());
       
  4974 
       
  4975         qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
       
  4976         qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
       
  4977 
       
  4978         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4979     }
       
  4980 
       
  4981     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4982     glDisableClientState(GL_VERTEX_ARRAY);
       
  4983 
       
  4984     glDisable(GL_TEXTURE_2D);
       
  4985 
       
  4986 #ifdef Q_WS_QWS
       
  4987     // XXX: This may not be needed as this behavior does seem to be caused by driver bug
       
  4988     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  4989 #endif
       
  4990 }
       
  4991 
       
  4992 
       
  4993 void QOpenGLPaintEngine::drawEllipse(const QRectF &rect)
       
  4994 {
       
  4995 #ifndef Q_WS_QWS
       
  4996     Q_D(QOpenGLPaintEngine);
       
  4997 
       
  4998     if (d->use_emulation) {
       
  4999         QPaintEngineEx::drawEllipse(rect);
       
  5000         return;
       
  5001     }
       
  5002 
       
  5003     if (d->high_quality_antialiasing) {
       
  5004         if (d->has_brush) {
       
  5005             d->disableClipping();
       
  5006 
       
  5007             glMatrixMode(GL_MODELVIEW);
       
  5008             glPushMatrix();
       
  5009             glLoadIdentity();
       
  5010 
       
  5011             GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
       
  5012                                                                FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, 0, true);
       
  5013             QGLEllipseMaskGenerator maskGenerator(rect,
       
  5014                                                   d->matrix,
       
  5015                                                   d->offscreen,
       
  5016                                                   program,
       
  5017                                                   mask_variable_locations[FRAGMENT_PROGRAM_MASK_ELLIPSE_AA]);
       
  5018 
       
  5019             d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  5020 
       
  5021             d->enableClipping();
       
  5022 
       
  5023             glMatrixMode(GL_MODELVIEW);
       
  5024             glPopMatrix();
       
  5025         }
       
  5026 
       
  5027         if (d->has_pen) {
       
  5028             QPainterPath path;
       
  5029             path.addEllipse(rect);
       
  5030 
       
  5031             d->strokePath(path, false);
       
  5032         }
       
  5033     } else {
       
  5034         DEBUG_ONCE_STR("QOpenGLPaintEngine::drawEllipse(): falling back to drawPath()");
       
  5035 
       
  5036         QPainterPath path;
       
  5037         path.addEllipse(rect);
       
  5038         drawPath(path);
       
  5039     }
       
  5040 #else
       
  5041     QPaintEngineEx::drawEllipse(rect);
       
  5042 #endif
       
  5043 }
       
  5044 
       
  5045 
       
  5046 void QOpenGLPaintEnginePrivate::updateFragmentProgramData(int locations[])
       
  5047 {
       
  5048 #ifdef Q_WS_QWS
       
  5049     Q_UNUSED(locations);
       
  5050 #else
       
  5051     QGL_FUNC_CONTEXT;
       
  5052 
       
  5053     QSize sz = offscreen.offscreenSize();
       
  5054 
       
  5055     float inv_mask_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
       
  5056 
       
  5057     sz = drawable_texture_size;
       
  5058 
       
  5059     float inv_dst_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
       
  5060 
       
  5061     // default inv size 0.125f == 1.0f / 8.0f for pattern brushes
       
  5062     float inv_brush_texture_size_data[4] = { 0.125f, 0.125f };
       
  5063 
       
  5064     // texture patterns have their own size
       
  5065     if (current_style == Qt::TexturePattern) {
       
  5066         QSize sz = cbrush.texture().size();
       
  5067 
       
  5068         inv_brush_texture_size_data[0] = 1.0f / sz.width();
       
  5069         inv_brush_texture_size_data[1] = 1.0f / sz.height();
       
  5070     }
       
  5071 
       
  5072     for (unsigned int i = 0; i < num_fragment_variables; ++i) {
       
  5073         int location = locations[i];
       
  5074 
       
  5075         if (location < 0)
       
  5076             continue;
       
  5077 
       
  5078         switch (i) {
       
  5079         case VAR_ANGLE:
       
  5080             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, angle_data);
       
  5081             break;
       
  5082         case VAR_LINEAR:
       
  5083             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, linear_data);
       
  5084             break;
       
  5085         case VAR_FMP:
       
  5086             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp_data);
       
  5087             break;
       
  5088         case VAR_FMP2_M_RADIUS2:
       
  5089             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp2_m_radius2_data);
       
  5090             break;
       
  5091         case VAR_INV_MASK_SIZE:
       
  5092             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_mask_size_data);
       
  5093             break;
       
  5094         case VAR_INV_DST_SIZE:
       
  5095             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_dst_size_data);
       
  5096             break;
       
  5097         case VAR_INV_MATRIX_M0:
       
  5098             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[0]);
       
  5099             break;
       
  5100         case VAR_INV_MATRIX_M1:
       
  5101             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[1]);
       
  5102             break;
       
  5103         case VAR_INV_MATRIX_M2:
       
  5104             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[2]);
       
  5105             break;
       
  5106         case VAR_PORTERDUFF_AB:
       
  5107             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_ab_data);
       
  5108             break;
       
  5109         case VAR_PORTERDUFF_XYZ:
       
  5110             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_xyz_data);
       
  5111             break;
       
  5112         case VAR_INV_BRUSH_TEXTURE_SIZE:
       
  5113             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_brush_texture_size_data);
       
  5114             break;
       
  5115         case VAR_MASK_OFFSET:
       
  5116             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_offset_data);
       
  5117             break;
       
  5118         case VAR_MASK_CHANNEL:
       
  5119             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_channel_data);
       
  5120             break;
       
  5121         case VAR_DST_TEXTURE:
       
  5122         case VAR_MASK_TEXTURE:
       
  5123         case VAR_PALETTE:
       
  5124         case VAR_BRUSH_TEXTURE:
       
  5125             // texture variables, not handled here
       
  5126             break;
       
  5127         default:
       
  5128             qDebug() << "QOpenGLPaintEnginePrivate: Unhandled fragment variable:" << i;
       
  5129         }
       
  5130     }
       
  5131 #endif
       
  5132 }
       
  5133 
       
  5134 
       
  5135 void QOpenGLPaintEnginePrivate::copyDrawable(const QRectF &rect)
       
  5136 {
       
  5137 #ifdef Q_WS_QWS
       
  5138     Q_UNUSED(rect);
       
  5139 #else
       
  5140     ensureDrawableTexture();
       
  5141 
       
  5142     DEBUG_ONCE qDebug() << "Refreshing drawable_texture for rectangle" << rect;
       
  5143     QRectF screen_rect = rect.adjusted(-1, -1, 1, 1);
       
  5144 
       
  5145     int left = qMax(0, static_cast<int>(screen_rect.left()));
       
  5146     int width = qMin(device->size().width() - left, static_cast<int>(screen_rect.width()) + 1);
       
  5147 
       
  5148     int bottom = qMax(0, static_cast<int>(device->size().height() - screen_rect.bottom()));
       
  5149     int height = qMin(device->size().height() - bottom, static_cast<int>(screen_rect.height()) + 1);
       
  5150 
       
  5151     glBindTexture(GL_TEXTURE_2D, drawable_texture);
       
  5152     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, left, bottom, width, height);
       
  5153 #endif
       
  5154 }
       
  5155 
       
  5156 
       
  5157 void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &maskOffset)
       
  5158 {
       
  5159 #ifdef Q_WS_QWS
       
  5160     Q_UNUSED(rect);
       
  5161     Q_UNUSED(maskOffset);
       
  5162 #else
       
  5163     q_vertexType vertexArray[8];
       
  5164     qt_add_rect_to_array(rect, vertexArray);
       
  5165 
       
  5166     composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
       
  5167 #endif
       
  5168 }
       
  5169 
       
  5170 
       
  5171 void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset)
       
  5172 {
       
  5173 #ifdef QT_OPENGL_ES
       
  5174     Q_UNUSED(primitive);
       
  5175     Q_UNUSED(vertexArray);
       
  5176     Q_UNUSED(vertexCount);
       
  5177     Q_UNUSED(maskOffset);
       
  5178 #else
       
  5179     Q_Q(QOpenGLPaintEngine);
       
  5180     QGL_FUNC_CONTEXT;
       
  5181 
       
  5182     if (current_style == Qt::NoBrush)
       
  5183         return;
       
  5184 
       
  5185     DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Using compositing program: fragment_brush ="
       
  5186                         << fragment_brush << ", fragment_composition_mode =" << fragment_composition_mode;
       
  5187 
       
  5188     if (has_fast_composition_mode)
       
  5189         q->updateCompositionMode(composition_mode);
       
  5190     else {
       
  5191         qreal minX = 1e9, minY = 1e9, maxX = -1e9, maxY = -1e9;
       
  5192 
       
  5193         for (int i = 0; i < vertexCount; ++i) {
       
  5194             qreal x = vt2f(vertexArray[2 * i]);
       
  5195             qreal y = vt2f(vertexArray[2 * i + 1]);
       
  5196 
       
  5197             qreal tx, ty;
       
  5198             matrix.map(x, y, &tx, &ty);
       
  5199 
       
  5200             minX = qMin(minX, tx);
       
  5201             minY = qMin(minY, ty);
       
  5202             maxX = qMax(maxX, tx);
       
  5203             maxY = qMax(maxY, ty);
       
  5204         }
       
  5205 
       
  5206         QRectF r(minX, minY, maxX - minX, maxY - minY);
       
  5207         copyDrawable(r);
       
  5208 
       
  5209         glBlendFunc(GL_ONE, GL_ZERO);
       
  5210     }
       
  5211 
       
  5212     int *locations = painter_variable_locations[fragment_brush][fragment_composition_mode];
       
  5213 
       
  5214     int texture_locations[] = { locations[VAR_DST_TEXTURE],
       
  5215                                 locations[VAR_MASK_TEXTURE],
       
  5216                                 locations[VAR_PALETTE] };
       
  5217 
       
  5218     int brush_texture_location = locations[VAR_BRUSH_TEXTURE];
       
  5219 
       
  5220     GLuint texture_targets[] = { GL_TEXTURE_2D,
       
  5221                                  GL_TEXTURE_2D,
       
  5222                                  GL_TEXTURE_1D };
       
  5223 
       
  5224     GLuint textures[] = { drawable_texture,
       
  5225                           offscreen.offscreenTexture(),
       
  5226                           grad_palette };
       
  5227 
       
  5228     const int num_textures = sizeof(textures) / sizeof(*textures);
       
  5229 
       
  5230     Q_ASSERT(num_textures == sizeof(texture_locations) / sizeof(*texture_locations));
       
  5231     Q_ASSERT(num_textures == sizeof(texture_targets) / sizeof(*texture_targets));
       
  5232 
       
  5233     for (int i = 0; i < num_textures; ++i)
       
  5234         if (texture_locations[i] >= 0) {
       
  5235             glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
       
  5236             glBindTexture(texture_targets[i], textures[i]);
       
  5237         }
       
  5238 
       
  5239     if (brush_texture_location >= 0) {
       
  5240         glActiveTexture(GL_TEXTURE0 + brush_texture_location);
       
  5241 
       
  5242         if (current_style == Qt::TexturePattern)
       
  5243             device->context()->d_func()->bindTexture(cbrush.textureImage(), GL_TEXTURE_2D, GL_RGBA,
       
  5244                                                      QGLContext::InternalBindOption);
       
  5245         else
       
  5246             device->context()->d_func()->bindTexture(qt_imageForBrush(current_style, false),
       
  5247                                                      GL_TEXTURE_2D, GL_RGBA,
       
  5248                                                      QGLContext::InternalBindOption);
       
  5249 
       
  5250         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, use_smooth_pixmap_transform);
       
  5251     }
       
  5252 
       
  5253     glEnableClientState(GL_VERTEX_ARRAY);
       
  5254     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  5255     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  5256     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  5257                                                        fragment_brush,
       
  5258                                                        fragment_composition_mode, false);
       
  5259     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
       
  5260 
       
  5261     mask_offset_data[0] = maskOffset.x();
       
  5262     mask_offset_data[1] = -maskOffset.y();
       
  5263 
       
  5264     updateFragmentProgramData(locations);
       
  5265 
       
  5266     glDrawArrays(primitive, 0, vertexCount);
       
  5267 
       
  5268     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  5269     glDisableClientState(GL_VERTEX_ARRAY);
       
  5270 
       
  5271     for (int i = 0; i < num_textures; ++i)
       
  5272         if (texture_locations[i] >= 0) {
       
  5273             glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
       
  5274             glBindTexture(texture_targets[i], 0);
       
  5275         }
       
  5276 
       
  5277     if (brush_texture_location >= 0) {
       
  5278         glActiveTexture(GL_TEXTURE0 + brush_texture_location);
       
  5279         glBindTexture(GL_TEXTURE_2D, 0);
       
  5280     }
       
  5281 
       
  5282     glActiveTexture(GL_TEXTURE0);
       
  5283 
       
  5284     if (!has_fast_composition_mode)
       
  5285         q->updateCompositionMode(composition_mode);
       
  5286 #endif
       
  5287 }
       
  5288 
       
  5289 void QOpenGLPaintEnginePrivate::cacheItemErased(int channel, const QRect &rect)
       
  5290 {
       
  5291     bool isInDrawQueue = false;
       
  5292 
       
  5293     foreach (const QDrawQueueItem &item, drawQueue) {
       
  5294         if (item.location.channel == channel && item.location.rect == rect) {
       
  5295             isInDrawQueue = true;
       
  5296             break;
       
  5297         }
       
  5298     }
       
  5299 
       
  5300     if (isInDrawQueue)
       
  5301         flushDrawQueue();
       
  5302 }
       
  5303 
       
  5304 void QOpenGLPaintEnginePrivate::addItem(const QGLMaskTextureCache::CacheLocation &location)
       
  5305 {
       
  5306     drawQueue << QDrawQueueItem(opacity, cbrush, brush_origin, composition_mode, matrix, location);
       
  5307 }
       
  5308 
       
  5309 void QOpenGLPaintEnginePrivate::drawItem(const QDrawQueueItem &item)
       
  5310 {
       
  5311     Q_Q(QOpenGLPaintEngine);
       
  5312 
       
  5313     opacity = item.opacity;
       
  5314     brush_origin = item.brush_origin;
       
  5315     q->updateCompositionMode(item.composition_mode);
       
  5316     matrix = item.matrix;
       
  5317     cbrush = item.brush;
       
  5318     brush_style = item.brush.style();
       
  5319 
       
  5320     mask_channel_data[0] = item.location.channel == 0;
       
  5321     mask_channel_data[1] = item.location.channel == 1;
       
  5322     mask_channel_data[2] = item.location.channel == 2;
       
  5323     mask_channel_data[3] = item.location.channel == 3;
       
  5324 
       
  5325     setGradientOps(item.brush, item.location.screen_rect);
       
  5326 
       
  5327     composite(item.location.screen_rect, item.location.rect.topLeft() - item.location.screen_rect.topLeft()
       
  5328                                          - QPoint(0, offscreen.offscreenSize().height() - device->size().height()));
       
  5329 }
       
  5330 
       
  5331 void QOpenGLPaintEnginePrivate::flushDrawQueue()
       
  5332 {
       
  5333 #ifndef QT_OPENGL_ES
       
  5334     Q_Q(QOpenGLPaintEngine);
       
  5335 
       
  5336     offscreen.release();
       
  5337 
       
  5338     if (!drawQueue.isEmpty()) {
       
  5339         DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::flushDrawQueue():" << drawQueue.size() << "items";
       
  5340 
       
  5341         glPushMatrix();
       
  5342         glLoadIdentity();
       
  5343         qreal old_opacity = opacity;
       
  5344         QPointF old_brush_origin = brush_origin;
       
  5345         QPainter::CompositionMode old_composition_mode = composition_mode;
       
  5346         QTransform old_matrix = matrix;
       
  5347         QBrush old_brush = cbrush;
       
  5348 
       
  5349         bool hqaa_old = high_quality_antialiasing;
       
  5350 
       
  5351         high_quality_antialiasing = true;
       
  5352 
       
  5353         foreach (const QDrawQueueItem &item, drawQueue)
       
  5354             drawItem(item);
       
  5355 
       
  5356         opacity = old_opacity;
       
  5357         brush_origin = old_brush_origin;
       
  5358         q->updateCompositionMode(old_composition_mode);
       
  5359         matrix = old_matrix;
       
  5360         cbrush = old_brush;
       
  5361         brush_style = old_brush.style();
       
  5362 
       
  5363         high_quality_antialiasing = hqaa_old;
       
  5364 
       
  5365         setGLBrush(old_brush.color());
       
  5366         qt_glColor4ubv(brush_color);
       
  5367 
       
  5368         drawQueue.clear();
       
  5369 
       
  5370         glPopMatrix();
       
  5371     }
       
  5372 #endif
       
  5373 }
       
  5374 
       
  5375 void QOpenGLPaintEngine::clipEnabledChanged()
       
  5376 {
       
  5377     Q_D(QOpenGLPaintEngine);
       
  5378 
       
  5379     d->updateDepthClip();
       
  5380 }
       
  5381 
       
  5382 void QOpenGLPaintEngine::penChanged()
       
  5383 {
       
  5384     updatePen(state()->pen);
       
  5385 }
       
  5386 
       
  5387 void QOpenGLPaintEngine::brushChanged()
       
  5388 {
       
  5389     updateBrush(state()->brush, state()->brushOrigin);
       
  5390 }
       
  5391 
       
  5392 void QOpenGLPaintEngine::brushOriginChanged()
       
  5393 {
       
  5394     updateBrush(state()->brush, state()->brushOrigin);
       
  5395 }
       
  5396 
       
  5397 void QOpenGLPaintEngine::opacityChanged()
       
  5398 {
       
  5399     Q_D(QOpenGLPaintEngine);
       
  5400     QPainterState *s = state();
       
  5401     d->opacity = s->opacity;
       
  5402     updateBrush(s->brush, s->brushOrigin);
       
  5403     updatePen(s->pen);
       
  5404 }
       
  5405 
       
  5406 void QOpenGLPaintEngine::compositionModeChanged()
       
  5407 {
       
  5408     updateCompositionMode(state()->composition_mode);
       
  5409 }
       
  5410 
       
  5411 void QOpenGLPaintEngine::renderHintsChanged()
       
  5412 {
       
  5413     updateRenderHints(state()->renderHints);
       
  5414 }
       
  5415 
       
  5416 void QOpenGLPaintEngine::transformChanged()
       
  5417 {
       
  5418     updateMatrix(state()->matrix);
       
  5419 }
       
  5420 
       
  5421 static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
       
  5422 {
       
  5423     const qreal *points = path.points();
       
  5424     const QPainterPath::ElementType *types = path.elements();
       
  5425 
       
  5426     QPainterPath p;
       
  5427     if (types) {
       
  5428         int id = 0;
       
  5429         for (int i=0; i<path.elementCount(); ++i) {
       
  5430             switch(types[i]) {
       
  5431             case QPainterPath::MoveToElement:
       
  5432                 p.moveTo(QPointF(points[id], points[id+1]));
       
  5433                 id+=2;
       
  5434                 break;
       
  5435             case QPainterPath::LineToElement:
       
  5436                 p.lineTo(QPointF(points[id], points[id+1]));
       
  5437                 id+=2;
       
  5438                 break;
       
  5439             case QPainterPath::CurveToElement: {
       
  5440                 QPointF p1(points[id], points[id+1]);
       
  5441                 QPointF p2(points[id+2], points[id+3]);
       
  5442                 QPointF p3(points[id+4], points[id+5]);
       
  5443                 p.cubicTo(p1, p2, p3);
       
  5444                 id+=6;
       
  5445                 break;
       
  5446             }
       
  5447             case QPainterPath::CurveToDataElement:
       
  5448                 ;
       
  5449                 break;
       
  5450             }
       
  5451         }
       
  5452     } else {
       
  5453         p.moveTo(QPointF(points[0], points[1]));
       
  5454         int id = 2;
       
  5455         for (int i=1; i<path.elementCount(); ++i) {
       
  5456             p.lineTo(QPointF(points[id], points[id+1]));
       
  5457             id+=2;
       
  5458         }
       
  5459     }
       
  5460     if (path.hints() & QVectorPath::WindingFill)
       
  5461         p.setFillRule(Qt::WindingFill);
       
  5462 
       
  5463     return p;
       
  5464 }
       
  5465 
       
  5466 void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
       
  5467 {
       
  5468     Q_D(QOpenGLPaintEngine);
       
  5469 
       
  5470     if (brush.style() == Qt::NoBrush)
       
  5471         return;
       
  5472 
       
  5473     if (!d->use_fragment_programs && needsEmulation(brush.style())) {
       
  5474         QPainter *p = painter();
       
  5475         QBrush oldBrush = p->brush();
       
  5476         p->setBrush(brush);
       
  5477         qt_draw_helper(p->d_ptr.data(), painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
       
  5478         p->setBrush(oldBrush);
       
  5479         return;
       
  5480     }
       
  5481 
       
  5482     QBrush old_brush = state()->brush;
       
  5483     updateBrush(brush, state()->brushOrigin);
       
  5484 
       
  5485     const qreal *points = path.points();
       
  5486     const QPainterPath::ElementType *types = path.elements();
       
  5487     if (!types && path.shape() == QVectorPath::RectangleHint) {
       
  5488         QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
       
  5489         QPen old_pen = state()->pen;
       
  5490         updatePen(Qt::NoPen);
       
  5491         drawRects(&r, 1);
       
  5492         updatePen(old_pen);
       
  5493     } else {
       
  5494         d->fillPath(painterPathFromVectorPath(path));
       
  5495     }
       
  5496 
       
  5497     updateBrush(old_brush, state()->brushOrigin);
       
  5498 }
       
  5499 
       
  5500 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
       
  5501     return (elementCount == 5 // 5-point polygon, check for closed rect
       
  5502             && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
       
  5503             && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
       
  5504             && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
       
  5505             ) ||
       
  5506            (elementCount == 4 // 4-point polygon, check for unclosed rect
       
  5507             && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
       
  5508             && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
       
  5509             );
       
  5510 }
       
  5511 
       
  5512 void QOpenGLPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
       
  5513 {
       
  5514     const qreal *points = path.points();
       
  5515     const QPainterPath::ElementType *types = path.elements();
       
  5516     if (!types && path.shape() == QVectorPath::RectangleHint) {
       
  5517         QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
       
  5518         updateClipRegion(QRegion(r.toRect()), op);
       
  5519         return;
       
  5520     }
       
  5521 
       
  5522     QPainterPath p;
       
  5523     if (types) {
       
  5524         int id = 0;
       
  5525         for (int i=0; i<path.elementCount(); ++i) {
       
  5526             switch(types[i]) {
       
  5527             case QPainterPath::MoveToElement:
       
  5528                 p.moveTo(QPointF(points[id], points[id+1]));
       
  5529                 id+=2;
       
  5530                 break;
       
  5531             case QPainterPath::LineToElement:
       
  5532                 p.lineTo(QPointF(points[id], points[id+1]));
       
  5533                 id+=2;
       
  5534                 break;
       
  5535             case QPainterPath::CurveToElement: {
       
  5536                 QPointF p1(points[id], points[id+1]);
       
  5537                 QPointF p2(points[id+2], points[id+3]);
       
  5538                 QPointF p3(points[id+4], points[id+5]);
       
  5539                 p.cubicTo(p1, p2, p3);
       
  5540                 id+=6;
       
  5541                 break;
       
  5542             }
       
  5543             case QPainterPath::CurveToDataElement:
       
  5544                 ;
       
  5545                 break;
       
  5546             }
       
  5547         }
       
  5548     } else if (!path.isEmpty()) {
       
  5549         p.moveTo(QPointF(points[0], points[1]));
       
  5550         int id = 2;
       
  5551         for (int i=1; i<path.elementCount(); ++i) {
       
  5552             p.lineTo(QPointF(points[id], points[id+1]));
       
  5553             id+=2;
       
  5554         }
       
  5555     }
       
  5556     if (path.hints() & QVectorPath::WindingFill)
       
  5557         p.setFillRule(Qt::WindingFill);
       
  5558 
       
  5559     updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
       
  5560     return;
       
  5561 }
       
  5562 
       
  5563 void QOpenGLPaintEngine::setState(QPainterState *s)
       
  5564 {
       
  5565     Q_D(QOpenGLPaintEngine);
       
  5566     QOpenGLPaintEngineState *new_state = static_cast<QOpenGLPaintEngineState *>(s);
       
  5567     QOpenGLPaintEngineState *old_state = state();
       
  5568 
       
  5569     QPaintEngineEx::setState(s);
       
  5570 
       
  5571     // are we in a save() ?
       
  5572     if (s == d->last_created_state) {
       
  5573         d->last_created_state = 0;
       
  5574         return;
       
  5575     }
       
  5576 
       
  5577     if (isActive()) {
       
  5578         if (old_state->depthClipId != new_state->depthClipId)
       
  5579             d->updateDepthClip();
       
  5580         penChanged();
       
  5581         brushChanged();
       
  5582         opacityChanged();
       
  5583         compositionModeChanged();
       
  5584         renderHintsChanged();
       
  5585         transformChanged();
       
  5586     }
       
  5587 }
       
  5588 
       
  5589 QPainterState *QOpenGLPaintEngine::createState(QPainterState *orig) const
       
  5590 {
       
  5591     const Q_D(QOpenGLPaintEngine);
       
  5592 
       
  5593     QOpenGLPaintEngineState *s;
       
  5594     if (!orig)
       
  5595         s = new QOpenGLPaintEngineState();
       
  5596     else
       
  5597         s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
       
  5598 
       
  5599     d->last_created_state = s;
       
  5600     return s;
       
  5601 }
       
  5602 
       
  5603 //
       
  5604 //  QOpenGLPaintEngineState
       
  5605 //
       
  5606 
       
  5607 QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
       
  5608     : QPainterState(other)
       
  5609 {
       
  5610     clipRegion = other.clipRegion;
       
  5611     hasClipping = other.hasClipping;
       
  5612     fastClip = other.fastClip;
       
  5613     depthClipId = other.depthClipId;
       
  5614 }
       
  5615 
       
  5616 QOpenGLPaintEngineState::QOpenGLPaintEngineState()
       
  5617 {
       
  5618     hasClipping = false;
       
  5619     depthClipId = 0;
       
  5620 }
       
  5621 
       
  5622 QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
       
  5623 {
       
  5624 }
       
  5625 
       
  5626 void QOpenGLPaintEnginePrivate::ensureDrawableTexture()
       
  5627 {
       
  5628     if (!dirty_drawable_texture)
       
  5629         return;
       
  5630 
       
  5631     dirty_drawable_texture = false;
       
  5632 
       
  5633 #ifndef QT_OPENGL_ES
       
  5634     glGenTextures(1, &drawable_texture);
       
  5635     glBindTexture(GL_TEXTURE_2D, drawable_texture);
       
  5636 
       
  5637     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
       
  5638             drawable_texture_size.width(),
       
  5639             drawable_texture_size.height(), 0,
       
  5640             GL_RGBA, GL_UNSIGNED_BYTE, NULL);
       
  5641 
       
  5642     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
  5643     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
  5644     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
       
  5645     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
       
  5646 #endif
       
  5647 }
       
  5648 
       
  5649 QT_END_NAMESPACE
       
  5650 
       
  5651 #include "qpaintengine_opengl.moc"