src/opengl/qpaintengine_opengl.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 static inline void qt_glColor4ubv(unsigned char *col)
       
   112 {
       
   113 #ifdef QT_OPENGL_ES
       
   114         glColor4f(col[0]/255.0, col[1]/255.0, col[2]/255.0, col[3]/255.0);
       
   115 #else
       
   116         glColor4ubv(col);
       
   117 #endif
       
   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 (QGLExtensions::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 #ifndef QT_OPENGL_ES
       
  1292     if (!ctx->d_ptr->internal_context) {
       
  1293         glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);
       
  1294         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
       
  1295         glPushAttrib(GL_ALL_ATTRIB_BITS);
       
  1296 
       
  1297         glDisableClientState(GL_EDGE_FLAG_ARRAY);
       
  1298         glDisableClientState(GL_INDEX_ARRAY);
       
  1299         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
       
  1300         glDisable(GL_TEXTURE_1D);
       
  1301         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
       
  1302         glPixelTransferi(GL_MAP_COLOR, false);
       
  1303         glPixelTransferi(GL_MAP_STENCIL, false);
       
  1304         glDisable(GL_TEXTURE_GEN_S);
       
  1305 
       
  1306         glPixelStorei(GL_PACK_SWAP_BYTES, false);
       
  1307         glPixelStorei(GL_PACK_LSB_FIRST, false);
       
  1308         glPixelStorei(GL_PACK_ROW_LENGTH, 0);
       
  1309         glPixelStorei(GL_PACK_SKIP_ROWS, 0);
       
  1310         glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
       
  1311         glPixelStorei(GL_PACK_ALIGNMENT, 4);
       
  1312 
       
  1313         glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
       
  1314         glPixelStorei(GL_UNPACK_LSB_FIRST, false);
       
  1315         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
       
  1316         glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       
  1317         glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
       
  1318         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
       
  1319 
       
  1320         if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) {
       
  1321             glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
       
  1322             glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
       
  1323             glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
       
  1324             glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
       
  1325         }
       
  1326     }
       
  1327 #endif
       
  1328 
       
  1329     if (!ctx->d_ptr->internal_context) {
       
  1330         glMatrixMode(GL_MODELVIEW);
       
  1331         glPushMatrix();
       
  1332         glMatrixMode(GL_TEXTURE);
       
  1333         glPushMatrix();
       
  1334         glLoadIdentity();
       
  1335         glDisableClientState(GL_COLOR_ARRAY);
       
  1336         glDisableClientState(GL_NORMAL_ARRAY);
       
  1337         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  1338         glDisableClientState(GL_VERTEX_ARRAY);
       
  1339 
       
  1340         if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
       
  1341             glDisable(GL_MULTISAMPLE);
       
  1342         glDisable(GL_TEXTURE_2D);
       
  1343         if (QGLExtensions::glExtensions & QGLExtensions::TextureRectangle)
       
  1344             glDisable(GL_TEXTURE_RECTANGLE_NV);
       
  1345         glDisable(GL_STENCIL_TEST);
       
  1346         glDisable(GL_CULL_FACE);
       
  1347         glDisable(GL_LIGHTING);
       
  1348         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  1349     }
       
  1350 
       
  1351     d->offscreen.begin();
       
  1352 
       
  1353     glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface...
       
  1354     glMatrixMode(GL_PROJECTION);
       
  1355     glLoadIdentity();
       
  1356 #ifdef QT_OPENGL_ES
       
  1357     glOrthof(0, sz.width(), sz.height(), 0, -999999, 999999);
       
  1358 #else
       
  1359     glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
       
  1360 #endif
       
  1361     glMatrixMode(GL_MODELVIEW);
       
  1362     glLoadIdentity();
       
  1363     glEnable(GL_BLEND);
       
  1364     d->composition_mode = QPainter::CompositionMode_SourceOver;
       
  1365 
       
  1366 #ifdef QT_OPENGL_ES
       
  1367     d->max_texture_size = ctx->d_func()->maxTextureSize();
       
  1368 #else
       
  1369     bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx);
       
  1370 
       
  1371     if (shared_ctx) {
       
  1372         d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize();
       
  1373     } else {
       
  1374         d->max_texture_size = ctx->d_func()->maxTextureSize();
       
  1375 
       
  1376         if (d->shader_ctx) {
       
  1377             d->shader_ctx->makeCurrent();
       
  1378             glBindTexture(GL_TEXTURE_1D, 0);
       
  1379             glDeleteTextures(1, &d->grad_palette);
       
  1380 
       
  1381             if (has_frag_program && d->use_fragment_programs)
       
  1382                 glDeleteTextures(1, &d->drawable_texture);
       
  1383             ctx->makeCurrent();
       
  1384         }
       
  1385         d->shader_ctx = d->device->context();
       
  1386         glGenTextures(1, &d->grad_palette);
       
  1387 
       
  1388         qt_mask_texture_cache()->clearCache();
       
  1389         d->use_fragment_programs = has_frag_program;
       
  1390     }
       
  1391 
       
  1392     if (d->use_fragment_programs && (!shared_ctx || sz.width() > d->drawable_texture_size.width()
       
  1393                                      || sz.height() > d->drawable_texture_size.height()))
       
  1394     {
       
  1395         // delete old texture if size has increased, otherwise it was deleted earlier
       
  1396         if (shared_ctx)
       
  1397             glDeleteTextures(1, &d->drawable_texture);
       
  1398 
       
  1399         d->dirty_drawable_texture = true;
       
  1400         d->drawable_texture_size = QSize(qt_next_power_of_two(sz.width()),
       
  1401                                          qt_next_power_of_two(sz.height()));
       
  1402     }
       
  1403 #endif
       
  1404 
       
  1405     updateClipRegion(QRegion(), Qt::NoClip);
       
  1406     penChanged();
       
  1407     brushChanged();
       
  1408     opacityChanged();
       
  1409     compositionModeChanged();
       
  1410     renderHintsChanged();
       
  1411     transformChanged();
       
  1412     return true;
       
  1413 }
       
  1414 
       
  1415 bool QOpenGLPaintEngine::end()
       
  1416 {
       
  1417     Q_D(QOpenGLPaintEngine);
       
  1418     d->flushDrawQueue();
       
  1419     d->offscreen.end();
       
  1420     QGLContext *ctx = const_cast<QGLContext *>(d->device->context());
       
  1421     if (!ctx->d_ptr->internal_context) {
       
  1422         glMatrixMode(GL_TEXTURE);
       
  1423         glPopMatrix();
       
  1424         glMatrixMode(GL_MODELVIEW);
       
  1425         glPopMatrix();
       
  1426     }
       
  1427 #ifndef QT_OPENGL_ES
       
  1428     if (ctx->d_ptr->internal_context) {
       
  1429         glDisable(GL_SCISSOR_TEST);
       
  1430     } else {
       
  1431         glMatrixMode(GL_PROJECTION);
       
  1432         glLoadMatrixd(&d->projection_matrix[0][0]);
       
  1433         glPopAttrib();
       
  1434         glPopClientAttrib();
       
  1435     }
       
  1436 #endif
       
  1437     d->device->endPaint();
       
  1438     qt_mask_texture_cache()->maintainCache();
       
  1439 
       
  1440     return true;
       
  1441 }
       
  1442 
       
  1443 void QOpenGLPaintEngine::updateState(const QPaintEngineState &state)
       
  1444 {
       
  1445     Q_D(QOpenGLPaintEngine);
       
  1446     QPaintEngine::DirtyFlags flags = state.state();
       
  1447 
       
  1448     bool update_fast_pen = false;
       
  1449 
       
  1450     if (flags & DirtyOpacity) {
       
  1451         update_fast_pen = true;
       
  1452         d->opacity = state.opacity();
       
  1453         if (d->opacity > 1.0f)
       
  1454             d->opacity = 1.0f;
       
  1455         if (d->opacity < 0.f)
       
  1456             d->opacity = 0.f;
       
  1457         // force update
       
  1458         flags |= DirtyPen;
       
  1459         flags |= DirtyBrush;
       
  1460     }
       
  1461 
       
  1462     if (flags & DirtyTransform) {
       
  1463         update_fast_pen = true;
       
  1464         updateMatrix(state.transform());
       
  1465         // brush setup depends on transform state
       
  1466         if (state.brush().style() != Qt::NoBrush)
       
  1467             flags |= DirtyBrush;
       
  1468     }
       
  1469 
       
  1470     if (flags & DirtyPen) {
       
  1471         update_fast_pen = true;
       
  1472         updatePen(state.pen());
       
  1473     }
       
  1474 
       
  1475     if (flags & (DirtyBrush | DirtyBrushOrigin)) {
       
  1476         updateBrush(state.brush(), state.brushOrigin());
       
  1477     }
       
  1478 
       
  1479     if (flags & DirtyFont) {
       
  1480         updateFont(state.font());
       
  1481     }
       
  1482 
       
  1483     if (state.state() & DirtyClipEnabled) {
       
  1484         if (state.isClipEnabled())
       
  1485             updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip);
       
  1486         else
       
  1487             updateClipRegion(QRegion(), Qt::NoClip);
       
  1488     }
       
  1489 
       
  1490     if (flags & DirtyClipPath) {
       
  1491         updateClipRegion(QRegion(state.clipPath().toFillPolygon().toPolygon(),
       
  1492                                  state.clipPath().fillRule()),
       
  1493                          state.clipOperation());
       
  1494     }
       
  1495 
       
  1496     if (flags & DirtyClipRegion) {
       
  1497         updateClipRegion(state.clipRegion(), state.clipOperation());
       
  1498     }
       
  1499 
       
  1500     if (flags & DirtyHints) {
       
  1501         updateRenderHints(state.renderHints());
       
  1502     }
       
  1503 
       
  1504     if (flags & DirtyCompositionMode) {
       
  1505         updateCompositionMode(state.compositionMode());
       
  1506     }
       
  1507 
       
  1508     if (update_fast_pen) {
       
  1509         Q_D(QOpenGLPaintEngine);
       
  1510         qreal pen_width = d->cpen.widthF();
       
  1511         d->has_fast_pen =
       
  1512             ((pen_width == 0 || (pen_width <= 1 && d->txop <= QTransform::TxTranslate))
       
  1513              || d->cpen.isCosmetic())
       
  1514             && d->cpen.style() == Qt::SolidLine
       
  1515             && d->cpen.isSolid();
       
  1516     }
       
  1517 }
       
  1518 
       
  1519 
       
  1520 void QOpenGLPaintEnginePrivate::setInvMatrixData(const QTransform &inv_matrix)
       
  1521 {
       
  1522     inv_matrix_data[0][0] = inv_matrix.m11();
       
  1523     inv_matrix_data[1][0] = inv_matrix.m21();
       
  1524     inv_matrix_data[2][0] = inv_matrix.m31();
       
  1525 
       
  1526     inv_matrix_data[0][1] = inv_matrix.m12();
       
  1527     inv_matrix_data[1][1] = inv_matrix.m22();
       
  1528     inv_matrix_data[2][1] = inv_matrix.m32();
       
  1529 
       
  1530     inv_matrix_data[0][2] = inv_matrix.m13();
       
  1531     inv_matrix_data[1][2] = inv_matrix.m23();
       
  1532     inv_matrix_data[2][2] = inv_matrix.m33();
       
  1533 }
       
  1534 
       
  1535 
       
  1536 void QOpenGLPaintEnginePrivate::updateGradient(const QBrush &brush, const QRectF &)
       
  1537 {
       
  1538 #ifdef QT_OPENGL_ES
       
  1539     Q_UNUSED(brush);
       
  1540 #else
       
  1541     bool has_mirrored_repeat = QGLExtensions::glExtensions & QGLExtensions::MirroredRepeat;
       
  1542     Qt::BrushStyle style = brush.style();
       
  1543 
       
  1544     QTransform m = brush.transform();
       
  1545 
       
  1546     if (has_mirrored_repeat && style == Qt::LinearGradientPattern) {
       
  1547         const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
       
  1548         QTransform m = brush.transform();
       
  1549         QPointF realStart = g->start();
       
  1550         QPointF realFinal = g->finalStop();
       
  1551         QPointF start = m.map(realStart);
       
  1552         QPointF stop;
       
  1553 
       
  1554         if (qFuzzyCompare(m.m11(), m.m22()) && m.m12() == 0.0 && m.m21() == 0.0) {
       
  1555             // It is a simple uniform scale and/or translation
       
  1556             stop = m.map(realFinal);
       
  1557         } else {
       
  1558             // It is not enough to just transform the endpoints.
       
  1559             // We have to make sure the _pattern_ is transformed correctly.
       
  1560 
       
  1561             qreal odx = realFinal.x() - realStart.x();
       
  1562             qreal ody = realFinal.y() - realStart.y();
       
  1563 
       
  1564             // nx, ny and dx, dy are normal and gradient direction after transform:
       
  1565             qreal nx = m.m11()*ody - m.m21()*odx;
       
  1566             qreal ny = m.m12()*ody - m.m22()*odx;
       
  1567 
       
  1568             qreal dx = m.m11()*odx + m.m21()*ody;
       
  1569             qreal dy = m.m12()*odx + m.m22()*ody;
       
  1570 
       
  1571             qreal lx = 1 / (dx - dy*nx/ny);
       
  1572             qreal ly = 1 / (dy - dx*ny/nx);
       
  1573             qreal l = 1 / qSqrt(lx*lx+ly*ly);
       
  1574 
       
  1575             stop = start + QPointF(-ny, nx) * l/qSqrt(nx*nx+ny*ny);
       
  1576         }
       
  1577 
       
  1578         float tr[4], f;
       
  1579         tr[0] = stop.x() - start.x();
       
  1580         tr[1] = stop.y() - start.y();
       
  1581         f = 1.0 / (tr[0]*tr[0] + tr[1]*tr[1]);
       
  1582         tr[0] *= f;
       
  1583         tr[1] *= f;
       
  1584         tr[2] = 0;
       
  1585         tr[3] = -(start.x()*tr[0] + start.y()*tr[1]);
       
  1586         brush_color[0] = brush_color[1] = brush_color[2] = brush_color[3] = 255;
       
  1587         qt_glColor4ubv(brush_color);
       
  1588         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
       
  1589         glTexGenfv(GL_S, GL_OBJECT_PLANE, tr);
       
  1590     }
       
  1591 
       
  1592     if (use_fragment_programs) {
       
  1593         if (style == Qt::RadialGradientPattern) {
       
  1594             const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
       
  1595             QPointF realCenter = g->center();
       
  1596             QPointF realFocal  = g->focalPoint();
       
  1597             qreal   realRadius = g->radius();
       
  1598             QTransform translate(1, 0, 0, 1, -realFocal.x(), -realFocal.y());
       
  1599             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1600             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1601             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1602 
       
  1603             setInvMatrixData(inv_matrix);
       
  1604 
       
  1605             fmp_data[0] = realCenter.x() - realFocal.x();
       
  1606             fmp_data[1] = realCenter.y() - realFocal.y();
       
  1607 
       
  1608             fmp2_m_radius2_data[0] = -fmp_data[0] * fmp_data[0] - fmp_data[1] * fmp_data[1] + realRadius * realRadius;
       
  1609         } else if (style == Qt::ConicalGradientPattern) {
       
  1610             const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
       
  1611             QPointF realCenter = g->center();
       
  1612             QTransform translate(1, 0, 0, 1, -realCenter.x(), -realCenter.y());
       
  1613             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1614             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1615             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1616 
       
  1617             setInvMatrixData(inv_matrix);
       
  1618 
       
  1619             angle_data[0] = -(g->angle() * 2 * Q_PI) / 360.0;
       
  1620         } else if (style == Qt::LinearGradientPattern) {
       
  1621             const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
       
  1622 
       
  1623             QPointF realStart = g->start();
       
  1624             QPointF realFinal = g->finalStop();
       
  1625             QTransform translate(1, 0, 0, 1, -realStart.x(), -realStart.y());
       
  1626             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1627             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1628             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted() * translate;
       
  1629 
       
  1630             setInvMatrixData(inv_matrix);
       
  1631 
       
  1632             QPointF l = realFinal - realStart;
       
  1633 
       
  1634             linear_data[0] = l.x();
       
  1635             linear_data[1] = l.y();
       
  1636 
       
  1637             linear_data[2] = 1.0f / (l.x() * l.x() + l.y() * l.y());
       
  1638         } else if (style != Qt::SolidPattern) {
       
  1639             QTransform gl_to_qt(1, 0, 0, -1, 0, pdev->height());
       
  1640             QTransform m = QTransform(matrix).translate(brush_origin.x(), brush_origin.y());
       
  1641             QTransform inv_matrix = gl_to_qt * (brush.transform() * m).inverted();
       
  1642 
       
  1643             setInvMatrixData(inv_matrix);
       
  1644         }
       
  1645     }
       
  1646 
       
  1647     if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
       
  1648         createGradientPaletteTexture(*brush.gradient());
       
  1649     }
       
  1650 #endif
       
  1651 }
       
  1652 
       
  1653 
       
  1654 class QOpenGLTessellator : public QTessellator
       
  1655 {
       
  1656 public:
       
  1657     QOpenGLTessellator() {}
       
  1658     ~QOpenGLTessellator() { }
       
  1659     QGLTrapezoid toGLTrapezoid(const Trapezoid &trap);
       
  1660 };
       
  1661 
       
  1662 QGLTrapezoid QOpenGLTessellator::toGLTrapezoid(const Trapezoid &trap)
       
  1663 {
       
  1664     QGLTrapezoid t;
       
  1665 
       
  1666     t.top = Q27Dot5ToDouble(trap.top);
       
  1667     t.bottom = Q27Dot5ToDouble(trap.bottom);
       
  1668 
       
  1669     Q27Dot5 y = trap.topLeft->y - trap.bottomLeft->y;
       
  1670 
       
  1671     qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y);
       
  1672 
       
  1673     qreal tx = Q27Dot5ToDouble(trap.topLeft->x);
       
  1674     qreal m = (-tx + Q27Dot5ToDouble(trap.bottomLeft->x)) / Q27Dot5ToDouble(y);
       
  1675     t.topLeftX = tx + m * (topLeftY - t.top);
       
  1676     t.bottomLeftX = tx + m * (topLeftY - t.bottom);
       
  1677 
       
  1678     y = trap.topRight->y - trap.bottomRight->y;
       
  1679 
       
  1680     qreal topRightY = Q27Dot5ToDouble(trap.topRight->y);
       
  1681 
       
  1682     tx = Q27Dot5ToDouble(trap.topRight->x);
       
  1683     m = (-tx + Q27Dot5ToDouble(trap.bottomRight->x)) / Q27Dot5ToDouble(y);
       
  1684 
       
  1685     t.topRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.top));
       
  1686     t.bottomRightX = tx + m * (topRightY - Q27Dot5ToDouble(trap.bottom));
       
  1687 
       
  1688     return t;
       
  1689 }
       
  1690 
       
  1691 class QOpenGLImmediateModeTessellator : public QOpenGLTessellator
       
  1692 {
       
  1693 public:
       
  1694     void addTrap(const Trapezoid &trap);
       
  1695     void tessellate(const QPointF *points, int nPoints, bool winding) {
       
  1696         trapezoids.reserve(trapezoids.size() + nPoints);
       
  1697         setWinding(winding);
       
  1698         QTessellator::tessellate(points, nPoints);
       
  1699     }
       
  1700 
       
  1701     QVector<QGLTrapezoid> trapezoids;
       
  1702 };
       
  1703 
       
  1704 void QOpenGLImmediateModeTessellator::addTrap(const Trapezoid &trap)
       
  1705 {
       
  1706     trapezoids.append(toGLTrapezoid(trap));
       
  1707 }
       
  1708 
       
  1709 #ifndef QT_OPENGL_ES
       
  1710 static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, QGLContext *ctx)
       
  1711 {
       
  1712     qreal minX = qMin(trap.topLeftX, trap.bottomLeftX);
       
  1713     qreal maxX = qMax(trap.topRightX, trap.bottomRightX);
       
  1714 
       
  1715     if (qFuzzyCompare(trap.top, trap.bottom) || qFuzzyCompare(minX, maxX) ||
       
  1716         (qFuzzyCompare(trap.topLeftX, trap.topRightX) && qFuzzyCompare(trap.bottomLeftX, trap.bottomRightX)))
       
  1717         return;
       
  1718 
       
  1719     const qreal xpadding = 1.0;
       
  1720     const qreal ypadding = 1.0;
       
  1721 
       
  1722     qreal topDist = offscreenHeight - trap.top;
       
  1723     qreal bottomDist = offscreenHeight - trap.bottom;
       
  1724 
       
  1725     qreal reciprocal = bottomDist / (bottomDist - topDist);
       
  1726 
       
  1727     qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal;
       
  1728     qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal;
       
  1729 
       
  1730     const bool topZero = qFuzzyIsNull(topDist);
       
  1731 
       
  1732     reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist;
       
  1733 
       
  1734     qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal;
       
  1735     qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal;
       
  1736 
       
  1737     qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA;
       
  1738     qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA;
       
  1739 
       
  1740     // fragment program needs the negative of invRightA as it mirrors the line
       
  1741     glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA);
       
  1742     glMultiTexCoord4f(GL_TEXTURE1, leftA, leftB, rightA, rightB);
       
  1743 
       
  1744     qreal topY = trap.top - ypadding;
       
  1745     qreal bottomY = trap.bottom + ypadding;
       
  1746 
       
  1747     qreal bounds_bottomLeftX = leftA * (offscreenHeight - bottomY) + leftB;
       
  1748     qreal bounds_bottomRightX = rightA * (offscreenHeight - bottomY) + rightB;
       
  1749     qreal bounds_topLeftX = leftA * (offscreenHeight - topY) + leftB;
       
  1750     qreal bounds_topRightX = rightA * (offscreenHeight - topY) + rightB;
       
  1751 
       
  1752     QPointF leftNormal(1, -leftA);
       
  1753     leftNormal /= qSqrt(leftNormal.x() * leftNormal.x() + leftNormal.y() * leftNormal.y());
       
  1754     QPointF rightNormal(1, -rightA);
       
  1755     rightNormal /= qSqrt(rightNormal.x() * rightNormal.x() + rightNormal.y() * rightNormal.y());
       
  1756 
       
  1757     qreal left_padding = xpadding / qAbs(leftNormal.x());
       
  1758     qreal right_padding = xpadding / qAbs(rightNormal.x());
       
  1759 
       
  1760     glVertex2d(bounds_topLeftX - left_padding, topY);
       
  1761     glVertex2d(bounds_topRightX + right_padding, topY);
       
  1762     glVertex2d(bounds_bottomRightX + right_padding, bottomY);
       
  1763     glVertex2d(bounds_bottomLeftX - left_padding, bottomY);
       
  1764 
       
  1765     glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
       
  1766 }
       
  1767 #endif // !Q_WS_QWS
       
  1768 
       
  1769 class QOpenGLTrapezoidToArrayTessellator : public QOpenGLTessellator
       
  1770 {
       
  1771 public:
       
  1772     QOpenGLTrapezoidToArrayTessellator() : vertices(0), allocated(0), size(0) {}
       
  1773     ~QOpenGLTrapezoidToArrayTessellator() { free(vertices); }
       
  1774     q_vertexType *vertices;
       
  1775     int allocated;
       
  1776     int size;
       
  1777     QRectF bounds;
       
  1778     void addTrap(const Trapezoid &trap);
       
  1779     void tessellate(const QPointF *points, int nPoints, bool winding) {
       
  1780         size = 0;
       
  1781         setWinding(winding);
       
  1782         bounds = QTessellator::tessellate(points, nPoints);
       
  1783     }
       
  1784 };
       
  1785 
       
  1786 void QOpenGLTrapezoidToArrayTessellator::addTrap(const Trapezoid &trap)
       
  1787 {
       
  1788     // On OpenGL ES we convert the trap to 2 triangles
       
  1789 #ifndef QT_OPENGL_ES
       
  1790     if (size > allocated - 8) {
       
  1791 #else
       
  1792     if (size > allocated - 12) {
       
  1793 #endif
       
  1794         allocated = qMax(2*allocated, 512);
       
  1795         vertices = (q_vertexType *)realloc(vertices, allocated * sizeof(q_vertexType));
       
  1796     }
       
  1797 
       
  1798     QGLTrapezoid t = toGLTrapezoid(trap);
       
  1799 
       
  1800 #ifndef QT_OPENGL_ES
       
  1801     vertices[size++] = f2vt(t.topLeftX);
       
  1802     vertices[size++] = f2vt(t.top);
       
  1803     vertices[size++] = f2vt(t.topRightX);
       
  1804     vertices[size++] = f2vt(t.top);
       
  1805     vertices[size++] = f2vt(t.bottomRightX);
       
  1806     vertices[size++] = f2vt(t.bottom);
       
  1807     vertices[size++] = f2vt(t.bottomLeftX);
       
  1808     vertices[size++] = f2vt(t.bottom);
       
  1809 #else
       
  1810     // First triangle
       
  1811     vertices[size++] = f2vt(t.topLeftX);
       
  1812     vertices[size++] = f2vt(t.top);
       
  1813     vertices[size++] = f2vt(t.topRightX);
       
  1814     vertices[size++] = f2vt(t.top);
       
  1815     vertices[size++] = f2vt(t.bottomRightX);
       
  1816     vertices[size++] = f2vt(t.bottom);
       
  1817 
       
  1818     // Second triangle
       
  1819     vertices[size++] = f2vt(t.bottomLeftX);
       
  1820     vertices[size++] = f2vt(t.bottom);
       
  1821     vertices[size++] = f2vt(t.topLeftX);
       
  1822     vertices[size++] = f2vt(t.top);
       
  1823     vertices[size++] = f2vt(t.bottomRightX);
       
  1824     vertices[size++] = f2vt(t.bottom);
       
  1825 #endif
       
  1826 }
       
  1827 
       
  1828 
       
  1829 void QOpenGLPaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
       
  1830                                                 Qt::FillRule fill)
       
  1831 {
       
  1832     QOpenGLTrapezoidToArrayTessellator tessellator;
       
  1833     tessellator.tessellate(polygonPoints, pointCount, fill == Qt::WindingFill);
       
  1834 
       
  1835     DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon with" << pointCount << "points using fillPolygon_dev";
       
  1836 
       
  1837     setGradientOps(cbrush, tessellator.bounds);
       
  1838 
       
  1839     bool fast_style = current_style == Qt::LinearGradientPattern
       
  1840                       || current_style == Qt::SolidPattern;
       
  1841 
       
  1842 #ifndef QT_OPENGL_ES
       
  1843     GLenum geometry_mode = GL_QUADS;
       
  1844 #else
       
  1845     GLenum geometry_mode = GL_TRIANGLES;
       
  1846 #endif
       
  1847 
       
  1848     if (use_fragment_programs && !(fast_style && has_fast_composition_mode)) {
       
  1849         composite(geometry_mode, tessellator.vertices, tessellator.size / 2);
       
  1850     } else {
       
  1851         glVertexPointer(2, q_vertexTypeEnum, 0, tessellator.vertices);
       
  1852         glEnableClientState(GL_VERTEX_ARRAY);
       
  1853         glDrawArrays(geometry_mode, 0, tessellator.size/2);
       
  1854         glDisableClientState(GL_VERTEX_ARRAY);
       
  1855     }
       
  1856 }
       
  1857 
       
  1858 
       
  1859 inline void QOpenGLPaintEnginePrivate::lineToStencil(qreal x, qreal y)
       
  1860 {
       
  1861     tess_points.add(QPointF(x, y));
       
  1862 
       
  1863     if (x > max_x)
       
  1864         max_x = x;
       
  1865     else if (x < min_x)
       
  1866         min_x = x;
       
  1867     if (y > max_y)
       
  1868         max_y = y;
       
  1869     else if (y < min_y)
       
  1870         min_y = y;
       
  1871 }
       
  1872 
       
  1873 inline void QOpenGLPaintEnginePrivate::curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
       
  1874 {
       
  1875     qreal inverseScaleHalf = inverseScale / 2;
       
  1876 
       
  1877     QBezier beziers[32];
       
  1878     beziers[0] = QBezier::fromPoints(tess_points.last(), cp1, cp2, ep);
       
  1879     QBezier *b = beziers;
       
  1880     while (b >= beziers) {
       
  1881         // check if we can pop the top bezier curve from the stack
       
  1882         qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  1883         qreal d;
       
  1884         if (l > inverseScale) {
       
  1885             d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  1886                 + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  1887             d /= l;
       
  1888         } else {
       
  1889             d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  1890                 qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  1891         }
       
  1892         if (d < inverseScaleHalf || b == beziers + 31) {
       
  1893             // good enough, we pop it off and add the endpoint
       
  1894             lineToStencil(b->x4, b->y4);
       
  1895             --b;
       
  1896         } else {
       
  1897             // split, second half of the polygon goes lower into the stack
       
  1898             b->split(b+1, b);
       
  1899            ++b;
       
  1900         }
       
  1901     }
       
  1902 }
       
  1903 
       
  1904 
       
  1905 void QOpenGLPaintEnginePrivate::pathToVertexArrays(const QPainterPath &path)
       
  1906 {
       
  1907     const QPainterPath::Element &first = path.elementAt(0);
       
  1908     min_x = max_x = first.x;
       
  1909     min_y = max_y = first.y;
       
  1910 
       
  1911     tess_points.reset();
       
  1912     tess_points_stops.clear();
       
  1913     lineToStencil(first.x, first.y);
       
  1914 
       
  1915     for (int i=1; i<path.elementCount(); ++i) {
       
  1916         const QPainterPath::Element &e = path.elementAt(i);
       
  1917         switch (e.type) {
       
  1918         case QPainterPath::MoveToElement:
       
  1919             tess_points_stops.append(tess_points.size());
       
  1920             lineToStencil(e.x, e.y);
       
  1921             break;
       
  1922         case QPainterPath::LineToElement:
       
  1923             lineToStencil(e.x, e.y);
       
  1924             break;
       
  1925         case QPainterPath::CurveToElement:
       
  1926             curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2));
       
  1927             i+=2;
       
  1928             break;
       
  1929         default:
       
  1930             break;
       
  1931         }
       
  1932     }
       
  1933     lineToStencil(first.x, first.y);
       
  1934     tess_points_stops.append(tess_points.size());
       
  1935 }
       
  1936 
       
  1937 
       
  1938 void QOpenGLPaintEnginePrivate::drawVertexArrays()
       
  1939 {
       
  1940     glEnableClientState(GL_VERTEX_ARRAY);
       
  1941     glVertexPointer(2, GL_DOUBLE, 0, tess_points.data());
       
  1942     int previous_stop = 0;
       
  1943     foreach(int stop, tess_points_stops) {
       
  1944         glDrawArrays(GL_TRIANGLE_FAN, previous_stop, stop-previous_stop);
       
  1945         previous_stop = stop;
       
  1946     }
       
  1947     glDisableClientState(GL_VERTEX_ARRAY);
       
  1948 }
       
  1949 
       
  1950 void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule)
       
  1951 {
       
  1952     Q_Q(QOpenGLPaintEngine);
       
  1953 
       
  1954     QRect rect = dirty_stencil.boundingRect();
       
  1955 
       
  1956     if (use_system_clip)
       
  1957         rect = q->systemClip().intersected(dirty_stencil).boundingRect();
       
  1958 
       
  1959     glStencilMask(~0);
       
  1960 
       
  1961     if (!rect.isEmpty()) {
       
  1962         disableClipping();
       
  1963 
       
  1964         glEnable(GL_SCISSOR_TEST);
       
  1965 
       
  1966         const int left = rect.left();
       
  1967         const int width = rect.width();
       
  1968         const int bottom = device->size().height() - (rect.bottom() + 1);
       
  1969         const int height = rect.height();
       
  1970 
       
  1971         glScissor(left, bottom, width, height);
       
  1972 
       
  1973         glClearStencil(0);
       
  1974         glClear(GL_STENCIL_BUFFER_BIT);
       
  1975         dirty_stencil -= rect;
       
  1976 
       
  1977         glDisable(GL_SCISSOR_TEST);
       
  1978 
       
  1979         enableClipping();
       
  1980     }
       
  1981 
       
  1982     // Enable stencil.
       
  1983     glEnable(GL_STENCIL_TEST);
       
  1984 
       
  1985     // Disable color writes.
       
  1986     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
       
  1987 
       
  1988     GLuint stencilMask = 0;
       
  1989 
       
  1990     if (fillRule == Qt::OddEvenFill) {
       
  1991         stencilMask = 1;
       
  1992 
       
  1993         // Enable stencil writes.
       
  1994         glStencilMask(stencilMask);
       
  1995 
       
  1996         // Set stencil xor mode.
       
  1997         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
       
  1998 
       
  1999         // Disable stencil func.
       
  2000         glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2001 
       
  2002         drawVertexArrays();
       
  2003     } else if (fillRule == Qt::WindingFill) {
       
  2004         stencilMask = ~0;
       
  2005 
       
  2006         if (has_stencil_face_ext) {
       
  2007             QGL_FUNC_CONTEXT;
       
  2008             glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
       
  2009 
       
  2010             glActiveStencilFaceEXT(GL_BACK);
       
  2011             glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
       
  2012             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2013 
       
  2014             glActiveStencilFaceEXT(GL_FRONT);
       
  2015             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
       
  2016             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2017 
       
  2018             drawVertexArrays();
       
  2019 
       
  2020             glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
       
  2021         } else {
       
  2022             glStencilFunc(GL_ALWAYS, 0, ~0);
       
  2023             glEnable(GL_CULL_FACE);
       
  2024 
       
  2025             glCullFace(GL_BACK);
       
  2026             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
       
  2027             drawVertexArrays();
       
  2028 
       
  2029             glCullFace(GL_FRONT);
       
  2030             glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
       
  2031             drawVertexArrays();
       
  2032 
       
  2033             glDisable(GL_CULL_FACE);
       
  2034         }
       
  2035     }
       
  2036 
       
  2037     // Enable color writes.
       
  2038     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2039     glStencilMask(stencilMask);
       
  2040 
       
  2041     setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y)));
       
  2042 
       
  2043     bool fast_fill = has_fast_composition_mode && (current_style == Qt::LinearGradientPattern || current_style == Qt::SolidPattern);
       
  2044 
       
  2045     if (use_fragment_programs && !fast_fill) {
       
  2046         DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (fragment programs)";
       
  2047         QRectF rect(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y));
       
  2048 
       
  2049         // Enable stencil func.
       
  2050         glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
       
  2051         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
       
  2052         composite(rect);
       
  2053     } else {
       
  2054         DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)";
       
  2055 
       
  2056         // Enable stencil func.
       
  2057         glStencilFunc(GL_NOTEQUAL, 0, stencilMask);
       
  2058         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
       
  2059 #ifndef QT_OPENGL_ES
       
  2060         glBegin(GL_QUADS);
       
  2061         glVertex2f(min_x, min_y);
       
  2062         glVertex2f(max_x, min_y);
       
  2063         glVertex2f(max_x, max_y);
       
  2064         glVertex2f(min_x, max_y);
       
  2065         glEnd();
       
  2066 #endif
       
  2067     }
       
  2068 
       
  2069     // Disable stencil writes.
       
  2070     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
       
  2071     glStencilMask(0);
       
  2072     glDisable(GL_STENCIL_TEST);
       
  2073 }
       
  2074 
       
  2075 void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
       
  2076 {
       
  2077     if (path.isEmpty())
       
  2078         return;
       
  2079 
       
  2080     if (use_stencil_method && !high_quality_antialiasing) {
       
  2081         pathToVertexArrays(path);
       
  2082         fillVertexArray(path.fillRule());
       
  2083         return;
       
  2084     }
       
  2085 
       
  2086     glMatrixMode(GL_MODELVIEW);
       
  2087     glLoadIdentity();
       
  2088 
       
  2089     if (high_quality_antialiasing)
       
  2090         drawOffscreenPath(path);
       
  2091     else {
       
  2092         QPolygonF poly = path.toFillPolygon(matrix);
       
  2093         fillPolygon_dev(poly.data(), poly.count(),
       
  2094                         path.fillRule());
       
  2095     }
       
  2096 
       
  2097     updateGLMatrix();
       
  2098 }
       
  2099 
       
  2100 
       
  2101 static inline bool needsEmulation(Qt::BrushStyle style)
       
  2102 {
       
  2103     return !(style == Qt::SolidPattern
       
  2104              || (style == Qt::LinearGradientPattern
       
  2105                  && (QGLExtensions::glExtensions & QGLExtensions::MirroredRepeat)));
       
  2106 }
       
  2107 
       
  2108 void QOpenGLPaintEnginePrivate::updateUseEmulation()
       
  2109 {
       
  2110     use_emulation = !use_fragment_programs
       
  2111                     && ((has_pen && needsEmulation(pen_brush_style))
       
  2112                         || (has_brush && needsEmulation(brush_style)));
       
  2113 }
       
  2114 
       
  2115 void QOpenGLPaintEngine::updatePen(const QPen &pen)
       
  2116 {
       
  2117     Q_D(QOpenGLPaintEngine);
       
  2118     Qt::PenStyle pen_style = pen.style();
       
  2119     d->pen_brush_style = pen.brush().style();
       
  2120     d->cpen = pen;
       
  2121     d->has_pen = (pen_style != Qt::NoPen) && (d->pen_brush_style != Qt::NoBrush);
       
  2122     d->updateUseEmulation();
       
  2123 
       
  2124     if (pen.isCosmetic()) {
       
  2125         GLfloat width = pen.widthF() == 0.0f ? 1.0f : pen.widthF();
       
  2126         glLineWidth(width);
       
  2127         glPointSize(width);
       
  2128     }
       
  2129 
       
  2130     if (d->pen_brush_style >= Qt::LinearGradientPattern
       
  2131         && d->pen_brush_style <= Qt::ConicalGradientPattern)
       
  2132     {
       
  2133         d->setGLPen(Qt::white);
       
  2134     } else {
       
  2135         d->setGLPen(pen.color());
       
  2136     }
       
  2137 
       
  2138     d->updateFastPen();
       
  2139 }
       
  2140 
       
  2141 void QOpenGLPaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
       
  2142 {
       
  2143     Q_D(QOpenGLPaintEngine);
       
  2144     d->cbrush = brush;
       
  2145     d->brush_style = brush.style();
       
  2146     d->brush_origin = origin;
       
  2147     d->has_brush = (d->brush_style != Qt::NoBrush);
       
  2148     d->updateUseEmulation();
       
  2149 }
       
  2150 
       
  2151 void QOpenGLPaintEngine::updateFont(const QFont &)
       
  2152 {
       
  2153 }
       
  2154 
       
  2155 void QOpenGLPaintEngine::updateMatrix(const QTransform &mtx)
       
  2156 {
       
  2157     Q_D(QOpenGLPaintEngine);
       
  2158 
       
  2159     d->matrix = mtx;
       
  2160 
       
  2161     d->mv_matrix[0][0] = mtx.m11();
       
  2162     d->mv_matrix[0][1] = mtx.m12();
       
  2163     d->mv_matrix[0][2] = 0;
       
  2164     d->mv_matrix[0][3] = mtx.m13();
       
  2165 
       
  2166     d->mv_matrix[1][0] = mtx.m21();
       
  2167     d->mv_matrix[1][1] = mtx.m22();
       
  2168     d->mv_matrix[1][2] = 0;
       
  2169     d->mv_matrix[1][3] = mtx.m23();
       
  2170 
       
  2171     d->mv_matrix[2][0] = 0;
       
  2172     d->mv_matrix[2][1] = 0;
       
  2173     d->mv_matrix[2][2] = 1;
       
  2174     d->mv_matrix[2][3] = 0;
       
  2175 
       
  2176     d->mv_matrix[3][0] = mtx.dx();
       
  2177     d->mv_matrix[3][1] = mtx.dy();
       
  2178     d->mv_matrix[3][2] = 0;
       
  2179     d->mv_matrix[3][3] = mtx.m33();
       
  2180 
       
  2181     d->txop = mtx.type();
       
  2182 
       
  2183     // 1/10000 == 0.0001, so we have good enough res to cover curves
       
  2184     // that span the entire widget...
       
  2185     d->inverseScale = qMax(1 / qMax( qMax(qAbs(mtx.m11()), qAbs(mtx.m22())),
       
  2186                                      qMax(qAbs(mtx.m12()), qAbs(mtx.m21())) ),
       
  2187                            qreal(0.0001));
       
  2188 
       
  2189     d->updateGLMatrix();
       
  2190     d->updateFastPen();
       
  2191 }
       
  2192 
       
  2193 void QOpenGLPaintEnginePrivate::updateGLMatrix() const
       
  2194 {
       
  2195     glMatrixMode(GL_MODELVIEW);
       
  2196 #ifndef QT_OPENGL_ES
       
  2197     glLoadMatrixd(&mv_matrix[0][0]);
       
  2198 #else
       
  2199     glLoadMatrixf(&mv_matrix[0][0]);
       
  2200 #endif
       
  2201 }
       
  2202 
       
  2203 void QOpenGLPaintEnginePrivate::disableClipping()
       
  2204 {
       
  2205     glDisable(GL_DEPTH_TEST);
       
  2206     glDisable(GL_SCISSOR_TEST);
       
  2207 }
       
  2208 
       
  2209 void QOpenGLPaintEnginePrivate::enableClipping()
       
  2210 {
       
  2211     Q_Q(QOpenGLPaintEngine);
       
  2212     if (!q->state()->hasClipping)
       
  2213         return;
       
  2214 
       
  2215     if (q->state()->fastClip.isEmpty())
       
  2216         glEnable(GL_DEPTH_TEST);
       
  2217     else
       
  2218         updateDepthClip(); // this will enable the scissor test
       
  2219 }
       
  2220 
       
  2221 void QOpenGLPaintEnginePrivate::updateDepthClip()
       
  2222 {
       
  2223     Q_Q(QOpenGLPaintEngine);
       
  2224 
       
  2225     ++q->state()->depthClipId;
       
  2226 
       
  2227     glDisable(GL_DEPTH_TEST);
       
  2228     glDisable(GL_SCISSOR_TEST);
       
  2229 
       
  2230     if (!q->state()->hasClipping)
       
  2231         return;
       
  2232 
       
  2233     QRect fastClip;
       
  2234     if (q->state()->clipEnabled) {
       
  2235         fastClip = q->state()->fastClip;
       
  2236     } else if (use_system_clip && q->systemClip().rects().count() == 1) {
       
  2237         fastClip = q->systemClip().rects().at(0);
       
  2238     }
       
  2239 
       
  2240     if (!fastClip.isEmpty()) {
       
  2241         glEnable(GL_SCISSOR_TEST);
       
  2242 
       
  2243         const int left = fastClip.left();
       
  2244         const int width = fastClip.width();
       
  2245         const int bottom = device->size().height() - (fastClip.bottom() + 1);
       
  2246         const int height = fastClip.height();
       
  2247 
       
  2248         glScissor(left, bottom, width, height);
       
  2249         return;
       
  2250     }
       
  2251 
       
  2252 #if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_1_CL)
       
  2253     glClearDepthf(0.0f);
       
  2254 #else
       
  2255     glClearDepth(0.0f);
       
  2256 #endif
       
  2257 
       
  2258     glEnable(GL_DEPTH_TEST);
       
  2259     glDepthMask(GL_TRUE);
       
  2260     glClear(GL_DEPTH_BUFFER_BIT);
       
  2261 
       
  2262     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
       
  2263     glDepthFunc(GL_ALWAYS);
       
  2264 
       
  2265     const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects();
       
  2266 
       
  2267     // rectangle count * 2 (triangles) * vertex count * component count (Z omitted)
       
  2268     QDataBuffer<q_vertexType> clipVertex(rects.size()*2*3*2);
       
  2269     for (int i = 0; i < rects.size(); ++i) {
       
  2270         q_vertexType x = i2vt(rects.at(i).left());
       
  2271         q_vertexType w = i2vt(rects.at(i).width());
       
  2272         q_vertexType h = i2vt(rects.at(i).height());
       
  2273         q_vertexType y = i2vt(rects.at(i).top());
       
  2274 
       
  2275         // First triangle
       
  2276         clipVertex.add(x);
       
  2277         clipVertex.add(y);
       
  2278 
       
  2279         clipVertex.add(x);
       
  2280         clipVertex.add(y + h);
       
  2281 
       
  2282         clipVertex.add(x + w);
       
  2283         clipVertex.add(y);
       
  2284 
       
  2285         // Second triangle
       
  2286         clipVertex.add(x);
       
  2287         clipVertex.add(y + h);
       
  2288 
       
  2289         clipVertex.add(x + w);
       
  2290         clipVertex.add(y + h);
       
  2291 
       
  2292         clipVertex.add (x + w);
       
  2293         clipVertex.add(y);
       
  2294     }
       
  2295 
       
  2296     if (rects.size()) {
       
  2297         glMatrixMode(GL_MODELVIEW);
       
  2298         glLoadIdentity();
       
  2299 
       
  2300         glEnableClientState(GL_VERTEX_ARRAY);
       
  2301         glVertexPointer(2, q_vertexTypeEnum, 0, clipVertex.data());
       
  2302 
       
  2303         glDrawArrays(GL_TRIANGLES, 0, rects.size()*2*3);
       
  2304         glDisableClientState(GL_VERTEX_ARRAY);
       
  2305         updateGLMatrix();
       
  2306     }
       
  2307 
       
  2308     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2309     glDepthMask(GL_FALSE);
       
  2310     glDepthFunc(GL_LEQUAL);
       
  2311 }
       
  2312 
       
  2313 void QOpenGLPaintEnginePrivate::systemStateChanged()
       
  2314 {
       
  2315     Q_Q(QOpenGLPaintEngine);
       
  2316     if (q->painter()->hasClipping())
       
  2317         q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip);
       
  2318     else
       
  2319         q->updateClipRegion(QRegion(), Qt::NoClip);
       
  2320 }
       
  2321 
       
  2322 void QOpenGLPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
       
  2323 {
       
  2324     Q_D(QOpenGLPaintEngine);
       
  2325 
       
  2326     // clipping is only supported when a stencil or depth buffer is
       
  2327     // available
       
  2328     if (!d->device->format().depth())
       
  2329         return;
       
  2330 
       
  2331     d->use_system_clip = false;
       
  2332     QRegion sysClip = systemClip();
       
  2333     if (!sysClip.isEmpty()) {
       
  2334         if (d->pdev->devType() != QInternal::Widget) {
       
  2335             d->use_system_clip = true;
       
  2336         } else {
       
  2337 #ifndef Q_WS_QWS
       
  2338             // Only use the system clip if we're currently rendering a widget with a GL painter.
       
  2339             if (d->currentClipWidget) {
       
  2340                 QWidgetPrivate *widgetPrivate = qt_widget_private(d->currentClipWidget->window());
       
  2341                 d->use_system_clip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
       
  2342             }
       
  2343 #endif
       
  2344         }
       
  2345     }
       
  2346 
       
  2347     d->flushDrawQueue();
       
  2348 
       
  2349     if (op == Qt::NoClip && !d->use_system_clip) {
       
  2350         state()->hasClipping = false;
       
  2351         state()->clipRegion = QRegion();
       
  2352         d->updateDepthClip();
       
  2353         return;
       
  2354     }
       
  2355 
       
  2356     bool isScreenClip = false;
       
  2357     if (!d->use_system_clip) {
       
  2358         QVector<QRect> untransformedRects = clipRegion.rects();
       
  2359 
       
  2360         if (untransformedRects.size() == 1) {
       
  2361             QPainterPath path;
       
  2362             path.addRect(untransformedRects[0]);
       
  2363             path = d->matrix.map(path);
       
  2364 
       
  2365             if (path.contains(QRectF(QPointF(), d->device->size())))
       
  2366                 isScreenClip = true;
       
  2367         }
       
  2368     }
       
  2369 
       
  2370     QRegion region = isScreenClip ? QRegion() : clipRegion * d->matrix;
       
  2371     switch (op) {
       
  2372     case Qt::NoClip:
       
  2373         if (!d->use_system_clip)
       
  2374             break;
       
  2375         state()->clipRegion = sysClip;
       
  2376         break;
       
  2377     case Qt::IntersectClip:
       
  2378         if (isScreenClip)
       
  2379             return;
       
  2380         if (state()->hasClipping) {
       
  2381             state()->clipRegion &= region;
       
  2382             break;
       
  2383         }
       
  2384         // fall through
       
  2385     case Qt::ReplaceClip:
       
  2386         if (d->use_system_clip)
       
  2387             state()->clipRegion = region & sysClip;
       
  2388         else
       
  2389             state()->clipRegion = region;
       
  2390         break;
       
  2391     case Qt::UniteClip:
       
  2392         state()->clipRegion |= region;
       
  2393         if (d->use_system_clip)
       
  2394             state()->clipRegion &= sysClip;
       
  2395         break;
       
  2396     default:
       
  2397         break;
       
  2398     }
       
  2399 
       
  2400     if (isScreenClip) {
       
  2401         state()->hasClipping = false;
       
  2402         state()->clipRegion = QRegion();
       
  2403     } else {
       
  2404         state()->hasClipping = op != Qt::NoClip || d->use_system_clip;
       
  2405     }
       
  2406 
       
  2407     if (state()->hasClipping && state()->clipRegion.rects().size() == 1)
       
  2408         state()->fastClip = state()->clipRegion.rects().at(0);
       
  2409     else
       
  2410         state()->fastClip = QRect();
       
  2411 
       
  2412     d->updateDepthClip();
       
  2413 }
       
  2414 
       
  2415 void QOpenGLPaintEngine::updateRenderHints(QPainter::RenderHints hints)
       
  2416 {
       
  2417     Q_D(QOpenGLPaintEngine);
       
  2418 
       
  2419     d->flushDrawQueue();
       
  2420     d->use_smooth_pixmap_transform = bool(hints & QPainter::SmoothPixmapTransform);
       
  2421     if ((hints & QPainter::Antialiasing) || (hints & QPainter::HighQualityAntialiasing)) {
       
  2422         if (d->use_fragment_programs && QGLOffscreen::isSupported()
       
  2423             && (hints & QPainter::HighQualityAntialiasing)) {
       
  2424             d->high_quality_antialiasing = true;
       
  2425         } else {
       
  2426             d->high_quality_antialiasing = false;
       
  2427             if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
       
  2428                 glEnable(GL_MULTISAMPLE);
       
  2429         }
       
  2430     } else {
       
  2431         d->high_quality_antialiasing = false;
       
  2432         if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
       
  2433             glDisable(GL_MULTISAMPLE);
       
  2434     }
       
  2435 
       
  2436     if (d->high_quality_antialiasing) {
       
  2437         d->offscreen.initialize();
       
  2438 
       
  2439         if (!d->offscreen.isValid()) {
       
  2440             DEBUG_ONCE_STR("Unable to initialize offscreen, disabling high quality antialiasing");
       
  2441             d->high_quality_antialiasing = false;
       
  2442             if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers)
       
  2443                 glEnable(GL_MULTISAMPLE);
       
  2444         }
       
  2445     }
       
  2446 
       
  2447     d->has_antialiasing = d->high_quality_antialiasing
       
  2448                           || ((hints & QPainter::Antialiasing)
       
  2449                               && (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers));
       
  2450 }
       
  2451 
       
  2452 
       
  2453 void QOpenGLPaintEnginePrivate::setPorterDuffData(float a, float b, float x, float y, float z)
       
  2454 {
       
  2455     porterduff_ab_data[0] = a;
       
  2456     porterduff_ab_data[1] = b;
       
  2457 
       
  2458     porterduff_xyz_data[0] = x;
       
  2459     porterduff_xyz_data[1] = y;
       
  2460     porterduff_xyz_data[2] = z;
       
  2461 }
       
  2462 
       
  2463 
       
  2464 void QOpenGLPaintEngine::updateCompositionMode(QPainter::CompositionMode composition_mode)
       
  2465 {
       
  2466     Q_D(QOpenGLPaintEngine);
       
  2467 
       
  2468     if (!d->use_fragment_programs && composition_mode > QPainter::CompositionMode_Plus)
       
  2469         composition_mode = QPainter::CompositionMode_SourceOver;
       
  2470 
       
  2471     d->composition_mode = composition_mode;
       
  2472 
       
  2473     d->has_fast_composition_mode = (!d->high_quality_antialiasing && composition_mode <= QPainter::CompositionMode_Plus)
       
  2474                                    || composition_mode == QPainter::CompositionMode_SourceOver
       
  2475                                    || composition_mode == QPainter::CompositionMode_Destination
       
  2476                                    || composition_mode == QPainter::CompositionMode_DestinationOver
       
  2477                                    || composition_mode == QPainter::CompositionMode_DestinationOut
       
  2478                                    || composition_mode == QPainter::CompositionMode_SourceAtop
       
  2479                                    || composition_mode == QPainter::CompositionMode_Xor
       
  2480                                    || composition_mode == QPainter::CompositionMode_Plus;
       
  2481 
       
  2482     if (d->has_fast_composition_mode)
       
  2483         d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODE_BLEND_MODE_MASK : COMPOSITION_MODE_BLEND_MODE_NOMASK;
       
  2484     else if (composition_mode <= QPainter::CompositionMode_Plus)
       
  2485         d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SIMPLE_PORTER_DUFF : COMPOSITION_MODES_SIMPLE_PORTER_DUFF_NOMASK;
       
  2486     else
       
  2487         switch (composition_mode) {
       
  2488         case QPainter::CompositionMode_Multiply:
       
  2489             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_MULTIPLY : COMPOSITION_MODES_MULTIPLY_NOMASK;
       
  2490             break;
       
  2491         case QPainter::CompositionMode_Screen:
       
  2492             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SCREEN : COMPOSITION_MODES_SCREEN_NOMASK;
       
  2493             break;
       
  2494         case QPainter::CompositionMode_Overlay:
       
  2495             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_OVERLAY : COMPOSITION_MODES_OVERLAY_NOMASK;
       
  2496             break;
       
  2497         case QPainter::CompositionMode_Darken:
       
  2498             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DARKEN : COMPOSITION_MODES_DARKEN_NOMASK;
       
  2499             break;
       
  2500         case QPainter::CompositionMode_Lighten:
       
  2501             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_LIGHTEN : COMPOSITION_MODES_LIGHTEN_NOMASK;
       
  2502             break;
       
  2503         case QPainter::CompositionMode_ColorDodge:
       
  2504             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORDODGE : COMPOSITION_MODES_COLORDODGE_NOMASK;
       
  2505             break;
       
  2506         case QPainter::CompositionMode_ColorBurn:
       
  2507             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_COLORBURN : COMPOSITION_MODES_COLORBURN_NOMASK;
       
  2508             break;
       
  2509         case QPainter::CompositionMode_HardLight:
       
  2510             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_HARDLIGHT : COMPOSITION_MODES_HARDLIGHT_NOMASK;
       
  2511             break;
       
  2512         case QPainter::CompositionMode_SoftLight:
       
  2513             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_SOFTLIGHT : COMPOSITION_MODES_SOFTLIGHT_NOMASK;
       
  2514             break;
       
  2515         case QPainter::CompositionMode_Difference:
       
  2516             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_DIFFERENCE : COMPOSITION_MODES_DIFFERENCE_NOMASK;
       
  2517             break;
       
  2518         case QPainter::CompositionMode_Exclusion:
       
  2519             d->fragment_composition_mode = d->high_quality_antialiasing ? COMPOSITION_MODES_EXCLUSION : COMPOSITION_MODES_EXCLUSION_NOMASK;
       
  2520             break;
       
  2521         default:
       
  2522             Q_ASSERT(false);
       
  2523         }
       
  2524 
       
  2525     switch(composition_mode) {
       
  2526     case QPainter::CompositionMode_DestinationOver:
       
  2527         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
       
  2528         d->setPorterDuffData(0, 1, 1, 1, 1);
       
  2529         break;
       
  2530     case QPainter::CompositionMode_Clear:
       
  2531         glBlendFunc(GL_ZERO, GL_ZERO);
       
  2532         d->setPorterDuffData(0, 0, 0, 0, 0);
       
  2533         break;
       
  2534     case QPainter::CompositionMode_Source:
       
  2535         glBlendFunc(GL_ONE, GL_ZERO);
       
  2536         d->setPorterDuffData(1, 0, 1, 1, 0);
       
  2537         break;
       
  2538     case QPainter::CompositionMode_Destination:
       
  2539         glBlendFunc(GL_ZERO, GL_ONE);
       
  2540         d->setPorterDuffData(0, 1, 1, 0, 1);
       
  2541         break;
       
  2542     case QPainter::CompositionMode_SourceIn:
       
  2543         glBlendFunc(GL_DST_ALPHA, GL_ZERO);
       
  2544         d->setPorterDuffData(1, 0, 1, 0, 0);
       
  2545         break;
       
  2546     case QPainter::CompositionMode_DestinationIn:
       
  2547         glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
       
  2548         d->setPorterDuffData(0, 1, 1, 0, 0);
       
  2549         break;
       
  2550     case QPainter::CompositionMode_SourceOut:
       
  2551         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
       
  2552         d->setPorterDuffData(0, 0, 0, 1, 0);
       
  2553         break;
       
  2554     case QPainter::CompositionMode_DestinationOut:
       
  2555         glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
       
  2556         d->setPorterDuffData(0, 0, 0, 0, 1);
       
  2557         break;
       
  2558     case QPainter::CompositionMode_SourceAtop:
       
  2559         glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
  2560         d->setPorterDuffData(1, 0, 1, 0, 1);
       
  2561         break;
       
  2562     case QPainter::CompositionMode_DestinationAtop:
       
  2563         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
       
  2564         d->setPorterDuffData(0, 1, 1, 1, 0);
       
  2565         break;
       
  2566     case QPainter::CompositionMode_Xor:
       
  2567         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
  2568         d->setPorterDuffData(0, 0, 0, 1, 1);
       
  2569         break;
       
  2570     case QPainter::CompositionMode_SourceOver:
       
  2571         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
       
  2572         d->setPorterDuffData(1, 0, 1, 1, 1);
       
  2573         break;
       
  2574     case QPainter::CompositionMode_Plus:
       
  2575         glBlendFunc(GL_ONE, GL_ONE);
       
  2576         d->setPorterDuffData(1, 1, 1, 1, 1);
       
  2577         break;
       
  2578     default:
       
  2579         break;
       
  2580     }
       
  2581 }
       
  2582 
       
  2583 class QGLMaskGenerator
       
  2584 {
       
  2585 public:
       
  2586     QGLMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal stroke_width = -1)
       
  2587         : p(path),
       
  2588           m(matrix),
       
  2589           w(stroke_width)
       
  2590     {
       
  2591     }
       
  2592 
       
  2593     virtual QRect screenRect() = 0;
       
  2594     virtual void drawMask(const QRect &rect) = 0;
       
  2595 
       
  2596     QPainterPath path() const { return p; }
       
  2597     QTransform matrix() const { return m; }
       
  2598     qreal strokeWidth() const { return w; }
       
  2599 
       
  2600     virtual ~QGLMaskGenerator() {}
       
  2601 
       
  2602 private:
       
  2603     QPainterPath p;
       
  2604     QTransform m;
       
  2605     qreal w;
       
  2606 };
       
  2607 
       
  2608 void QGLMaskTextureCache::setOffscreenSize(const QSize &sz)
       
  2609 {
       
  2610     Q_ASSERT(sz.width() == sz.height());
       
  2611 
       
  2612     if (offscreenSize != sz) {
       
  2613         offscreenSize = sz;
       
  2614         clearCache();
       
  2615     }
       
  2616 }
       
  2617 
       
  2618 void QGLMaskTextureCache::clearCache()
       
  2619 {
       
  2620     cache.clear();
       
  2621 
       
  2622     int quad_tree_size = 1;
       
  2623 
       
  2624     for (int i = block_size; i < offscreenSize.width(); i *= 2)
       
  2625         quad_tree_size += quad_tree_size * 4;
       
  2626 
       
  2627     for (int i = 0; i < 4; ++i) {
       
  2628         occupied_quadtree[i].resize(quad_tree_size);
       
  2629 
       
  2630         occupied_quadtree[i][0].key = 0;
       
  2631         occupied_quadtree[i][0].largest_available_block = offscreenSize.width();
       
  2632         occupied_quadtree[i][0].largest_used_block = 0;
       
  2633 
       
  2634         DEBUG_ONCE qDebug() << "QGLMaskTextureCache:: created quad tree of size" << quad_tree_size;
       
  2635     }
       
  2636 }
       
  2637 
       
  2638 void QGLMaskTextureCache::setDrawableSize(const QSize &sz)
       
  2639 {
       
  2640     drawableSize = sz;
       
  2641 }
       
  2642 
       
  2643 void QGLMaskTextureCache::maintainCache()
       
  2644 {
       
  2645     QGLTextureCacheHash::iterator it = cache.begin();
       
  2646     QGLTextureCacheHash::iterator end = cache.end();
       
  2647 
       
  2648     while (it != end) {
       
  2649         CacheInfo &cache_info = it.value();
       
  2650         ++cache_info.age;
       
  2651 
       
  2652         if (cache_info.age > 1) {
       
  2653             quadtreeInsert(cache_info.loc.channel, 0, cache_info.loc.rect);
       
  2654             it = cache.erase(it);
       
  2655         } else {
       
  2656             ++it;
       
  2657         }
       
  2658     }
       
  2659 }
       
  2660 
       
  2661 //#define DISABLE_MASK_CACHE
       
  2662 
       
  2663 QGLMaskTextureCache::CacheLocation QGLMaskTextureCache::getMask(QGLMaskGenerator &maskGenerator, QOpenGLPaintEnginePrivate *e)
       
  2664 {
       
  2665 #ifndef DISABLE_MASK_CACHE
       
  2666     engine = e;
       
  2667 
       
  2668     quint64 key = hash(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
       
  2669 
       
  2670     if (key == 0)
       
  2671         key = 1;
       
  2672 
       
  2673     CacheInfo info(maskGenerator.path(), maskGenerator.matrix(), maskGenerator.strokeWidth());
       
  2674 
       
  2675     QGLTextureCacheHash::iterator it = cache.find(key);
       
  2676 
       
  2677     while (it != cache.end() && it.key() == key) {
       
  2678         CacheInfo &cache_info = it.value();
       
  2679         if (info.stroke_width == cache_info.stroke_width && info.matrix == cache_info.matrix && info.path == cache_info.path) {
       
  2680             DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Using cached mask");
       
  2681 
       
  2682             cache_info.age = 0;
       
  2683             return cache_info.loc;
       
  2684         }
       
  2685         ++it;
       
  2686     }
       
  2687 
       
  2688     // mask was not found, create new mask
       
  2689 
       
  2690     DEBUG_ONCE_STR("QGLMaskTextureCache::getMask(): Creating new mask...");
       
  2691 
       
  2692     createMask(key, info, maskGenerator);
       
  2693 
       
  2694     cache.insert(key, info);
       
  2695 
       
  2696     return info.loc;
       
  2697 #else
       
  2698     CacheInfo info(maskGenerator.path(), maskGenerator.matrix());
       
  2699     createMask(0, info, maskGenerator);
       
  2700     return info.loc;
       
  2701 #endif
       
  2702 }
       
  2703 
       
  2704 #ifndef FloatToQuint64
       
  2705 #define FloatToQuint64(i) (quint64)((i) * 32)
       
  2706 #endif
       
  2707 
       
  2708 quint64 QGLMaskTextureCache::hash(const QPainterPath &p, const QTransform &m, qreal w)
       
  2709 {
       
  2710     Q_ASSERT(sizeof(quint64) == 8);
       
  2711 
       
  2712     quint64 h = 0;
       
  2713 
       
  2714     for (int i = 0; i < p.elementCount(); ++i) {
       
  2715         h += FloatToQuint64(p.elementAt(i).x) << 32;
       
  2716         h += FloatToQuint64(p.elementAt(i).y);
       
  2717         h += p.elementAt(i).type;
       
  2718     }
       
  2719 
       
  2720     h += FloatToQuint64(m.m11());
       
  2721 #ifndef Q_OS_WINCE    //  ###
       
  2722     //Compiler crashes for arm on WinCE
       
  2723     h += FloatToQuint64(m.m12()) << 4;
       
  2724     h += FloatToQuint64(m.m13()) << 8;
       
  2725     h += FloatToQuint64(m.m21()) << 12;
       
  2726     h += FloatToQuint64(m.m22()) << 16;
       
  2727     h += FloatToQuint64(m.m23()) << 20;
       
  2728     h += FloatToQuint64(m.m31()) << 24;
       
  2729     h += FloatToQuint64(m.m32()) << 28;
       
  2730 #endif
       
  2731     h += FloatToQuint64(m.m33()) << 32;
       
  2732 
       
  2733     h += FloatToQuint64(w);
       
  2734 
       
  2735     return h;
       
  2736 }
       
  2737 
       
  2738 void QGLMaskTextureCache::createMask(quint64 key, CacheInfo &info, QGLMaskGenerator &maskGenerator)
       
  2739 {
       
  2740     info.loc.screen_rect = maskGenerator.screenRect();
       
  2741 
       
  2742     if (info.loc.screen_rect.isEmpty()) {
       
  2743         info.loc.channel = 0;
       
  2744         info.loc.rect = QRect();
       
  2745         return;
       
  2746     }
       
  2747 
       
  2748     quadtreeAllocate(key, info.loc.screen_rect.size(), &info.loc.rect, &info.loc.channel);
       
  2749 
       
  2750     int ch = info.loc.channel;
       
  2751     glColorMask(ch == 0, ch == 1, ch == 2, ch == 3);
       
  2752 
       
  2753     maskGenerator.drawMask(info.loc.rect);
       
  2754 
       
  2755     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  2756 }
       
  2757 
       
  2758 int QGLMaskTextureCache::quadtreeBlocksize(int node)
       
  2759 {
       
  2760     DEBUG_ONCE qDebug() << "Offscreen size:" << offscreenSize.width();
       
  2761 
       
  2762     int blocksize = offscreenSize.width();
       
  2763 
       
  2764     while (node) {
       
  2765         node = (node - 1) / 4;
       
  2766         blocksize /= 2;
       
  2767     }
       
  2768 
       
  2769     return blocksize;
       
  2770 }
       
  2771 
       
  2772 QPoint QGLMaskTextureCache::quadtreeLocation(int node)
       
  2773 {
       
  2774     QPoint location;
       
  2775     int blocksize = quadtreeBlocksize(node);
       
  2776 
       
  2777     while (node) {
       
  2778         --node;
       
  2779 
       
  2780         if (node & 1)
       
  2781             location.setX(location.x() + blocksize);
       
  2782 
       
  2783         if (node & 2)
       
  2784             location.setY(location.y() + blocksize);
       
  2785 
       
  2786         node /= 4;
       
  2787         blocksize *= 2;
       
  2788     }
       
  2789 
       
  2790     return location;
       
  2791 }
       
  2792 
       
  2793 void QGLMaskTextureCache::quadtreeUpdate(int channel, int node, int current_block_size)
       
  2794 {
       
  2795     while (node) {
       
  2796         node = (node - 1) / 4;
       
  2797 
       
  2798         int first_child = node * 4 + 1;
       
  2799 
       
  2800         int largest_available = 0;
       
  2801         int largest_used = 0;
       
  2802 
       
  2803         bool all_empty = true;
       
  2804 
       
  2805         for (int i = 0; i < 4; ++i) {
       
  2806             largest_available = qMax(largest_available, occupied_quadtree[channel][first_child + i].largest_available_block);
       
  2807             largest_used = qMax(largest_used, occupied_quadtree[channel][first_child + i].largest_used_block);
       
  2808 
       
  2809             if (occupied_quadtree[channel][first_child + i].largest_available_block < current_block_size)
       
  2810                 all_empty = false;
       
  2811         }
       
  2812 
       
  2813         current_block_size *= 2;
       
  2814 
       
  2815         if (all_empty) {
       
  2816             occupied_quadtree[channel][node].largest_available_block = current_block_size;
       
  2817             occupied_quadtree[channel][node].largest_used_block = 0;
       
  2818         } else {
       
  2819             occupied_quadtree[channel][node].largest_available_block = largest_available;
       
  2820             occupied_quadtree[channel][node].largest_used_block = largest_used;
       
  2821         }
       
  2822     }
       
  2823 }
       
  2824 
       
  2825 void QGLMaskTextureCache::quadtreeInsert(int channel, quint64 key, const QRect &rect, int node)
       
  2826 {
       
  2827     int current_block_size = quadtreeBlocksize(node);
       
  2828     QPoint location = quadtreeLocation(node);
       
  2829     QRect relative = rect.translated(-location);
       
  2830 
       
  2831     if (relative.left() >= current_block_size || relative.top() >= current_block_size
       
  2832         || relative.right() < 0 || relative.bottom() < 0)
       
  2833         return;
       
  2834 
       
  2835     if (current_block_size == block_size // no more refining possible
       
  2836         || (relative.top() < block_size && relative.bottom() >= (current_block_size - block_size)
       
  2837             && relative.left() < block_size && relative.right() >= (current_block_size - block_size)))
       
  2838     {
       
  2839         if (key != 0) {
       
  2840             occupied_quadtree[channel][node].largest_available_block = 0;
       
  2841             occupied_quadtree[channel][node].largest_used_block = rect.width() * rect.height();
       
  2842         } else {
       
  2843             occupied_quadtree[channel][node].largest_available_block = current_block_size;
       
  2844             occupied_quadtree[channel][node].largest_used_block = 0;
       
  2845         }
       
  2846 
       
  2847         occupied_quadtree[channel][node].key = key;
       
  2848 
       
  2849         quadtreeUpdate(channel, node, current_block_size);
       
  2850     } else {
       
  2851         if (key && occupied_quadtree[channel][node].largest_available_block == current_block_size) {
       
  2852             // refining the quad tree, initialize child nodes
       
  2853             int half_block_size = current_block_size / 2;
       
  2854 
       
  2855             int temp = node * 4 + 1;
       
  2856             for (int sibling = 0; sibling < 4; ++sibling) {
       
  2857                 occupied_quadtree[channel][temp + sibling].largest_available_block = half_block_size;
       
  2858                 occupied_quadtree[channel][temp + sibling].largest_used_block = 0;
       
  2859                 occupied_quadtree[channel][temp + sibling].key = 0;
       
  2860             }
       
  2861         }
       
  2862 
       
  2863         node = node * 4 + 1;
       
  2864 
       
  2865         for (int sibling = 0; sibling < 4; ++sibling)
       
  2866             quadtreeInsert(channel, key, rect, node + sibling);
       
  2867     }
       
  2868 }
       
  2869 
       
  2870 void QGLMaskTextureCache::quadtreeClear(int channel, const QRect &rect, int node)
       
  2871 {
       
  2872     const quint64 &key = occupied_quadtree[channel][node].key;
       
  2873 
       
  2874     int current_block_size = quadtreeBlocksize(node);
       
  2875     QPoint location = quadtreeLocation(node);
       
  2876 
       
  2877     QRect relative = rect.translated(-location);
       
  2878 
       
  2879     if (relative.left() >= current_block_size || relative.top() >= current_block_size
       
  2880         || relative.right() < 0 || relative.bottom() < 0)
       
  2881         return;
       
  2882 
       
  2883     if (key != 0) {
       
  2884         QGLTextureCacheHash::iterator it = cache.find(key);
       
  2885 
       
  2886         Q_ASSERT(it != cache.end());
       
  2887 
       
  2888         while (it != cache.end() && it.key() == key) {
       
  2889             const CacheInfo &cache_info = it.value();
       
  2890 
       
  2891             if (cache_info.loc.channel == channel
       
  2892                 && cache_info.loc.rect.left() <= location.x()
       
  2893                 && cache_info.loc.rect.top() <= location.y()
       
  2894                 && cache_info.loc.rect.right() >= location.x()
       
  2895                 && cache_info.loc.rect.bottom() >= location.y())
       
  2896             {
       
  2897                 quadtreeInsert(channel, 0, cache_info.loc.rect);
       
  2898                 engine->cacheItemErased(channel, cache_info.loc.rect);
       
  2899                 cache.erase(it);
       
  2900                 goto found;
       
  2901             } else {
       
  2902                 ++it;
       
  2903             }
       
  2904         }
       
  2905 
       
  2906         // if we don't find the key there's an error in the quadtree
       
  2907         Q_ASSERT(false);
       
  2908 found:
       
  2909         Q_ASSERT(occupied_quadtree[channel][node].key == 0);
       
  2910     } else if (occupied_quadtree[channel][node].largest_available_block < current_block_size) {
       
  2911         Q_ASSERT(current_block_size >= block_size);
       
  2912 
       
  2913         node = node * 4 + 1;
       
  2914 
       
  2915         for (int sibling = 0; sibling < 4; ++sibling)
       
  2916             quadtreeClear(channel, rect, node + sibling);
       
  2917     }
       
  2918 }
       
  2919 
       
  2920 bool QGLMaskTextureCache::quadtreeFindAvailableLocation(const QSize &size, QRect *rect, int *channel)
       
  2921 {
       
  2922     int needed_block_size = qMax(1, qMax(size.width(), size.height()));
       
  2923 
       
  2924     for (int i = 0; i < 4; ++i) {
       
  2925         int current_block_size = offscreenSize.width();
       
  2926 
       
  2927         if (occupied_quadtree[i][0].largest_available_block >= needed_block_size) {
       
  2928             int node = 0;
       
  2929 
       
  2930             while (current_block_size != occupied_quadtree[i][node].largest_available_block) {
       
  2931                 Q_ASSERT(current_block_size > block_size);
       
  2932                 Q_ASSERT(current_block_size > occupied_quadtree[i][node].largest_available_block);
       
  2933 
       
  2934                 node = node * 4 + 1;
       
  2935                 current_block_size /= 2;
       
  2936 
       
  2937                 int sibling = 0;
       
  2938 
       
  2939                 while (occupied_quadtree[i][node + sibling].largest_available_block < needed_block_size)
       
  2940                     ++sibling;
       
  2941 
       
  2942                 Q_ASSERT(sibling < 4);
       
  2943                 node += sibling;
       
  2944             }
       
  2945 
       
  2946             *channel = i;
       
  2947             *rect = QRect(quadtreeLocation(node), size);
       
  2948 
       
  2949             return true;
       
  2950         }
       
  2951     }
       
  2952 
       
  2953     return false;
       
  2954 }
       
  2955 
       
  2956 void QGLMaskTextureCache::quadtreeFindExistingLocation(const QSize &size, QRect *rect, int *channel)
       
  2957 {
       
  2958     // try to pick small masks to throw out, as large masks are more expensive to recompute
       
  2959     *channel = qrand() % 4;
       
  2960     for (int i = 0; i < 4; ++i)
       
  2961         if (occupied_quadtree[i][0].largest_used_block < occupied_quadtree[*channel][0].largest_used_block)
       
  2962             *channel = i;
       
  2963 
       
  2964     int needed_block_size = qt_next_power_of_two(qMax(1, qMax(size.width(), size.height())));
       
  2965 
       
  2966     int node = 0;
       
  2967     int current_block_size = offscreenSize.width();
       
  2968 
       
  2969     while (current_block_size > block_size
       
  2970            && current_block_size >= needed_block_size * 2
       
  2971            && occupied_quadtree[*channel][node].key == 0)
       
  2972     {
       
  2973         node = node * 4 + 1;
       
  2974 
       
  2975         int sibling = 0;
       
  2976 
       
  2977         for (int i = 1; i < 4; ++i) {
       
  2978             if (occupied_quadtree[*channel][node + i].largest_used_block
       
  2979                 <= occupied_quadtree[*channel][node + sibling].largest_used_block)
       
  2980             {
       
  2981                 sibling = i;
       
  2982             }
       
  2983         }
       
  2984 
       
  2985         node += sibling;
       
  2986         current_block_size /= 2;
       
  2987     }
       
  2988 
       
  2989     *rect = QRect(quadtreeLocation(node), size);
       
  2990 }
       
  2991 
       
  2992 void QGLMaskTextureCache::quadtreeAllocate(quint64 key, const QSize &size, QRect *rect, int *channel)
       
  2993 {
       
  2994 #ifndef DISABLE_MASK_CACHE
       
  2995     if (!quadtreeFindAvailableLocation(size, rect, channel)) {
       
  2996         quadtreeFindExistingLocation(size, rect, channel);
       
  2997         quadtreeClear(*channel, *rect);
       
  2998     }
       
  2999 
       
  3000     quadtreeInsert(*channel, key, *rect);
       
  3001 #else
       
  3002     *channel = 0;
       
  3003     *rect = QRect(QPoint(), size);
       
  3004 #endif
       
  3005 }
       
  3006 
       
  3007 class QGLTrapezoidMaskGenerator : public QGLMaskGenerator
       
  3008 {
       
  3009 public:
       
  3010     QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, qreal strokeWidth = -1.0);
       
  3011 
       
  3012     QRect screenRect();
       
  3013     void drawMask(const QRect &rect);
       
  3014 
       
  3015 private:
       
  3016     QRect screen_rect;
       
  3017     bool has_screen_rect;
       
  3018 
       
  3019     QGLOffscreen *offscreen;
       
  3020 
       
  3021     GLuint maskFragmentProgram;
       
  3022 
       
  3023     virtual QVector<QGLTrapezoid> generateTrapezoids() = 0;
       
  3024     virtual QRect computeScreenRect() = 0;
       
  3025 };
       
  3026 
       
  3027 class QGLPathMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3028 {
       
  3029 public:
       
  3030     QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3031 
       
  3032 private:
       
  3033     QVector<QGLTrapezoid> generateTrapezoids();
       
  3034     QRect computeScreenRect();
       
  3035 
       
  3036     QPolygonF poly;
       
  3037 };
       
  3038 
       
  3039 class QGLLineMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3040 {
       
  3041 public:
       
  3042     QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3043 
       
  3044 private:
       
  3045     QVector<QGLTrapezoid> generateTrapezoids();
       
  3046     QRect computeScreenRect();
       
  3047 
       
  3048     QPainterPath transformedPath;
       
  3049 };
       
  3050 
       
  3051 class QGLRectMaskGenerator : public QGLTrapezoidMaskGenerator
       
  3052 {
       
  3053 public:
       
  3054     QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram);
       
  3055 
       
  3056 private:
       
  3057     QVector<QGLTrapezoid> generateTrapezoids();
       
  3058     QRect computeScreenRect();
       
  3059 
       
  3060     QPainterPath transformedPath;
       
  3061 };
       
  3062 
       
  3063 class QGLEllipseMaskGenerator : public QGLMaskGenerator
       
  3064 {
       
  3065 public:
       
  3066     QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offscreen, GLuint maskFragmentProgram, int *maskVariableLocations);
       
  3067 
       
  3068     QRect screenRect();
       
  3069     void drawMask(const QRect &rect);
       
  3070 
       
  3071 private:
       
  3072     QRect screen_rect;
       
  3073 
       
  3074     QRectF ellipseRect;
       
  3075 
       
  3076     QGLOffscreen *offscreen;
       
  3077 
       
  3078     GLuint maskFragmentProgram;
       
  3079 
       
  3080     int *maskVariableLocations;
       
  3081 
       
  3082     float vertexArray[4 * 2];
       
  3083 };
       
  3084 
       
  3085 QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program, qreal stroke_width)
       
  3086     : QGLMaskGenerator(path, matrix, stroke_width)
       
  3087     ,  has_screen_rect(false)
       
  3088     ,  offscreen(&offs)
       
  3089     ,  maskFragmentProgram(program)
       
  3090 {
       
  3091 }
       
  3092 
       
  3093 extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array);
       
  3094 extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array);
       
  3095 
       
  3096 void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect)
       
  3097 {
       
  3098 #ifdef QT_OPENGL_ES
       
  3099     Q_UNUSED(rect);
       
  3100 #else
       
  3101     glMatrixMode(GL_MODELVIEW);
       
  3102     glPushMatrix();
       
  3103     glLoadIdentity();
       
  3104 
       
  3105     QGLContext *ctx = offscreen->context();
       
  3106     offscreen->bind();
       
  3107 
       
  3108     glDisable(GL_TEXTURE_GEN_S);
       
  3109     glDisable(GL_TEXTURE_1D);
       
  3110 
       
  3111     GLfloat vertexArray[4 * 2];
       
  3112     qt_add_rect_to_array(rect, vertexArray);
       
  3113 
       
  3114     bool needs_scissor = rect != screen_rect;
       
  3115 
       
  3116     if (needs_scissor) {
       
  3117         glEnable(GL_SCISSOR_TEST);
       
  3118         glScissor(rect.left(), offscreen->offscreenSize().height() - rect.bottom() - 1, rect.width(), rect.height());
       
  3119     }
       
  3120 
       
  3121     QVector<QGLTrapezoid> trapezoids = generateTrapezoids();
       
  3122 
       
  3123     // clear mask
       
  3124     glBlendFunc(GL_ZERO, GL_ZERO); // clear
       
  3125     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3126     glEnableClientState(GL_VERTEX_ARRAY);
       
  3127     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3128     glDisableClientState(GL_VERTEX_ARRAY);
       
  3129 
       
  3130     glBlendFunc(GL_ONE, GL_ONE); // add mask
       
  3131     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  3132     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
       
  3133 
       
  3134     QPoint delta = rect.topLeft() - screen_rect.topLeft();
       
  3135     glBegin(GL_QUADS);
       
  3136     for (int i = 0; i < trapezoids.size(); ++i)
       
  3137         drawTrapezoid(trapezoids[i].translated(delta), offscreen->offscreenSize().height(), ctx);
       
  3138     glEnd();
       
  3139 
       
  3140     if (needs_scissor)
       
  3141         glDisable(GL_SCISSOR_TEST);
       
  3142 
       
  3143     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  3144 
       
  3145     glMatrixMode(GL_MODELVIEW);
       
  3146     glPopMatrix();
       
  3147 #endif
       
  3148 }
       
  3149 
       
  3150 QRect QGLTrapezoidMaskGenerator::screenRect()
       
  3151 {
       
  3152     if (!has_screen_rect) {
       
  3153         screen_rect = computeScreenRect();
       
  3154         has_screen_rect = true;
       
  3155     }
       
  3156 
       
  3157     screen_rect = screen_rect.intersected(QRect(QPoint(), offscreen->drawableSize()));
       
  3158 
       
  3159     return screen_rect;
       
  3160 }
       
  3161 
       
  3162 QGLPathMaskGenerator::QGLPathMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
       
  3163     : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
       
  3164 {
       
  3165 }
       
  3166 
       
  3167 QRect QGLPathMaskGenerator::computeScreenRect()
       
  3168 {
       
  3169     poly = path().toFillPolygon(matrix());
       
  3170     return poly.boundingRect().toAlignedRect();
       
  3171 }
       
  3172 
       
  3173 QVector<QGLTrapezoid> QGLPathMaskGenerator::generateTrapezoids()
       
  3174 {
       
  3175     QOpenGLImmediateModeTessellator tessellator;
       
  3176     tessellator.tessellate(poly.data(), poly.count(), path().fillRule() == Qt::WindingFill);
       
  3177     return tessellator.trapezoids;
       
  3178 }
       
  3179 
       
  3180 QGLRectMaskGenerator::QGLRectMaskGenerator(const QPainterPath &path, const QTransform &matrix, QGLOffscreen &offs, GLuint program)
       
  3181     : QGLTrapezoidMaskGenerator(path, matrix, offs, program)
       
  3182 {
       
  3183 }
       
  3184 
       
  3185 QGLLineMaskGenerator::QGLLineMaskGenerator(const QPainterPath &path, const QTransform &matrix, qreal width, QGLOffscreen &offs, GLuint program)
       
  3186     : QGLTrapezoidMaskGenerator(path, matrix, offs, program, width)
       
  3187 {
       
  3188 }
       
  3189 
       
  3190 QRect QGLRectMaskGenerator::computeScreenRect()
       
  3191 {
       
  3192     transformedPath = matrix().map(path());
       
  3193 
       
  3194     return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
       
  3195 }
       
  3196 
       
  3197 QRect QGLLineMaskGenerator::computeScreenRect()
       
  3198 {
       
  3199     transformedPath = matrix().map(path());
       
  3200 
       
  3201     return transformedPath.controlPointRect().adjusted(-1, -1, 1, 1).toAlignedRect();
       
  3202 }
       
  3203 
       
  3204 QVector<QGLTrapezoid> QGLLineMaskGenerator::generateTrapezoids()
       
  3205 {
       
  3206     QOpenGLImmediateModeTessellator tessellator;
       
  3207     QPointF last;
       
  3208     for (int i = 0; i < transformedPath.elementCount(); ++i) {
       
  3209         QPainterPath::Element element = transformedPath.elementAt(i);
       
  3210 
       
  3211         Q_ASSERT(!element.isCurveTo());
       
  3212 
       
  3213         if (element.isLineTo())
       
  3214             tessellator.tessellateRect(last, element, strokeWidth());
       
  3215 
       
  3216         last = element;
       
  3217     }
       
  3218 
       
  3219     return tessellator.trapezoids;
       
  3220 }
       
  3221 
       
  3222 QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids()
       
  3223 {
       
  3224     Q_ASSERT(transformedPath.elementCount() == 5);
       
  3225 
       
  3226     QOpenGLImmediateModeTessellator tessellator;
       
  3227     if (matrix().type() <= QTransform::TxScale) {
       
  3228         QPointF a = transformedPath.elementAt(0);
       
  3229         QPointF b = transformedPath.elementAt(1);
       
  3230         QPointF c = transformedPath.elementAt(2);
       
  3231         QPointF d = transformedPath.elementAt(3);
       
  3232 
       
  3233         QPointF first = (a + d) * 0.5;
       
  3234         QPointF last = (b + c) * 0.5;
       
  3235 
       
  3236         QPointF delta = a - d;
       
  3237 
       
  3238         // manhattan distance (no rotation)
       
  3239         qreal width = qAbs(delta.x()) + qAbs(delta.y());
       
  3240 
       
  3241         Q_ASSERT(qFuzzyIsNull(delta.x()) || qFuzzyIsNull(delta.y()));
       
  3242 
       
  3243         tessellator.tessellateRect(first, last, width);
       
  3244     } else {
       
  3245         QPointF points[5];
       
  3246 
       
  3247         for (int i = 0; i < 5; ++i)
       
  3248             points[i] = transformedPath.elementAt(i);
       
  3249 
       
  3250         tessellator.tessellateConvex(points, 5);
       
  3251     }
       
  3252     return tessellator.trapezoids;
       
  3253 }
       
  3254 
       
  3255 static QPainterPath ellipseRectToPath(const QRectF &rect)
       
  3256 {
       
  3257     QPainterPath path;
       
  3258     path.addEllipse(rect);
       
  3259     return path;
       
  3260 }
       
  3261 
       
  3262 QGLEllipseMaskGenerator::QGLEllipseMaskGenerator(const QRectF &rect, const QTransform &matrix, QGLOffscreen &offs, GLuint program, int *locations)
       
  3263     : QGLMaskGenerator(ellipseRectToPath(rect), matrix),
       
  3264       ellipseRect(rect),
       
  3265       offscreen(&offs),
       
  3266       maskFragmentProgram(program),
       
  3267       maskVariableLocations(locations)
       
  3268 {
       
  3269 }
       
  3270 
       
  3271 QRect QGLEllipseMaskGenerator::screenRect()
       
  3272 {
       
  3273     QPointF center = ellipseRect.center();
       
  3274 
       
  3275     QPointF points[] = {
       
  3276         QPointF(ellipseRect.left(), center.y()),
       
  3277         QPointF(ellipseRect.right(), center.y()),
       
  3278         QPointF(center.x(), ellipseRect.top()),
       
  3279         QPointF(center.x(), ellipseRect.bottom())
       
  3280     };
       
  3281 
       
  3282     qreal min_screen_delta_len = QREAL_MAX;
       
  3283 
       
  3284     for (int i = 0; i < 4; ++i) {
       
  3285         QPointF delta = points[i] - center;
       
  3286 
       
  3287         // normalize
       
  3288         delta /= qSqrt(delta.x() * delta.x() + delta.y() * delta.y());
       
  3289 
       
  3290         QPointF screen_delta(matrix().m11() * delta.x() + matrix().m21() * delta.y(),
       
  3291                              matrix().m12() * delta.x() + matrix().m22() * delta.y());
       
  3292 
       
  3293         min_screen_delta_len = qMin(min_screen_delta_len,
       
  3294                                     qreal(qSqrt(screen_delta.x() * screen_delta.x() + screen_delta.y() * screen_delta.y())));
       
  3295     }
       
  3296 
       
  3297     const qreal padding = 2.0f;
       
  3298 
       
  3299     qreal grow = padding / min_screen_delta_len;
       
  3300 
       
  3301     QRectF boundingRect = ellipseRect.adjusted(-grow, -grow, grow, grow);
       
  3302 
       
  3303     boundingRect = matrix().mapRect(boundingRect);
       
  3304 
       
  3305     QPointF p(0.5, 0.5);
       
  3306 
       
  3307     screen_rect = QRect((boundingRect.topLeft() - p).toPoint(),
       
  3308                         (boundingRect.bottomRight() + p).toPoint());
       
  3309 
       
  3310     return screen_rect;
       
  3311 }
       
  3312 
       
  3313 void QGLEllipseMaskGenerator::drawMask(const QRect &rect)
       
  3314 {
       
  3315 #ifdef QT_OPENGL_ES
       
  3316     Q_UNUSED(rect);
       
  3317 #else
       
  3318     QGLContext *ctx = offscreen->context();
       
  3319     offscreen->bind();
       
  3320 
       
  3321     glDisable(GL_TEXTURE_GEN_S);
       
  3322     glDisable(GL_TEXTURE_1D);
       
  3323 
       
  3324     // fragment program needs the inverse radii of the ellipse
       
  3325     glTexCoord2f(1.0f / (ellipseRect.width() * 0.5f),
       
  3326                  1.0f / (ellipseRect.height() * 0.5f));
       
  3327 
       
  3328     QTransform translate(1, 0, 0, 1, -ellipseRect.center().x(), -ellipseRect.center().y());
       
  3329     QTransform gl_to_qt(1, 0, 0, -1, 0, offscreen->drawableSize().height());
       
  3330     QTransform inv_matrix = gl_to_qt * matrix().inverted() * translate;
       
  3331 
       
  3332     float m[3][4] = { { inv_matrix.m11(), inv_matrix.m12(), inv_matrix.m13() },
       
  3333                       { inv_matrix.m21(), inv_matrix.m22(), inv_matrix.m23() },
       
  3334                       { inv_matrix.m31(), inv_matrix.m32(), inv_matrix.m33() } };
       
  3335 
       
  3336     QPoint offs(screen_rect.left() - rect.left(), (offscreen->drawableSize().height() - screen_rect.top())
       
  3337                                                 - (offscreen->offscreenSize().height() - rect.top()));
       
  3338 
       
  3339     // last component needs to be 1.0f to avoid Nvidia bug on linux
       
  3340     float ellipse_offset[4] = { offs.x(), offs.y(), 0.0f, 1.0f };
       
  3341 
       
  3342     GLfloat vertexArray[4 * 2];
       
  3343     qt_add_rect_to_array(rect, vertexArray);
       
  3344 
       
  3345     glBlendFunc(GL_ONE, GL_ZERO); // set mask
       
  3346     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  3347     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, maskFragmentProgram);
       
  3348 
       
  3349     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M0], m[0]);
       
  3350     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M1], m[1]);
       
  3351     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_INV_MATRIX_M2], m[2]);
       
  3352 
       
  3353     glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, maskVariableLocations[VAR_ELLIPSE_OFFSET], ellipse_offset);
       
  3354 
       
  3355     glEnableClientState(GL_VERTEX_ARRAY);
       
  3356     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3357     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3358     glDisableClientState(GL_VERTEX_ARRAY);
       
  3359     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  3360 #endif
       
  3361 }
       
  3362 
       
  3363 void QOpenGLPaintEnginePrivate::drawOffscreenPath(const QPainterPath &path)
       
  3364 {
       
  3365 #ifdef Q_WS_QWS
       
  3366     Q_UNUSED(path);
       
  3367 #else
       
  3368     DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::drawOffscreenPath()");
       
  3369 
       
  3370     disableClipping();
       
  3371 
       
  3372     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  3373                                                        FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3374     QGLPathMaskGenerator maskGenerator(path, matrix, offscreen, program);
       
  3375     addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
       
  3376 
       
  3377     enableClipping();
       
  3378 #endif
       
  3379 }
       
  3380 
       
  3381 void QOpenGLPaintEnginePrivate::drawFastRect(const QRectF &r)
       
  3382 {
       
  3383     Q_Q(QOpenGLPaintEngine);
       
  3384     DEBUG_ONCE_STR("QOpenGLPaintEngine::drawRects(): drawing fast rect");
       
  3385 
       
  3386     q_vertexType vertexArray[10];
       
  3387     qt_add_rect_to_array(r, vertexArray);
       
  3388 
       
  3389     if (has_pen)
       
  3390         QOpenGLCoordinateOffset::enableOffset(this);
       
  3391 
       
  3392     if (has_brush) {
       
  3393         flushDrawQueue();
       
  3394 
       
  3395         bool temp = high_quality_antialiasing;
       
  3396         high_quality_antialiasing = false;
       
  3397 
       
  3398         q->updateCompositionMode(composition_mode);
       
  3399 
       
  3400         setGradientOps(cbrush, r);
       
  3401 
       
  3402         bool fast_style = current_style == Qt::LinearGradientPattern
       
  3403                           || current_style == Qt::SolidPattern;
       
  3404 
       
  3405         if (fast_style && has_fast_composition_mode) {
       
  3406             glEnableClientState(GL_VERTEX_ARRAY);
       
  3407             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3408             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  3409             glDisableClientState(GL_VERTEX_ARRAY);
       
  3410         } else {
       
  3411             composite(r);
       
  3412         }
       
  3413 
       
  3414         high_quality_antialiasing = temp;
       
  3415 
       
  3416         q->updateCompositionMode(composition_mode);
       
  3417     }
       
  3418 
       
  3419     if (has_pen) {
       
  3420         if (has_fast_pen && !high_quality_antialiasing) {
       
  3421             setGradientOps(cpen.brush(), r);
       
  3422 
       
  3423             vertexArray[8] = vertexArray[0];
       
  3424             vertexArray[9] = vertexArray[1];
       
  3425 
       
  3426             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3427             glEnableClientState(GL_VERTEX_ARRAY);
       
  3428             glDrawArrays(GL_LINE_STRIP, 0, 5);
       
  3429             glDisableClientState(GL_VERTEX_ARRAY);
       
  3430         } else {
       
  3431             QPainterPath path;
       
  3432             path.setFillRule(Qt::WindingFill);
       
  3433 
       
  3434             qreal left = r.left();
       
  3435             qreal right = r.right();
       
  3436             qreal top = r.top();
       
  3437             qreal bottom = r.bottom();
       
  3438 
       
  3439             path.moveTo(left, top);
       
  3440             path.lineTo(right, top);
       
  3441             path.lineTo(right, bottom);
       
  3442             path.lineTo(left, bottom);
       
  3443             path.lineTo(left, top);
       
  3444 
       
  3445             strokePath(path, false);
       
  3446         }
       
  3447 
       
  3448         QOpenGLCoordinateOffset::disableOffset(this);
       
  3449     }
       
  3450 }
       
  3451 
       
  3452 bool QOpenGLPaintEnginePrivate::isFastRect(const QRectF &rect)
       
  3453 {
       
  3454     if (matrix.type() < QTransform::TxRotate) {
       
  3455         QRectF r = matrix.mapRect(rect);
       
  3456         return r.topLeft().toPoint() == r.topLeft()
       
  3457             && r.bottomRight().toPoint() == r.bottomRight();
       
  3458     }
       
  3459 
       
  3460     return false;
       
  3461 }
       
  3462 
       
  3463 void QOpenGLPaintEngine::drawRects(const QRect *rects, int rectCount)
       
  3464 {
       
  3465     struct RectF {
       
  3466         qreal x;
       
  3467         qreal y;
       
  3468         qreal w;
       
  3469         qreal h;
       
  3470     };
       
  3471     Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
       
  3472     RectF fr[256];
       
  3473     while (rectCount) {
       
  3474         int i = 0;
       
  3475         while (i < rectCount && i < 256) {
       
  3476             fr[i].x = rects[i].x();
       
  3477             fr[i].y = rects[i].y();
       
  3478             fr[i].w = rects[i].width();
       
  3479             fr[i].h = rects[i].height();
       
  3480             ++i;
       
  3481         }
       
  3482         drawRects((QRectF *)(void *)fr, i);
       
  3483         rects += i;
       
  3484         rectCount -= i;
       
  3485     }
       
  3486 }
       
  3487 
       
  3488 void QOpenGLPaintEngine::drawRects(const QRectF *rects, int rectCount)
       
  3489 {
       
  3490     Q_D(QOpenGLPaintEngine);
       
  3491 
       
  3492     if (d->use_emulation) {
       
  3493         QPaintEngineEx::drawRects(rects, rectCount);
       
  3494         return;
       
  3495     }
       
  3496 
       
  3497     for (int i=0; i<rectCount; ++i) {
       
  3498         const QRectF &r = rects[i];
       
  3499 
       
  3500         // optimization for rects which can be drawn aliased
       
  3501         if (!d->high_quality_antialiasing || d->isFastRect(r)) {
       
  3502             d->drawFastRect(r);
       
  3503         } else {
       
  3504             QPainterPath path;
       
  3505             path.addRect(r);
       
  3506 
       
  3507             if (d->has_brush) {
       
  3508                 d->disableClipping();
       
  3509                 GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
       
  3510                                                                    FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3511 
       
  3512                 if (d->matrix.type() >= QTransform::TxProject) {
       
  3513                     QGLPathMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
       
  3514                     d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  3515                 } else {
       
  3516                     QGLRectMaskGenerator maskGenerator(path, d->matrix, d->offscreen, program);
       
  3517                     d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  3518                 }
       
  3519 
       
  3520                 d->enableClipping();
       
  3521             }
       
  3522 
       
  3523             if (d->has_pen) {
       
  3524                 if (d->has_fast_pen)
       
  3525                     d->strokeLines(path);
       
  3526                 else
       
  3527                     d->strokePath(path, false);
       
  3528             }
       
  3529         }
       
  3530     }
       
  3531 }
       
  3532 
       
  3533 static void addQuadAsTriangle(q_vertexType *quad, q_vertexType *triangle)
       
  3534 {
       
  3535     triangle[0] = quad[0];
       
  3536     triangle[1] = quad[1];
       
  3537 
       
  3538     triangle[2] = quad[2];
       
  3539     triangle[3] = quad[3];
       
  3540 
       
  3541     triangle[4] = quad[4];
       
  3542     triangle[5] = quad[5];
       
  3543 
       
  3544     triangle[6] = quad[4];
       
  3545     triangle[7] = quad[5];
       
  3546 
       
  3547     triangle[8] = quad[6];
       
  3548     triangle[9] = quad[7];
       
  3549 
       
  3550     triangle[10] = quad[0];
       
  3551     triangle[11] = quad[1];
       
  3552 }
       
  3553 
       
  3554 void QOpenGLPaintEngine::drawPoints(const QPoint *points, int pointCount)
       
  3555 {
       
  3556     Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
       
  3557     QT_PointF fp[256];
       
  3558     while (pointCount) {
       
  3559         int i = 0;
       
  3560         while (i < pointCount && i < 256) {
       
  3561             fp[i].x = points[i].x();
       
  3562             fp[i].y = points[i].y();
       
  3563             ++i;
       
  3564         }
       
  3565         drawPoints((QPointF *)(void *)fp, i);
       
  3566         points += i;
       
  3567         pointCount -= i;
       
  3568     }
       
  3569 }
       
  3570 
       
  3571 void QOpenGLPaintEngine::drawPoints(const QPointF *points, int pointCount)
       
  3572 {
       
  3573     Q_D(QOpenGLPaintEngine);
       
  3574 
       
  3575     if (d->use_emulation) {
       
  3576         QPaintEngineEx::drawPoints(points, pointCount);
       
  3577         return;
       
  3578     }
       
  3579 
       
  3580     d->setGradientOps(d->cpen.brush(), QRectF());
       
  3581 
       
  3582     if (!d->cpen.isCosmetic() || d->high_quality_antialiasing) {
       
  3583         Qt::PenCapStyle capStyle = d->cpen.capStyle();
       
  3584         if (capStyle == Qt::FlatCap)
       
  3585             d->cpen.setCapStyle(Qt::SquareCap);
       
  3586         QPaintEngine::drawPoints(points, pointCount);
       
  3587         d->cpen.setCapStyle(capStyle);
       
  3588         return;
       
  3589     }
       
  3590 
       
  3591     d->flushDrawQueue();
       
  3592 
       
  3593     if (d->has_fast_pen) {
       
  3594         QVarLengthArray<q_vertexType> vertexArray(6 * pointCount);
       
  3595 
       
  3596         glMatrixMode(GL_MODELVIEW);
       
  3597         glPushMatrix();
       
  3598         glLoadIdentity();
       
  3599 
       
  3600         int j = 0;
       
  3601         for (int i = 0; i < pointCount; ++i) {
       
  3602             QPointF mapped = d->matrix.map(points[i]);
       
  3603 
       
  3604             qreal xf = qRound(mapped.x());
       
  3605             qreal yf = qRound(mapped.y());
       
  3606 
       
  3607             q_vertexType x = f2vt(xf);
       
  3608             q_vertexType y = f2vt(yf);
       
  3609 
       
  3610             vertexArray[j++] = x;
       
  3611             vertexArray[j++] = y - f2vt(0.5);
       
  3612 
       
  3613             vertexArray[j++] = x + f2vt(1.5);
       
  3614             vertexArray[j++] = y + f2vt(1.0);
       
  3615 
       
  3616             vertexArray[j++] = x;
       
  3617             vertexArray[j++] = y + f2vt(1.0);
       
  3618         }
       
  3619 
       
  3620         glEnableClientState(GL_VERTEX_ARRAY);
       
  3621 
       
  3622         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3623         glDrawArrays(GL_TRIANGLES, 0, pointCount*3);
       
  3624 
       
  3625         glDisableClientState(GL_VERTEX_ARRAY);
       
  3626 
       
  3627         glPopMatrix();
       
  3628         return;
       
  3629     }
       
  3630 
       
  3631     const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
       
  3632 
       
  3633     if (sizeof(qreal) == sizeof(double)) {
       
  3634         Q_ASSERT(sizeof(QPointF) == 16);
       
  3635         glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
       
  3636     }
       
  3637     else {
       
  3638         Q_ASSERT(sizeof(QPointF) == 8);
       
  3639         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3640     }
       
  3641 
       
  3642     glEnableClientState(GL_VERTEX_ARRAY);
       
  3643     glDrawArrays(GL_POINTS, 0, pointCount);
       
  3644     glDisableClientState(GL_VERTEX_ARRAY);
       
  3645 }
       
  3646 
       
  3647 void QOpenGLPaintEngine::drawLines(const QLine *lines, int lineCount)
       
  3648 {
       
  3649     struct PointF {
       
  3650         qreal x;
       
  3651         qreal y;
       
  3652     };
       
  3653     struct LineF {
       
  3654         PointF p1;
       
  3655         PointF p2;
       
  3656     };
       
  3657     Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
       
  3658     Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
       
  3659     LineF fl[256];
       
  3660     while (lineCount) {
       
  3661         int i = 0;
       
  3662         while (i < lineCount && i < 256) {
       
  3663             fl[i].p1.x = lines[i].x1();
       
  3664             fl[i].p1.y = lines[i].y1();
       
  3665             fl[i].p2.x = lines[i].x2();
       
  3666             fl[i].p2.y = lines[i].y2();
       
  3667             ++i;
       
  3668         }
       
  3669         drawLines((QLineF *)(void *)fl, i);
       
  3670         lines += i;
       
  3671         lineCount -= i;
       
  3672     }
       
  3673 }
       
  3674 
       
  3675 void QOpenGLPaintEngine::drawLines(const QLineF *lines, int lineCount)
       
  3676 {
       
  3677     Q_D(QOpenGLPaintEngine);
       
  3678 
       
  3679     if (d->use_emulation) {
       
  3680         QPaintEngineEx::drawLines(lines, lineCount);
       
  3681         return;
       
  3682     }
       
  3683 
       
  3684     if (d->has_pen) {
       
  3685         QOpenGLCoordinateOffset offset(d);
       
  3686         if (d->has_fast_pen && !d->high_quality_antialiasing) {
       
  3687             //### gradient resolving on lines isn't correct
       
  3688             d->setGradientOps(d->cpen.brush(), QRectF());
       
  3689 
       
  3690             bool useRects = false;
       
  3691             // scale or 90 degree rotation?
       
  3692             if (d->matrix.type() <= QTransform::TxTranslate
       
  3693                 || (!d->cpen.isCosmetic()
       
  3694                     && (d->matrix.type() <= QTransform::TxScale
       
  3695                         || (d->matrix.type() == QTransform::TxRotate
       
  3696                             && d->matrix.m11() == 0 && d->matrix.m22() == 0)))) {
       
  3697                 useRects = true;
       
  3698                 for (int i = 0; i < lineCount; ++i) {
       
  3699                     if (lines[i].p1().x() != lines[i].p2().x()
       
  3700                         && lines[i].p1().y() != lines[i].p2().y()) {
       
  3701                         useRects = false;
       
  3702                         break;
       
  3703                     }
       
  3704                 }
       
  3705             }
       
  3706 
       
  3707             q_vertexType endCap = f2vt(d->cpen.capStyle() == Qt::FlatCap ? 0 : 0.5);
       
  3708             if (useRects) {
       
  3709                 QVarLengthArray<q_vertexType> vertexArray(12 * lineCount);
       
  3710 
       
  3711                 q_vertexType quad[8];
       
  3712                 for (int i = 0; i < lineCount; ++i) {
       
  3713                     q_vertexType x1 = f2vt(lines[i].x1());
       
  3714                     q_vertexType x2 = f2vt(lines[i].x2());
       
  3715                     q_vertexType y1 = f2vt(lines[i].y1());
       
  3716                     q_vertexType y2 = f2vt(lines[i].y2());
       
  3717 
       
  3718                     if (x1 == x2) {
       
  3719                         if (y1 > y2)
       
  3720                             qSwap(y1, y2);
       
  3721 
       
  3722                         quad[0] = x1 - f2vt(0.5);
       
  3723                         quad[1] = y1 - endCap;
       
  3724 
       
  3725                         quad[2] = x1 + f2vt(0.5);
       
  3726                         quad[3] = y1 - endCap;
       
  3727 
       
  3728                         quad[4] = x1 + f2vt(0.5);
       
  3729                         quad[5] = y2 + endCap;
       
  3730 
       
  3731                         quad[6] = x1 - f2vt(0.5);
       
  3732                         quad[7] = y2 + endCap;
       
  3733                     } else {
       
  3734                         if (x1 > x2)
       
  3735                             qSwap(x1, x2);
       
  3736 
       
  3737                         quad[0] = x1 - endCap;
       
  3738                         quad[1] = y1 + f2vt(0.5);
       
  3739 
       
  3740                         quad[2] = x1 - endCap;
       
  3741                         quad[3] = y1 - f2vt(0.5);
       
  3742 
       
  3743                         quad[4] = x2 + endCap;
       
  3744                         quad[5] = y1 - f2vt(0.5);
       
  3745 
       
  3746                         quad[6] = x2 + endCap;
       
  3747                         quad[7] = y1 + f2vt(0.5);
       
  3748                     }
       
  3749 
       
  3750                     addQuadAsTriangle(quad, &vertexArray[12*i]);
       
  3751                 }
       
  3752 
       
  3753                 glEnableClientState(GL_VERTEX_ARRAY);
       
  3754 
       
  3755                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3756                 glDrawArrays(GL_TRIANGLES, 0, lineCount*6);
       
  3757 
       
  3758                 glDisableClientState(GL_VERTEX_ARRAY);
       
  3759             } else {
       
  3760                 QVarLengthArray<q_vertexType> vertexArray(4 * lineCount);
       
  3761                 for (int i = 0; i < lineCount; ++i) {
       
  3762                     const QPointF a = lines[i].p1();
       
  3763                     vertexArray[4*i]   = f2vt(lines[i].x1());
       
  3764                     vertexArray[4*i+1] = f2vt(lines[i].y1());
       
  3765                     vertexArray[4*i+2] = f2vt(lines[i].x2());
       
  3766                     vertexArray[4*i+3] = f2vt(lines[i].y2());
       
  3767                 }
       
  3768 
       
  3769                 glEnableClientState(GL_VERTEX_ARRAY);
       
  3770 
       
  3771                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3772                 glDrawArrays(GL_LINES, 0, lineCount*2);
       
  3773 
       
  3774                 glVertexPointer(2, q_vertexTypeEnum, 4*sizeof(q_vertexType), vertexArray.constData() + 2);
       
  3775                 glDrawArrays(GL_POINTS, 0, lineCount);
       
  3776 
       
  3777                 glDisableClientState(GL_VERTEX_ARRAY);
       
  3778             }
       
  3779         } else {
       
  3780             QPainterPath path;
       
  3781             path.setFillRule(Qt::WindingFill);
       
  3782             for (int i=0; i<lineCount; ++i) {
       
  3783                 const QLineF &l = lines[i];
       
  3784 
       
  3785                 if (l.p1() == l.p2()) {
       
  3786                     if (d->cpen.capStyle() != Qt::FlatCap) {
       
  3787                         QPointF p = l.p1();
       
  3788                         drawPoints(&p, 1);
       
  3789                     }
       
  3790                     continue;
       
  3791                 }
       
  3792 
       
  3793                 path.moveTo(l.x1(), l.y1());
       
  3794                 path.lineTo(l.x2(), l.y2());
       
  3795             }
       
  3796 
       
  3797             if (d->has_fast_pen && d->high_quality_antialiasing)
       
  3798                 d->strokeLines(path);
       
  3799             else
       
  3800                 d->strokePath(path, false);
       
  3801         }
       
  3802     }
       
  3803 }
       
  3804 
       
  3805 void QOpenGLPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
       
  3806 {
       
  3807     Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
       
  3808     QVarLengthArray<QT_PointF> p(pointCount);
       
  3809     for (int i=0; i<pointCount; ++i) {
       
  3810         p[i].x = points[i].x();
       
  3811         p[i].y = points[i].y();
       
  3812     }
       
  3813     drawPolygon((QPointF *)p.data(), pointCount, mode);
       
  3814 }
       
  3815 
       
  3816 void QOpenGLPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
       
  3817 {
       
  3818     Q_D(QOpenGLPaintEngine);
       
  3819     if(pointCount < 2)
       
  3820         return;
       
  3821 
       
  3822     if (d->use_emulation) {
       
  3823         QPaintEngineEx::drawPolygon(points, pointCount, mode);
       
  3824         return;
       
  3825     }
       
  3826 
       
  3827     QRectF bounds;
       
  3828     if ((mode == ConvexMode && !d->high_quality_antialiasing && state()->brushNeedsResolving()) ||
       
  3829         ((d->has_fast_pen && !d->high_quality_antialiasing) && state()->penNeedsResolving())) {
       
  3830         qreal minx = points[0].x(), miny = points[0].y(),
       
  3831               maxx = points[0].x(), maxy = points[0].y();
       
  3832         for (int i = 1; i < pointCount; ++i) {
       
  3833             const QPointF &pt = points[i];
       
  3834             if (minx > pt.x())
       
  3835                 minx = pt.x();
       
  3836             if (miny > pt.y())
       
  3837                 miny = pt.y();
       
  3838             if (maxx < pt.x())
       
  3839                 maxx = pt.x();
       
  3840             if (maxy < pt.y())
       
  3841                 maxy = pt.y();
       
  3842         }
       
  3843         bounds = QRectF(minx, maxx, maxx-minx, maxy-miny);
       
  3844     }
       
  3845 
       
  3846     QOpenGLCoordinateOffset offset(d);
       
  3847 
       
  3848     if (d->has_brush && mode != PolylineMode) {
       
  3849         if (mode == ConvexMode && !d->high_quality_antialiasing) {
       
  3850             //### resolving on polygon from points isn't correct
       
  3851             d->setGradientOps(d->cbrush, bounds);
       
  3852 
       
  3853             const qreal *vertexArray = reinterpret_cast<const qreal*>(&points[0]);
       
  3854 
       
  3855             if (sizeof(qreal) == sizeof(double)) {
       
  3856                 Q_ASSERT(sizeof(QPointF) == 16);
       
  3857                 glVertexPointer(2, GL_DOUBLE, 0, vertexArray);
       
  3858             }
       
  3859             else {
       
  3860                 Q_ASSERT(sizeof(QPointF) == 8);
       
  3861                 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  3862             }
       
  3863 
       
  3864             glEnableClientState(GL_VERTEX_ARRAY);
       
  3865             glDrawArrays(GL_TRIANGLE_FAN, 0, pointCount);
       
  3866             glDisableClientState(GL_VERTEX_ARRAY);
       
  3867         } else {
       
  3868             QPainterPath path;
       
  3869             path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
       
  3870             path.moveTo(points[0]);
       
  3871             for (int i=1; i<pointCount; ++i)
       
  3872                 path.lineTo(points[i]);
       
  3873             d->fillPath(path);
       
  3874         }
       
  3875     }
       
  3876 
       
  3877     if (d->has_pen) {
       
  3878         if (d->has_fast_pen && !d->high_quality_antialiasing) {
       
  3879             d->setGradientOps(d->cpen.brush(), bounds);
       
  3880             QVarLengthArray<q_vertexType> vertexArray(pointCount*2 + 2);
       
  3881             glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray.constData());
       
  3882             int i;
       
  3883             for (i=0; i<pointCount; ++i) {
       
  3884                 vertexArray[i*2] = f2vt(points[i].x());
       
  3885                 vertexArray[i*2+1] = f2vt(points[i].y());
       
  3886             }
       
  3887 
       
  3888             glEnableClientState(GL_VERTEX_ARRAY);
       
  3889             if (mode != PolylineMode) {
       
  3890                 vertexArray[i*2] = vertexArray[0];
       
  3891                 vertexArray[i*2+1] = vertexArray[1];
       
  3892                 glDrawArrays(GL_LINE_STRIP, 0, pointCount+1);
       
  3893             } else {
       
  3894                 glDrawArrays(GL_LINE_STRIP, 0, pointCount);
       
  3895                 glDrawArrays(GL_POINTS, pointCount-1, 1);
       
  3896             }
       
  3897             glDisableClientState(GL_VERTEX_ARRAY);
       
  3898         } else {
       
  3899             QPainterPath path(points[0]);
       
  3900             for (int i = 1; i < pointCount; ++i)
       
  3901                 path.lineTo(points[i]);
       
  3902             if (mode != PolylineMode)
       
  3903                 path.lineTo(points[0]);
       
  3904 
       
  3905             if (d->has_fast_pen)
       
  3906                 d->strokeLines(path);
       
  3907             else
       
  3908                 d->strokePath(path, true);
       
  3909         }
       
  3910     }
       
  3911 }
       
  3912 
       
  3913 void QOpenGLPaintEnginePrivate::strokeLines(const QPainterPath &path)
       
  3914 {
       
  3915     DEBUG_ONCE_STR("QOpenGLPaintEnginePrivate::strokeLines()");
       
  3916 
       
  3917     qreal penWidth = cpen.widthF();
       
  3918 
       
  3919     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  3920                                                        FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, 0, true);
       
  3921     QGLLineMaskGenerator maskGenerator(path, matrix, penWidth == 0 ? 1.0 : penWidth,
       
  3922                                        offscreen, program);
       
  3923 
       
  3924     disableClipping();
       
  3925 
       
  3926     QBrush temp = cbrush;
       
  3927     QPointF origin = brush_origin;
       
  3928 
       
  3929     cbrush = cpen.brush();
       
  3930     brush_origin = QPointF();
       
  3931 
       
  3932     addItem(qt_mask_texture_cache()->getMask(maskGenerator, this));
       
  3933 
       
  3934     cbrush = temp;
       
  3935     brush_origin = origin;
       
  3936 
       
  3937     enableClipping();
       
  3938 }
       
  3939 
       
  3940 extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
       
  3941 
       
  3942 void QOpenGLPaintEnginePrivate::strokePath(const QPainterPath &path, bool use_cache)
       
  3943 {
       
  3944     QBrush old_brush = cbrush;
       
  3945     cbrush = cpen.brush();
       
  3946 
       
  3947     qreal txscale = 1;
       
  3948     if (cpen.isCosmetic() || (qt_scaleForTransform(matrix, &txscale) && txscale != 1)) {
       
  3949         QTransform temp = matrix;
       
  3950         matrix = QTransform();
       
  3951         glPushMatrix();
       
  3952 
       
  3953         if (has_antialiasing) {
       
  3954             glLoadIdentity();
       
  3955         } else {
       
  3956             float offs_matrix[] =
       
  3957               { 1, 0, 0, 0,
       
  3958                 0, 1, 0, 0,
       
  3959                 0, 0, 1, 0,
       
  3960                 0.5, 0.5, 0, 1 };
       
  3961             glLoadMatrixf(offs_matrix);
       
  3962         }
       
  3963 
       
  3964         QPen pen = cpen;
       
  3965         if (txscale != 1)
       
  3966             pen.setWidthF(pen.widthF() * txscale);
       
  3967         if (use_cache)
       
  3968             fillPath(qt_opengl_stroke_cache()->getStrokedPath(temp.map(path), pen));
       
  3969         else
       
  3970             fillPath(strokeForPath(temp.map(path), pen));
       
  3971 
       
  3972         glPopMatrix();
       
  3973         matrix = temp;
       
  3974     } else if (use_cache) {
       
  3975         fillPath(qt_opengl_stroke_cache()->getStrokedPath(path, cpen));
       
  3976     } else {
       
  3977         fillPath(strokeForPath(path, cpen));
       
  3978     }
       
  3979 
       
  3980     cbrush = old_brush;
       
  3981 }
       
  3982 
       
  3983 void QOpenGLPaintEnginePrivate::strokePathFastPen(const QPainterPath &path, bool needsResolving)
       
  3984 {
       
  3985 #ifndef QT_OPENGL_ES
       
  3986     QRectF bounds;
       
  3987     if (needsResolving)
       
  3988         bounds = path.controlPointRect();
       
  3989     setGradientOps(cpen.brush(), bounds);
       
  3990 
       
  3991     QBezier beziers[32];
       
  3992     for (int i=0; i<path.elementCount(); ++i) {
       
  3993         const QPainterPath::Element &e = path.elementAt(i);
       
  3994         switch (e.type) {
       
  3995         case QPainterPath::MoveToElement:
       
  3996             if (i != 0)
       
  3997                 glEnd(); // GL_LINE_STRIP
       
  3998             glBegin(GL_LINE_STRIP);
       
  3999             glVertex2d(e.x, e.y);
       
  4000 
       
  4001             break;
       
  4002         case QPainterPath::LineToElement:
       
  4003             glVertex2d(e.x, e.y);
       
  4004             break;
       
  4005 
       
  4006         case QPainterPath::CurveToElement:
       
  4007         {
       
  4008             QPointF sp = path.elementAt(i-1);
       
  4009             QPointF cp2 = path.elementAt(i+1);
       
  4010             QPointF ep = path.elementAt(i+2);
       
  4011             i+=2;
       
  4012 
       
  4013             qreal inverseScaleHalf = inverseScale / 2;
       
  4014             beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
       
  4015             QBezier *b = beziers;
       
  4016             while (b >= beziers) {
       
  4017                 // check if we can pop the top bezier curve from the stack
       
  4018                 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  4019                 qreal d;
       
  4020                 if (l > inverseScale) {
       
  4021                     d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
       
  4022                               - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  4023                         + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
       
  4024                                 - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  4025                     d /= l;
       
  4026                 } else {
       
  4027                     d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  4028                         qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  4029                 }
       
  4030                 if (d < inverseScaleHalf || b == beziers + 31) {
       
  4031                     // good enough, we pop it off and add the endpoint
       
  4032                     glVertex2d(b->x4, b->y4);
       
  4033                     --b;
       
  4034                 } else {
       
  4035                     // split, second half of the polygon goes lower into the stack
       
  4036                     b->split(b+1, b);
       
  4037                     ++b;
       
  4038                 }
       
  4039             }
       
  4040         } // case CurveToElement
       
  4041         default:
       
  4042             break;
       
  4043         } // end of switch
       
  4044     }
       
  4045     glEnd(); // GL_LINE_STRIP
       
  4046 #else
       
  4047     // have to use vertex arrays on embedded
       
  4048     QRectF bounds;
       
  4049     if (needsResolving)
       
  4050         bounds = path.controlPointRect();
       
  4051     setGradientOps(cpen.brush(), bounds);
       
  4052 
       
  4053     glEnableClientState(GL_VERTEX_ARRAY);
       
  4054     tess_points.reset();
       
  4055     QBezier beziers[32];
       
  4056     for (int i=0; i<path.elementCount(); ++i) {
       
  4057         const QPainterPath::Element &e = path.elementAt(i);
       
  4058         switch (e.type) {
       
  4059         case QPainterPath::MoveToElement:
       
  4060             if (i != 0) {
       
  4061                 glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
       
  4062                 glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
       
  4063                 tess_points.reset();
       
  4064             }
       
  4065             tess_points.add(QPointF(e.x, e.y));
       
  4066 
       
  4067             break;
       
  4068         case QPainterPath::LineToElement:
       
  4069             tess_points.add(QPointF(e.x, e.y));
       
  4070             break;
       
  4071 
       
  4072         case QPainterPath::CurveToElement:
       
  4073         {
       
  4074             QPointF sp = path.elementAt(i-1);
       
  4075             QPointF cp2 = path.elementAt(i+1);
       
  4076             QPointF ep = path.elementAt(i+2);
       
  4077             i+=2;
       
  4078 
       
  4079             qreal inverseScaleHalf = inverseScale / 2;
       
  4080             beziers[0] = QBezier::fromPoints(sp, e, cp2, ep);
       
  4081             QBezier *b = beziers;
       
  4082             while (b >= beziers) {
       
  4083                 // check if we can pop the top bezier curve from the stack
       
  4084                 qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1);
       
  4085                 qreal d;
       
  4086                 if (l > inverseScale) {
       
  4087                     d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2)
       
  4088                               - (b->y4 - b->y1)*(b->x1 - b->x2) )
       
  4089                         + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3)
       
  4090                                 - (b->y4 - b->y1)*(b->x1 - b->x3) );
       
  4091                     d /= l;
       
  4092                 } else {
       
  4093                     d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
       
  4094                         qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
       
  4095                 }
       
  4096                 if (d < inverseScaleHalf || b == beziers + 31) {
       
  4097                     // good enough, we pop it off and add the endpoint
       
  4098                     tess_points.add(QPointF(b->x4, b->y4));
       
  4099                     --b;
       
  4100                 } else {
       
  4101                     // split, second half of the polygon goes lower into the stack
       
  4102                     b->split(b+1, b);
       
  4103                     ++b;
       
  4104                 }
       
  4105             }
       
  4106         } // case CurveToElement
       
  4107         default:
       
  4108             break;
       
  4109         } // end of switch
       
  4110     }
       
  4111     glVertexPointer(2, q_vertexTypeEnum, 0, tess_points.data());
       
  4112     glDrawArrays(GL_LINE_STRIP, 0, tess_points.size());
       
  4113     glDisableClientState(GL_VERTEX_ARRAY);
       
  4114 #endif
       
  4115 }
       
  4116 
       
  4117 static bool pathClosed(const QPainterPath &path)
       
  4118 {
       
  4119     QPointF lastMoveTo = path.elementAt(0);
       
  4120     QPointF lastPoint = lastMoveTo;
       
  4121 
       
  4122     for (int i = 1; i < path.elementCount(); ++i) {
       
  4123         const QPainterPath::Element &e = path.elementAt(i);
       
  4124         switch (e.type) {
       
  4125         case QPainterPath::MoveToElement:
       
  4126             if (lastMoveTo != lastPoint)
       
  4127                 return false;
       
  4128             lastMoveTo = lastPoint = e;
       
  4129             break;
       
  4130         case QPainterPath::LineToElement:
       
  4131             lastPoint = e;
       
  4132             break;
       
  4133         case QPainterPath::CurveToElement:
       
  4134             lastPoint = path.elementAt(i + 2);
       
  4135             i+=2;
       
  4136             break;
       
  4137         default:
       
  4138             break;
       
  4139         }
       
  4140     }
       
  4141 
       
  4142     return lastMoveTo == lastPoint;
       
  4143 }
       
  4144 
       
  4145 void QOpenGLPaintEngine::drawPath(const QPainterPath &path)
       
  4146 {
       
  4147     Q_D(QOpenGLPaintEngine);
       
  4148 
       
  4149     if (path.isEmpty())
       
  4150         return;
       
  4151 
       
  4152     if (d->use_emulation) {
       
  4153         QPaintEngineEx::drawPath(path);
       
  4154         return;
       
  4155     }
       
  4156 
       
  4157     QOpenGLCoordinateOffset offset(d);
       
  4158 
       
  4159     if (d->has_brush) {
       
  4160         bool path_closed = pathClosed(path);
       
  4161 
       
  4162         bool has_thick_pen =
       
  4163             path_closed
       
  4164             && d->has_pen
       
  4165             && d->cpen.style() == Qt::SolidLine
       
  4166             && d->cpen.isSolid()
       
  4167             && d->cpen.color().alpha() == 255
       
  4168             && d->txop < QTransform::TxProject
       
  4169             && d->cpen.widthF() >= 2 / qSqrt(qMin(d->matrix.m11() * d->matrix.m11()
       
  4170                                                   + d->matrix.m21() * d->matrix.m21(),
       
  4171                                                   d->matrix.m12() * d->matrix.m12()
       
  4172                                                   + d->matrix.m22() * d->matrix.m22()));
       
  4173 
       
  4174         if (has_thick_pen) {
       
  4175             DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::drawPath(): Using thick pen optimization, style:" << d->cbrush.style();
       
  4176 
       
  4177             d->flushDrawQueue();
       
  4178 
       
  4179             bool temp = d->high_quality_antialiasing;
       
  4180             d->high_quality_antialiasing = false;
       
  4181 
       
  4182             updateCompositionMode(d->composition_mode);
       
  4183 
       
  4184             d->fillPath(path);
       
  4185 
       
  4186             d->high_quality_antialiasing = temp;
       
  4187             updateCompositionMode(d->composition_mode);
       
  4188         } else {
       
  4189             d->fillPath(path);
       
  4190         }
       
  4191     }
       
  4192 
       
  4193     if (d->has_pen) {
       
  4194         if (d->has_fast_pen && !d->high_quality_antialiasing)
       
  4195             d->strokePathFastPen(path, state()->penNeedsResolving());
       
  4196         else
       
  4197             d->strokePath(path, true);
       
  4198     }
       
  4199 }
       
  4200 
       
  4201 void QOpenGLPaintEnginePrivate::drawImageAsPath(const QRectF &r, const QImage &img, const QRectF &sr)
       
  4202 {
       
  4203     QBrush old_brush = cbrush;
       
  4204     QPointF old_brush_origin = brush_origin;
       
  4205 
       
  4206     qreal scaleX = r.width() / sr.width();
       
  4207     qreal scaleY = r.height() / sr.height();
       
  4208 
       
  4209     QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
       
  4210     brush_matrix.scale(scaleX, scaleY);
       
  4211     brush_matrix.translate(-sr.left(), -sr.top());
       
  4212 
       
  4213     cbrush = QBrush(img);
       
  4214     cbrush.setTransform(brush_matrix);
       
  4215     brush_origin = QPointF();
       
  4216 
       
  4217     QPainterPath p;
       
  4218     p.addRect(r);
       
  4219     fillPath(p);
       
  4220 
       
  4221     cbrush = old_brush;
       
  4222     brush_origin = old_brush_origin;
       
  4223 }
       
  4224 
       
  4225 void QOpenGLPaintEnginePrivate::drawTiledImageAsPath(const QRectF &r, const QImage &img, qreal sx, qreal sy,
       
  4226                                                      const QPointF &offset)
       
  4227 {
       
  4228     QBrush old_brush = cbrush;
       
  4229     QPointF old_brush_origin = brush_origin;
       
  4230 
       
  4231     QTransform brush_matrix = QTransform::fromTranslate(r.left(), r.top());
       
  4232     brush_matrix.scale(sx, sy);
       
  4233     brush_matrix.translate(-offset.x(), -offset.y());
       
  4234 
       
  4235     cbrush = QBrush(img);
       
  4236     cbrush.setTransform(brush_matrix);
       
  4237     brush_origin = QPointF();
       
  4238 
       
  4239     QPainterPath p;
       
  4240     p.addRect(r);
       
  4241     fillPath(p);
       
  4242 
       
  4243     cbrush = old_brush;
       
  4244     brush_origin = old_brush_origin;
       
  4245 }
       
  4246 
       
  4247 static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
       
  4248 {
       
  4249     return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
       
  4250 }
       
  4251 
       
  4252 template <typename T>
       
  4253 static const T qSubImage(const T &image, const QRectF &src, QRectF *srcNew)
       
  4254 {
       
  4255     const int sx1 = qMax(0, qFloor(src.left()));
       
  4256     const int sy1 = qMax(0, qFloor(src.top()));
       
  4257     const int sx2 = qMin(image.width(), qCeil(src.right()));
       
  4258     const int sy2 = qMin(image.height(), qCeil(src.bottom()));
       
  4259 
       
  4260     const T sub = image.copy(sx1, sy1, sx2 - sx1, sy2 - sy1);
       
  4261 
       
  4262     if (srcNew)
       
  4263         *srcNew = src.translated(-sx1, -sy1);
       
  4264 
       
  4265     return sub;
       
  4266 }
       
  4267 
       
  4268 void QOpenGLPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
       
  4269 {
       
  4270     Q_D(QOpenGLPaintEngine);
       
  4271     if (pm.depth() == 1) {
       
  4272         QPixmap tpx(pm.size());
       
  4273         tpx.fill(Qt::transparent);
       
  4274         QPainter p(&tpx);
       
  4275         p.setPen(d->cpen);
       
  4276         p.drawPixmap(0, 0, pm);
       
  4277         p.end();
       
  4278         drawPixmap(r, tpx, sr);
       
  4279         return;
       
  4280     }
       
  4281 
       
  4282     const int sz = d->max_texture_size;
       
  4283     if (pm.width() > sz || pm.height() > sz) {
       
  4284         QRectF subsr;
       
  4285         const QPixmap sub = qSubImage(pm, sr, &subsr);
       
  4286 
       
  4287         if (sub.width() <= sz && sub.height() <= sz) {
       
  4288             drawPixmap(r, sub, subsr);
       
  4289         } else {
       
  4290             const QPixmap scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
       
  4291             const qreal sx = scaled.width() / qreal(sub.width());
       
  4292             const qreal sy = scaled.height() / qreal(sub.height());
       
  4293 
       
  4294             drawPixmap(r, scaled, scaleRect(subsr, sx, sy));
       
  4295         }
       
  4296         return;
       
  4297     }
       
  4298 
       
  4299 
       
  4300     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
       
  4301         d->drawImageAsPath(r, pm.toImage(), sr);
       
  4302     else {
       
  4303         GLenum target = qt_gl_preferredTextureTarget();
       
  4304         d->flushDrawQueue();
       
  4305         QGLTexture *tex =
       
  4306             d->device->context()->d_func()->bindTexture(pm, target, GL_RGBA,
       
  4307                                                         QGLContext::InternalBindOption);
       
  4308         drawTextureRect(pm.width(), pm.height(), r, sr, target, tex);
       
  4309     }
       
  4310 }
       
  4311 
       
  4312 void QOpenGLPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &offset)
       
  4313 {
       
  4314     Q_D(QOpenGLPaintEngine);
       
  4315     if (pm.depth() == 1) {
       
  4316         QPixmap tpx(pm.size());
       
  4317         tpx.fill(Qt::transparent);
       
  4318         QPainter p(&tpx);
       
  4319         p.setPen(d->cpen);
       
  4320         p.drawPixmap(0, 0, pm);
       
  4321         p.end();
       
  4322         drawTiledPixmap(r, tpx, offset);
       
  4323         return;
       
  4324     }
       
  4325 
       
  4326     QImage scaled;
       
  4327     const int sz = d->max_texture_size;
       
  4328     if (pm.width() > sz || pm.height() > sz) {
       
  4329         int rw = qCeil(r.width());
       
  4330         int rh = qCeil(r.height());
       
  4331         if (rw < pm.width() && rh < pm.height()) {
       
  4332             drawTiledPixmap(r, pm.copy(0, 0, rw, rh), offset);
       
  4333             return;
       
  4334         }
       
  4335 
       
  4336         scaled = pm.toImage().scaled(sz, sz, Qt::KeepAspectRatio);
       
  4337     }
       
  4338 
       
  4339     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r))) {
       
  4340         if (scaled.isNull())
       
  4341             d->drawTiledImageAsPath(r, pm.toImage(), 1, 1, offset);
       
  4342         else {
       
  4343             const qreal sx = pm.width() / qreal(scaled.width());
       
  4344             const qreal sy = pm.height() / qreal(scaled.height());
       
  4345             d->drawTiledImageAsPath(r, scaled, sx, sy, offset);
       
  4346         }
       
  4347     } else {
       
  4348         d->flushDrawQueue();
       
  4349 
       
  4350         QGLTexture *tex;
       
  4351         if (scaled.isNull())
       
  4352             tex = d->device->context()->d_func()->bindTexture(pm, GL_TEXTURE_2D, GL_RGBA,
       
  4353                                                               QGLContext::InternalBindOption);
       
  4354         else
       
  4355             tex = d->device->context()->d_func()->bindTexture(scaled, GL_TEXTURE_2D, GL_RGBA,
       
  4356                                                               QGLContext::InternalBindOption);
       
  4357         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, d->use_smooth_pixmap_transform);
       
  4358 
       
  4359 #ifndef QT_OPENGL_ES
       
  4360         glPushAttrib(GL_CURRENT_BIT);
       
  4361         glDisable(GL_TEXTURE_GEN_S);
       
  4362 #endif
       
  4363         glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
       
  4364         glEnable(GL_TEXTURE_2D);
       
  4365 
       
  4366         GLdouble tc_w = r.width()/pm.width();
       
  4367         GLdouble tc_h = r.height()/pm.height();
       
  4368 
       
  4369         // Rotate the texture so that it is aligned correctly and the
       
  4370         // wrapping is done correctly
       
  4371         if (tex->options & QGLContext::InvertedYBindOption) {
       
  4372             glMatrixMode(GL_TEXTURE);
       
  4373             glPushMatrix();
       
  4374             glRotatef(180.0, 0.0, 1.0, 0.0);
       
  4375             glRotatef(180.0, 0.0, 0.0, 1.0);
       
  4376         }
       
  4377 
       
  4378         q_vertexType vertexArray[4*2];
       
  4379         q_vertexType texCoordArray[4*2];
       
  4380 
       
  4381         double offset_x = offset.x() / pm.width();
       
  4382         double offset_y = offset.y() / pm.height();
       
  4383 
       
  4384         qt_add_rect_to_array(r, vertexArray);
       
  4385         qt_add_texcoords_to_array(offset_x, offset_y,
       
  4386                                   tc_w + offset_x, tc_h + offset_y, texCoordArray);
       
  4387 
       
  4388         glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4389         glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4390 
       
  4391         glEnableClientState(GL_VERTEX_ARRAY);
       
  4392         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4393         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4394         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4395         glDisableClientState(GL_VERTEX_ARRAY);
       
  4396         if (tex->options & QGLContext::InvertedYBindOption)
       
  4397             glPopMatrix();
       
  4398 
       
  4399         glDisable(GL_TEXTURE_2D);
       
  4400 #ifndef QT_OPENGL_ES
       
  4401         glPopAttrib();
       
  4402 #endif
       
  4403     }
       
  4404 }
       
  4405 
       
  4406 void QOpenGLPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
       
  4407                                    Qt::ImageConversionFlags)
       
  4408 {
       
  4409     Q_D(QOpenGLPaintEngine);
       
  4410 
       
  4411     const int sz = d->max_texture_size;
       
  4412     if (image.width() > sz || image.height() > sz) {
       
  4413         QRectF subsr;
       
  4414         const QImage sub = qSubImage(image, sr, &subsr);
       
  4415 
       
  4416         if (sub.width() <= sz && sub.height() <= sz) {
       
  4417             drawImage(r, sub, subsr, 0);
       
  4418         } else {
       
  4419             const QImage scaled = sub.scaled(sz, sz, Qt::KeepAspectRatio);
       
  4420             const qreal sx = scaled.width() / qreal(sub.width());
       
  4421             const qreal sy = scaled.height() / qreal(sub.height());
       
  4422 
       
  4423             drawImage(r, scaled, scaleRect(subsr, sx, sy), 0);
       
  4424         }
       
  4425         return;
       
  4426     }
       
  4427 
       
  4428     if (d->composition_mode > QPainter::CompositionMode_Plus || (d->high_quality_antialiasing && !d->isFastRect(r)))
       
  4429         d->drawImageAsPath(r, image, sr);
       
  4430     else {
       
  4431         GLenum target = qt_gl_preferredTextureTarget();
       
  4432         d->flushDrawQueue();
       
  4433         QGLTexture *tex =
       
  4434             d->device->context()->d_func()->bindTexture(image, target, GL_RGBA,
       
  4435                                                         QGLContext::InternalBindOption);
       
  4436         drawTextureRect(image.width(), image.height(), r, sr, target, tex);
       
  4437     }
       
  4438 }
       
  4439 
       
  4440 void QOpenGLPaintEngine::drawTextureRect(int tx_width, int tx_height, const QRectF &r,
       
  4441                                          const QRectF &sr, GLenum target, QGLTexture *tex)
       
  4442 {
       
  4443     Q_D(QOpenGLPaintEngine);
       
  4444 #ifndef QT_OPENGL_ES
       
  4445     glPushAttrib(GL_CURRENT_BIT);
       
  4446     glDisable(GL_TEXTURE_GEN_S);
       
  4447 #endif
       
  4448     glColor4f(d->opacity, d->opacity, d->opacity, d->opacity);
       
  4449     glEnable(target);
       
  4450     updateTextureFilter(target, GL_CLAMP_TO_EDGE, d->use_smooth_pixmap_transform);
       
  4451 
       
  4452     qreal x1, x2, y1, y2;
       
  4453     if (target == GL_TEXTURE_2D) {
       
  4454         x1 = sr.x() / tx_width;
       
  4455         x2 = x1 + sr.width() / tx_width;
       
  4456         if (tex->options & QGLContext::InvertedYBindOption) {
       
  4457             y1 = 1 - (sr.bottom() / tx_height);
       
  4458             y2 = 1 - (sr.y() / tx_height);
       
  4459         } else {
       
  4460             y1 = sr.bottom() / tx_height;
       
  4461             y2 = sr.y() / tx_height;
       
  4462         }
       
  4463     } else {
       
  4464         x1 = sr.x();
       
  4465         x2 = sr.right();
       
  4466         y1 = sr.bottom();
       
  4467         y2 = sr.y();
       
  4468     }
       
  4469 
       
  4470     q_vertexType vertexArray[4*2];
       
  4471     q_vertexType texCoordArray[4*2];
       
  4472 
       
  4473     qt_add_rect_to_array(r, vertexArray);
       
  4474     qt_add_texcoords_to_array(x1, y2, x2, y1, texCoordArray);
       
  4475 
       
  4476     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4477     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4478 
       
  4479     glEnableClientState(GL_VERTEX_ARRAY);
       
  4480     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4481     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4482     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4483     glDisableClientState(GL_VERTEX_ARRAY);
       
  4484 
       
  4485     glDisable(target);
       
  4486 #ifndef QT_OPENGL_ES
       
  4487     glPopAttrib();
       
  4488 #endif
       
  4489 }
       
  4490 
       
  4491 #ifdef Q_WS_WIN
       
  4492 HDC
       
  4493 #else
       
  4494 Qt::HANDLE
       
  4495 #endif
       
  4496 QOpenGLPaintEngine::handle() const
       
  4497 {
       
  4498     return 0;
       
  4499 }
       
  4500 
       
  4501 static const int x_margin = 1;
       
  4502 static const int y_margin = 0;
       
  4503 
       
  4504 struct QGLGlyphCoord {
       
  4505     // stores the offset and size of a glyph texture
       
  4506     qreal x;
       
  4507     qreal y;
       
  4508     qreal width;
       
  4509     qreal height;
       
  4510     qreal log_width;
       
  4511     qreal log_height;
       
  4512     QFixed x_offset;
       
  4513     QFixed y_offset;
       
  4514 };
       
  4515 
       
  4516 struct QGLFontTexture {
       
  4517     int x_offset; // glyph offset within the
       
  4518     int y_offset;
       
  4519     GLuint texture;
       
  4520     int width;
       
  4521     int height;
       
  4522 };
       
  4523 
       
  4524 typedef QHash<glyph_t, QGLGlyphCoord*>  QGLGlyphHash;
       
  4525 typedef QHash<QFontEngine*, QGLGlyphHash*> QGLFontGlyphHash;
       
  4526 typedef QHash<quint64, QGLFontTexture*> QGLFontTexHash;
       
  4527 typedef QHash<const QGLContext*, QGLFontGlyphHash*> QGLContextHash;
       
  4528 
       
  4529 class QGLGlyphCache : public QObject
       
  4530 {
       
  4531     Q_OBJECT
       
  4532 public:
       
  4533     QGLGlyphCache() : QObject(0) { current_cache = 0; }
       
  4534     ~QGLGlyphCache();
       
  4535     QGLGlyphCoord *lookup(QFontEngine *, glyph_t);
       
  4536     void cacheGlyphs(QGLContext *, const QTextItemInt &, const QVarLengthArray<glyph_t> &);
       
  4537     void cleanCache();
       
  4538     void allocTexture(int width, int height, GLuint texture);
       
  4539 
       
  4540 public slots:
       
  4541     void cleanupContext(const QGLContext *);
       
  4542     void fontEngineDestroyed(QObject *);
       
  4543     void widgetDestroyed(QObject *);
       
  4544 
       
  4545 protected:
       
  4546     QGLGlyphHash *current_cache;
       
  4547     QGLFontTexHash qt_font_textures;
       
  4548     QGLContextHash qt_context_cache;
       
  4549 };
       
  4550 
       
  4551 QGLGlyphCache::~QGLGlyphCache()
       
  4552 {
       
  4553 //     qDebug() << "cleaning out the QGLGlyphCache";
       
  4554     cleanCache();
       
  4555 }
       
  4556 
       
  4557 void QGLGlyphCache::fontEngineDestroyed(QObject *o)
       
  4558 {
       
  4559 //     qDebug() << "fontEngineDestroyed()";
       
  4560     QFontEngine *fe = static_cast<QFontEngine *>(o); // safe, since only the type is used
       
  4561     QList<const QGLContext *> keys = qt_context_cache.keys();
       
  4562     const QGLContext *ctx = 0;
       
  4563 
       
  4564     for (int i=0; i < keys.size(); ++i) {
       
  4565         QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
       
  4566         if (font_cache->find(fe) != font_cache->end()) {
       
  4567             ctx = keys.at(i);
       
  4568             QGLGlyphHash *cache = font_cache->take(fe);
       
  4569             delete cache;
       
  4570             break;
       
  4571         }
       
  4572     }
       
  4573 
       
  4574     quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
       
  4575     QGLFontTexture *tex = qt_font_textures.take(font_key);
       
  4576     if (tex) {
       
  4577 #ifdef Q_WS_MAC
       
  4578         if (
       
  4579 #  ifndef QT_MAC_USE_COCOA
       
  4580             aglGetCurrentContext() != 0
       
  4581 #  else
       
  4582             qt_current_nsopengl_context() != 0
       
  4583 #  endif
       
  4584            )
       
  4585 #endif
       
  4586             glDeleteTextures(1, &tex->texture);
       
  4587         delete tex;
       
  4588     }
       
  4589 }
       
  4590 
       
  4591 void QGLGlyphCache::widgetDestroyed(QObject *)
       
  4592 {
       
  4593 //     qDebug() << "widget destroyed";
       
  4594     cleanCache(); // ###
       
  4595 }
       
  4596 
       
  4597 void QGLGlyphCache::cleanupContext(const QGLContext *ctx)
       
  4598 {
       
  4599 //     qDebug() << "==> cleaning for: " << hex << ctx;
       
  4600     QGLFontGlyphHash *font_cache = qt_context_cache.take(ctx);
       
  4601 
       
  4602     if (font_cache) {
       
  4603         QList<QFontEngine *> keys = font_cache->keys();
       
  4604         for (int i=0; i < keys.size(); ++i) {
       
  4605             QFontEngine *fe = keys.at(i);
       
  4606             delete font_cache->take(fe);
       
  4607             quint64 font_key = (reinterpret_cast<quint64>(ctx) << 32) | reinterpret_cast<quint64>(fe);
       
  4608             QGLFontTexture *font_tex = qt_font_textures.take(font_key);
       
  4609             if (font_tex) {
       
  4610 #ifdef Q_WS_MAC
       
  4611                 if (
       
  4612 #  ifndef QT_MAC_USE_COCOA
       
  4613             aglGetCurrentContext() == 0
       
  4614 #  else
       
  4615             qt_current_nsopengl_context() != 0
       
  4616 #  endif
       
  4617                    )
       
  4618 #endif
       
  4619                     glDeleteTextures(1, &font_tex->texture);
       
  4620                 delete font_tex;
       
  4621             }
       
  4622         }
       
  4623         delete font_cache;
       
  4624     }
       
  4625 //    qDebug() << "<=== done cleaning, num tex:" << qt_font_textures.size() << "num ctx:" << qt_context_cache.size();
       
  4626 }
       
  4627 
       
  4628 void QGLGlyphCache::cleanCache()
       
  4629 {
       
  4630     QGLFontTexHash::const_iterator it = qt_font_textures.constBegin();
       
  4631     if (QGLContext::currentContext()) {
       
  4632         while (it != qt_font_textures.constEnd()) {
       
  4633 #if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
       
  4634             if (qt_current_nsopengl_context() == 0)
       
  4635                 break;
       
  4636 #endif
       
  4637             glDeleteTextures(1, &it.value()->texture);
       
  4638             ++it;
       
  4639         }
       
  4640     }
       
  4641     qDeleteAll(qt_font_textures);
       
  4642     qt_font_textures.clear();
       
  4643 
       
  4644     QList<const QGLContext *> keys = qt_context_cache.keys();
       
  4645     for (int i=0; i < keys.size(); ++i) {
       
  4646         QGLFontGlyphHash *font_cache = qt_context_cache.value(keys.at(i));
       
  4647         qDeleteAll(*font_cache);
       
  4648         font_cache->clear();
       
  4649     }
       
  4650     qDeleteAll(qt_context_cache);
       
  4651     qt_context_cache.clear();
       
  4652 }
       
  4653 
       
  4654 void QGLGlyphCache::allocTexture(int width, int height, GLuint texture)
       
  4655 {
       
  4656     uchar *tex_data = (uchar *) malloc(width*height*2);
       
  4657     memset(tex_data, 0, width*height*2);
       
  4658     glBindTexture(GL_TEXTURE_2D, texture);
       
  4659 #ifndef QT_OPENGL_ES
       
  4660     glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8_ALPHA8,
       
  4661                  width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4662 #else
       
  4663     glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
       
  4664                  width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4665 #endif
       
  4666     free(tex_data);
       
  4667 }
       
  4668 
       
  4669 #if 0
       
  4670 // useful for debugging the glyph cache
       
  4671 static QImage getCurrentTexture(const QColor &color, QGLFontTexture *font_tex)
       
  4672 {
       
  4673     ushort *old_tex_data = (ushort *) malloc(font_tex->width*font_tex->height*2);
       
  4674     glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4675     QImage im(font_tex->width, font_tex->height, QImage::Format_ARGB32);
       
  4676     for (int y=0; y<font_tex->height; ++y) {
       
  4677         for (int x=0; x<font_tex->width; ++x) {
       
  4678             im.setPixel(x, y, ((*(old_tex_data+x+y*font_tex->width)) << 24) | (0x00ffffff & color.rgb()));
       
  4679         }
       
  4680     }
       
  4681     delete old_tex_data;
       
  4682     return im;
       
  4683 }
       
  4684 #endif
       
  4685 
       
  4686 void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti,
       
  4687                                 const QVarLengthArray<glyph_t> &glyphs)
       
  4688 {
       
  4689     QGLContextHash::const_iterator dev_it = qt_context_cache.constFind(context);
       
  4690     QGLFontGlyphHash *font_cache = 0;
       
  4691     const QGLContext *context_key = 0;
       
  4692 
       
  4693     if (dev_it == qt_context_cache.constEnd()) {
       
  4694         // check for shared contexts
       
  4695         QList<const QGLContext *> contexts = qt_context_cache.keys();
       
  4696         for (int i=0; i<contexts.size(); ++i) {
       
  4697             const QGLContext *ctx = contexts.at(i);
       
  4698             if (ctx != context && QGLContext::areSharing(context, ctx)) {
       
  4699                 context_key = ctx;
       
  4700                 dev_it = qt_context_cache.constFind(context_key);
       
  4701                 break;
       
  4702             }
       
  4703         }
       
  4704     }
       
  4705 
       
  4706     if (dev_it == qt_context_cache.constEnd()) {
       
  4707         // no shared contexts either - create a new entry
       
  4708         font_cache = new QGLFontGlyphHash;
       
  4709 //         qDebug() << "new context" << context << font_cache;
       
  4710         qt_context_cache.insert(context, font_cache);
       
  4711         if (context->isValid() && context->device()->devType() == QInternal::Widget) {
       
  4712             QWidget *widget = static_cast<QWidget *>(context->device());
       
  4713             connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
       
  4714             connect(QGLSignalProxy::instance(),
       
  4715                     SIGNAL(aboutToDestroyContext(const QGLContext *)),
       
  4716                     SLOT(cleanupContext(const QGLContext *)));
       
  4717         }
       
  4718     } else {
       
  4719         font_cache = dev_it.value();
       
  4720     }
       
  4721     Q_ASSERT(font_cache != 0);
       
  4722 
       
  4723     QGLFontGlyphHash::const_iterator cache_it = font_cache->constFind(ti.fontEngine);
       
  4724     QGLGlyphHash *cache = 0;
       
  4725     if (cache_it == font_cache->constEnd()) {
       
  4726         cache = new QGLGlyphHash;
       
  4727         font_cache->insert(ti.fontEngine, cache);
       
  4728         connect(ti.fontEngine, SIGNAL(destroyed(QObject*)), SLOT(fontEngineDestroyed(QObject*)));
       
  4729     } else {
       
  4730         cache = cache_it.value();
       
  4731     }
       
  4732     current_cache = cache;
       
  4733 
       
  4734     quint64 font_key = (reinterpret_cast<quint64>(context_key ? context_key : context) << 32)
       
  4735                        | reinterpret_cast<quint64>(ti.fontEngine);
       
  4736     QGLFontTexHash::const_iterator it = qt_font_textures.constFind(font_key);
       
  4737     QGLFontTexture *font_tex;
       
  4738     if (it == qt_font_textures.constEnd()) {
       
  4739         GLuint font_texture;
       
  4740         glGenTextures(1, &font_texture);
       
  4741         GLint tex_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
       
  4742         GLint tex_width = qt_next_power_of_two(tex_height*30); // ###
       
  4743         GLint max_tex_size;
       
  4744         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
       
  4745         Q_ASSERT(max_tex_size > 0);
       
  4746         if (tex_width > max_tex_size)
       
  4747             tex_width = max_tex_size;
       
  4748         allocTexture(tex_width, tex_height, font_texture);
       
  4749         font_tex = new QGLFontTexture;
       
  4750         font_tex->texture = font_texture;
       
  4751         font_tex->x_offset = x_margin;
       
  4752         font_tex->y_offset = y_margin;
       
  4753         font_tex->width = tex_width;
       
  4754         font_tex->height = tex_height;
       
  4755 //         qDebug() << "new font tex - width:" << tex_width << "height:"<< tex_height
       
  4756 //                  << hex << "tex id:" << font_tex->texture << "key:" << font_key << "num cached:" << qt_font_textures.size();
       
  4757         qt_font_textures.insert(font_key, font_tex);
       
  4758     } else {
       
  4759         font_tex = it.value();
       
  4760         glBindTexture(GL_TEXTURE_2D, font_tex->texture);
       
  4761     }
       
  4762 
       
  4763     for (int i=0; i< glyphs.size(); ++i) {
       
  4764         QGLGlyphHash::const_iterator it = cache->constFind(glyphs[i]);
       
  4765         if (it == cache->constEnd()) {
       
  4766             // render new glyph and put it in the cache
       
  4767             glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]);
       
  4768             int glyph_width = qRound(metrics.width.toReal())+2;
       
  4769             int glyph_height = qRound(ti.ascent.toReal() + ti.descent.toReal())+2;
       
  4770 
       
  4771             if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
       
  4772                 int strip_height = qt_next_power_of_two(qRound(ti.ascent.toReal() + ti.descent.toReal())+2);
       
  4773                 font_tex->x_offset = x_margin;
       
  4774                 font_tex->y_offset += strip_height;
       
  4775                 if (font_tex->y_offset >= font_tex->height) {
       
  4776                     // get hold of the old font texture
       
  4777                     uchar *old_tex_data = (uchar *) malloc(font_tex->width*font_tex->height*2);
       
  4778                     int old_tex_height = font_tex->height;
       
  4779 #ifndef QT_OPENGL_ES
       
  4780                     glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4781 #endif
       
  4782 
       
  4783                     // realloc a larger texture
       
  4784                     glDeleteTextures(1, &font_tex->texture);
       
  4785                     glGenTextures(1, &font_tex->texture);
       
  4786                     font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
       
  4787                     allocTexture(font_tex->width, font_tex->height, font_tex->texture);
       
  4788 
       
  4789                     // write back the old texture data
       
  4790                     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, font_tex->width, old_tex_height,
       
  4791                                     GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, old_tex_data);
       
  4792                     free(old_tex_data);
       
  4793 
       
  4794                     // update the texture coords and the y offset for the existing glyphs in
       
  4795                     // the cache, because of the texture size change
       
  4796                     QGLGlyphHash::iterator it = cache->begin();
       
  4797                     while (it != cache->end()) {
       
  4798                         it.value()->height = (it.value()->height * old_tex_height) / font_tex->height;
       
  4799                         it.value()->y = (it.value()->y * old_tex_height) / font_tex->height;
       
  4800                         ++it;
       
  4801                     }
       
  4802                 }
       
  4803             }
       
  4804 
       
  4805             QImage glyph_im(ti.fontEngine->alphaMapForGlyph(glyphs[i]));
       
  4806             glyph_im = glyph_im.convertToFormat(QImage::Format_Indexed8);
       
  4807             glyph_width = glyph_im.width();
       
  4808             Q_ASSERT(glyph_width >= 0);
       
  4809             // pad the glyph width to an even number
       
  4810             if (glyph_width%2 != 0)
       
  4811                 ++glyph_width;
       
  4812 
       
  4813             QGLGlyphCoord *qgl_glyph = new QGLGlyphCoord;
       
  4814             qgl_glyph->x = qreal(font_tex->x_offset) / font_tex->width;
       
  4815             qgl_glyph->y = qreal(font_tex->y_offset) / font_tex->height;
       
  4816             qgl_glyph->width = qreal(glyph_width) / font_tex->width;
       
  4817             qgl_glyph->height = qreal(glyph_height) / font_tex->height;
       
  4818             qgl_glyph->log_width = qreal(glyph_width);
       
  4819             qgl_glyph->log_height = qgl_glyph->height * font_tex->height;
       
  4820 #ifdef Q_WS_MAC
       
  4821             qgl_glyph->x_offset = -metrics.x + 1;
       
  4822             qgl_glyph->y_offset = metrics.y - 2;
       
  4823 #else
       
  4824             qgl_glyph->x_offset = -metrics.x;
       
  4825             qgl_glyph->y_offset = metrics.y;
       
  4826 #endif
       
  4827 
       
  4828             if (!glyph_im.isNull()) {
       
  4829 
       
  4830                 int idx = 0;
       
  4831                 uchar *tex_data = (uchar *) malloc(glyph_width*glyph_im.height()*2);
       
  4832                 memset(tex_data, 0, glyph_width*glyph_im.height()*2);
       
  4833 
       
  4834                 for (int y=0; y<glyph_im.height(); ++y) {
       
  4835                     uchar *s = (uchar *) glyph_im.scanLine(y);
       
  4836                     for (int x=0; x<glyph_im.width(); ++x) {
       
  4837                         uchar alpha = qAlpha(glyph_im.color(*s));
       
  4838                         tex_data[idx] = alpha;
       
  4839                         tex_data[idx+1] = alpha;
       
  4840                         ++s;
       
  4841                         idx += 2;
       
  4842                     }
       
  4843                     if (glyph_im.width()%2 != 0)
       
  4844                         idx += 2;
       
  4845                 }
       
  4846                 glTexSubImage2D(GL_TEXTURE_2D, 0, font_tex->x_offset, font_tex->y_offset,
       
  4847                                 glyph_width, glyph_im.height(),
       
  4848                                 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tex_data);
       
  4849                 free(tex_data);
       
  4850             }
       
  4851             if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
       
  4852                 font_tex->x_offset = x_margin;
       
  4853                 font_tex->y_offset += glyph_height + y_margin;
       
  4854             } else {
       
  4855                 font_tex->x_offset += glyph_width + x_margin;
       
  4856             }
       
  4857 
       
  4858             cache->insert(glyphs[i], qgl_glyph);
       
  4859         }
       
  4860     }
       
  4861 }
       
  4862 
       
  4863 QGLGlyphCoord *QGLGlyphCache::lookup(QFontEngine *, glyph_t g)
       
  4864 {
       
  4865     Q_ASSERT(current_cache != 0);
       
  4866     // ### careful here
       
  4867     QGLGlyphHash::const_iterator it = current_cache->constFind(g);
       
  4868     if (it == current_cache->constEnd())
       
  4869         return 0;
       
  4870     else
       
  4871         return it.value();
       
  4872 }
       
  4873 
       
  4874 Q_GLOBAL_STATIC(QGLGlyphCache, qt_glyph_cache)
       
  4875 
       
  4876 //
       
  4877 // assumption: the context that this is called for has to be the
       
  4878 // current context
       
  4879 //
       
  4880 void qgl_cleanup_glyph_cache(QGLContext *ctx)
       
  4881 {
       
  4882     qt_glyph_cache()->cleanupContext(ctx);
       
  4883 }
       
  4884 
       
  4885 void QOpenGLPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
       
  4886 {
       
  4887     Q_D(QOpenGLPaintEngine);
       
  4888 
       
  4889     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
       
  4890 
       
  4891     // fall back to drawing a polygon if the scale factor is large, or
       
  4892     // we use a gradient pen
       
  4893     if ((d->matrix.det() > 1) || (d->pen_brush_style >= Qt::LinearGradientPattern
       
  4894                                   && d->pen_brush_style <= Qt::ConicalGradientPattern)) {
       
  4895         QPaintEngine::drawTextItem(p, textItem);
       
  4896         return;
       
  4897     }
       
  4898 
       
  4899     d->flushDrawQueue();
       
  4900 
       
  4901     // add the glyphs used to the glyph texture cache
       
  4902     QVarLengthArray<QFixedPoint> positions;
       
  4903     QVarLengthArray<glyph_t> glyphs;
       
  4904     QTransform matrix = QTransform::fromTranslate(qRound(p.x()), qRound(p.y()));
       
  4905     ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
       
  4906 
       
  4907     // make sure the glyphs we want to draw are in the cache
       
  4908     qt_glyph_cache()->cacheGlyphs(d->device->context(), ti, glyphs);
       
  4909 
       
  4910     d->setGradientOps(Qt::SolidPattern, QRectF()); // turns off gradient ops
       
  4911     qt_glColor4ubv(d->pen_color);
       
  4912     glEnable(GL_TEXTURE_2D);
       
  4913 
       
  4914 #ifdef Q_WS_QWS
       
  4915     // XXX: it is necessary to disable alpha writes on GLES/embedded because we don't want
       
  4916     // text rendering to update the alpha in the window surface.
       
  4917     // XXX: This may not be needed as this behavior does seem to be caused by driver bug
       
  4918     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
       
  4919 #endif
       
  4920 
       
  4921     // do the actual drawing
       
  4922     q_vertexType vertexArray[4*2];
       
  4923     q_vertexType texCoordArray[4*2];
       
  4924 
       
  4925     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  4926     glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
       
  4927 
       
  4928     glEnableClientState(GL_VERTEX_ARRAY);
       
  4929     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4930     bool antialias = !(ti.fontEngine->fontDef.styleStrategy & QFont::NoAntialias);
       
  4931     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
       
  4932     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, antialias ? GL_LINEAR : GL_NEAREST);
       
  4933 
       
  4934     for (int i=0; i< glyphs.size(); ++i) {
       
  4935         QGLGlyphCoord *g = qt_glyph_cache()->lookup(ti.fontEngine, glyphs[i]);
       
  4936 
       
  4937         // we don't cache glyphs with no width/height
       
  4938         if (!g)
       
  4939             continue;
       
  4940 
       
  4941         qreal x1, x2, y1, y2;
       
  4942         x1 = g->x;
       
  4943         y1 = g->y;
       
  4944         x2 = x1 + g->width;
       
  4945         y2 = y1 + g->height;
       
  4946 
       
  4947         QPointF logical_pos((positions[i].x - g->x_offset).toReal(),
       
  4948                             (positions[i].y + g->y_offset).toReal());
       
  4949 
       
  4950         qt_add_rect_to_array(QRectF(logical_pos, QSizeF(g->log_width, g->log_height)), vertexArray);
       
  4951         qt_add_texcoords_to_array(x1, y1, x2, y2, texCoordArray);
       
  4952 
       
  4953         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
       
  4954     }
       
  4955 
       
  4956     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
       
  4957     glDisableClientState(GL_VERTEX_ARRAY);
       
  4958 
       
  4959     glDisable(GL_TEXTURE_2D);
       
  4960 
       
  4961 #ifdef Q_WS_QWS
       
  4962     // XXX: This may not be needed as this behavior does seem to be caused by driver bug
       
  4963     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
       
  4964 #endif
       
  4965 }
       
  4966 
       
  4967 
       
  4968 void QOpenGLPaintEngine::drawEllipse(const QRectF &rect)
       
  4969 {
       
  4970 #ifndef Q_WS_QWS
       
  4971     Q_D(QOpenGLPaintEngine);
       
  4972 
       
  4973     if (d->use_emulation) {
       
  4974         QPaintEngineEx::drawEllipse(rect);
       
  4975         return;
       
  4976     }
       
  4977 
       
  4978     if (d->high_quality_antialiasing) {
       
  4979         if (d->has_brush) {
       
  4980             d->disableClipping();
       
  4981 
       
  4982             glMatrixMode(GL_MODELVIEW);
       
  4983             glPushMatrix();
       
  4984             glLoadIdentity();
       
  4985 
       
  4986             GLuint program = qt_gl_program_cache()->getProgram(d->device->context(),
       
  4987                                                                FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, 0, true);
       
  4988             QGLEllipseMaskGenerator maskGenerator(rect,
       
  4989                                                   d->matrix,
       
  4990                                                   d->offscreen,
       
  4991                                                   program,
       
  4992                                                   mask_variable_locations[FRAGMENT_PROGRAM_MASK_ELLIPSE_AA]);
       
  4993 
       
  4994             d->addItem(qt_mask_texture_cache()->getMask(maskGenerator, d));
       
  4995 
       
  4996             d->enableClipping();
       
  4997 
       
  4998             glMatrixMode(GL_MODELVIEW);
       
  4999             glPopMatrix();
       
  5000         }
       
  5001 
       
  5002         if (d->has_pen) {
       
  5003             QPainterPath path;
       
  5004             path.addEllipse(rect);
       
  5005 
       
  5006             d->strokePath(path, false);
       
  5007         }
       
  5008     } else {
       
  5009         DEBUG_ONCE_STR("QOpenGLPaintEngine::drawEllipse(): falling back to drawPath()");
       
  5010 
       
  5011         QPainterPath path;
       
  5012         path.addEllipse(rect);
       
  5013         drawPath(path);
       
  5014     }
       
  5015 #else
       
  5016     QPaintEngineEx::drawEllipse(rect);
       
  5017 #endif
       
  5018 }
       
  5019 
       
  5020 
       
  5021 void QOpenGLPaintEnginePrivate::updateFragmentProgramData(int locations[])
       
  5022 {
       
  5023 #ifdef Q_WS_QWS
       
  5024     Q_UNUSED(locations);
       
  5025 #else
       
  5026     QGL_FUNC_CONTEXT;
       
  5027 
       
  5028     QSize sz = offscreen.offscreenSize();
       
  5029 
       
  5030     float inv_mask_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
       
  5031 
       
  5032     sz = drawable_texture_size;
       
  5033 
       
  5034     float inv_dst_size_data[4] = { 1.0f / sz.width(), 1.0f / sz.height(), 0.0f, 0.0f };
       
  5035 
       
  5036     // default inv size 0.125f == 1.0f / 8.0f for pattern brushes
       
  5037     float inv_brush_texture_size_data[4] = { 0.125f, 0.125f };
       
  5038 
       
  5039     // texture patterns have their own size
       
  5040     if (current_style == Qt::TexturePattern) {
       
  5041         QSize sz = cbrush.texture().size();
       
  5042 
       
  5043         inv_brush_texture_size_data[0] = 1.0f / sz.width();
       
  5044         inv_brush_texture_size_data[1] = 1.0f / sz.height();
       
  5045     }
       
  5046 
       
  5047     for (unsigned int i = 0; i < num_fragment_variables; ++i) {
       
  5048         int location = locations[i];
       
  5049 
       
  5050         if (location < 0)
       
  5051             continue;
       
  5052 
       
  5053         switch (i) {
       
  5054         case VAR_ANGLE:
       
  5055             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, angle_data);
       
  5056             break;
       
  5057         case VAR_LINEAR:
       
  5058             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, linear_data);
       
  5059             break;
       
  5060         case VAR_FMP:
       
  5061             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp_data);
       
  5062             break;
       
  5063         case VAR_FMP2_M_RADIUS2:
       
  5064             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, fmp2_m_radius2_data);
       
  5065             break;
       
  5066         case VAR_INV_MASK_SIZE:
       
  5067             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_mask_size_data);
       
  5068             break;
       
  5069         case VAR_INV_DST_SIZE:
       
  5070             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_dst_size_data);
       
  5071             break;
       
  5072         case VAR_INV_MATRIX_M0:
       
  5073             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[0]);
       
  5074             break;
       
  5075         case VAR_INV_MATRIX_M1:
       
  5076             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[1]);
       
  5077             break;
       
  5078         case VAR_INV_MATRIX_M2:
       
  5079             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_matrix_data[2]);
       
  5080             break;
       
  5081         case VAR_PORTERDUFF_AB:
       
  5082             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_ab_data);
       
  5083             break;
       
  5084         case VAR_PORTERDUFF_XYZ:
       
  5085             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, porterduff_xyz_data);
       
  5086             break;
       
  5087         case VAR_INV_BRUSH_TEXTURE_SIZE:
       
  5088             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, inv_brush_texture_size_data);
       
  5089             break;
       
  5090         case VAR_MASK_OFFSET:
       
  5091             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_offset_data);
       
  5092             break;
       
  5093         case VAR_MASK_CHANNEL:
       
  5094             glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, location, mask_channel_data);
       
  5095             break;
       
  5096         case VAR_DST_TEXTURE:
       
  5097         case VAR_MASK_TEXTURE:
       
  5098         case VAR_PALETTE:
       
  5099         case VAR_BRUSH_TEXTURE:
       
  5100             // texture variables, not handled here
       
  5101             break;
       
  5102         default:
       
  5103             qDebug() << "QOpenGLPaintEnginePrivate: Unhandled fragment variable:" << i;
       
  5104         }
       
  5105     }
       
  5106 #endif
       
  5107 }
       
  5108 
       
  5109 
       
  5110 void QOpenGLPaintEnginePrivate::copyDrawable(const QRectF &rect)
       
  5111 {
       
  5112 #ifdef Q_WS_QWS
       
  5113     Q_UNUSED(rect);
       
  5114 #else
       
  5115     ensureDrawableTexture();
       
  5116 
       
  5117     DEBUG_ONCE qDebug() << "Refreshing drawable_texture for rectangle" << rect;
       
  5118     QRectF screen_rect = rect.adjusted(-1, -1, 1, 1);
       
  5119 
       
  5120     int left = qMax(0, static_cast<int>(screen_rect.left()));
       
  5121     int width = qMin(device->size().width() - left, static_cast<int>(screen_rect.width()) + 1);
       
  5122 
       
  5123     int bottom = qMax(0, static_cast<int>(device->size().height() - screen_rect.bottom()));
       
  5124     int height = qMin(device->size().height() - bottom, static_cast<int>(screen_rect.height()) + 1);
       
  5125 
       
  5126     glBindTexture(GL_TEXTURE_2D, drawable_texture);
       
  5127     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, left, bottom, width, height);
       
  5128 #endif
       
  5129 }
       
  5130 
       
  5131 
       
  5132 void QOpenGLPaintEnginePrivate::composite(const QRectF &rect, const QPoint &maskOffset)
       
  5133 {
       
  5134 #ifdef Q_WS_QWS
       
  5135     Q_UNUSED(rect);
       
  5136     Q_UNUSED(maskOffset);
       
  5137 #else
       
  5138     q_vertexType vertexArray[8];
       
  5139     qt_add_rect_to_array(rect, vertexArray);
       
  5140 
       
  5141     composite(GL_TRIANGLE_FAN, vertexArray, 4, maskOffset);
       
  5142 #endif
       
  5143 }
       
  5144 
       
  5145 
       
  5146 void QOpenGLPaintEnginePrivate::composite(GLuint primitive, const q_vertexType *vertexArray, int vertexCount, const QPoint &maskOffset)
       
  5147 {
       
  5148 #ifdef QT_OPENGL_ES
       
  5149     Q_UNUSED(primitive);
       
  5150     Q_UNUSED(vertexArray);
       
  5151     Q_UNUSED(vertexCount);
       
  5152     Q_UNUSED(maskOffset);
       
  5153 #else
       
  5154     Q_Q(QOpenGLPaintEngine);
       
  5155     QGL_FUNC_CONTEXT;
       
  5156 
       
  5157     if (current_style == Qt::NoBrush)
       
  5158         return;
       
  5159 
       
  5160     DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Using compositing program: fragment_brush ="
       
  5161                         << fragment_brush << ", fragment_composition_mode =" << fragment_composition_mode;
       
  5162 
       
  5163     if (has_fast_composition_mode)
       
  5164         q->updateCompositionMode(composition_mode);
       
  5165     else {
       
  5166         qreal minX = 1e9, minY = 1e9, maxX = -1e9, maxY = -1e9;
       
  5167 
       
  5168         for (int i = 0; i < vertexCount; ++i) {
       
  5169             qreal x = vt2f(vertexArray[2 * i]);
       
  5170             qreal y = vt2f(vertexArray[2 * i + 1]);
       
  5171 
       
  5172             qreal tx, ty;
       
  5173             matrix.map(x, y, &tx, &ty);
       
  5174 
       
  5175             minX = qMin(minX, tx);
       
  5176             minY = qMin(minY, ty);
       
  5177             maxX = qMax(maxX, tx);
       
  5178             maxY = qMax(maxY, ty);
       
  5179         }
       
  5180 
       
  5181         QRectF r(minX, minY, maxX - minX, maxY - minY);
       
  5182         copyDrawable(r);
       
  5183 
       
  5184         glBlendFunc(GL_ONE, GL_ZERO);
       
  5185     }
       
  5186 
       
  5187     int *locations = painter_variable_locations[fragment_brush][fragment_composition_mode];
       
  5188 
       
  5189     int texture_locations[] = { locations[VAR_DST_TEXTURE],
       
  5190                                 locations[VAR_MASK_TEXTURE],
       
  5191                                 locations[VAR_PALETTE] };
       
  5192 
       
  5193     int brush_texture_location = locations[VAR_BRUSH_TEXTURE];
       
  5194 
       
  5195     GLuint texture_targets[] = { GL_TEXTURE_2D,
       
  5196                                  GL_TEXTURE_2D,
       
  5197                                  GL_TEXTURE_1D };
       
  5198 
       
  5199     GLuint textures[] = { drawable_texture,
       
  5200                           offscreen.offscreenTexture(),
       
  5201                           grad_palette };
       
  5202 
       
  5203     const int num_textures = sizeof(textures) / sizeof(*textures);
       
  5204 
       
  5205     Q_ASSERT(num_textures == sizeof(texture_locations) / sizeof(*texture_locations));
       
  5206     Q_ASSERT(num_textures == sizeof(texture_targets) / sizeof(*texture_targets));
       
  5207 
       
  5208     for (int i = 0; i < num_textures; ++i)
       
  5209         if (texture_locations[i] >= 0) {
       
  5210             glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
       
  5211             glBindTexture(texture_targets[i], textures[i]);
       
  5212         }
       
  5213 
       
  5214     if (brush_texture_location >= 0) {
       
  5215         glActiveTexture(GL_TEXTURE0 + brush_texture_location);
       
  5216 
       
  5217         if (current_style == Qt::TexturePattern)
       
  5218             device->context()->d_func()->bindTexture(cbrush.textureImage(), GL_TEXTURE_2D, GL_RGBA,
       
  5219                                                      QGLContext::InternalBindOption);
       
  5220         else
       
  5221             device->context()->d_func()->bindTexture(qt_imageForBrush(current_style, false),
       
  5222                                                      GL_TEXTURE_2D, GL_RGBA,
       
  5223                                                      QGLContext::InternalBindOption);
       
  5224 
       
  5225         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, use_smooth_pixmap_transform);
       
  5226     }
       
  5227 
       
  5228     glEnableClientState(GL_VERTEX_ARRAY);
       
  5229     glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
       
  5230     glEnable(GL_FRAGMENT_PROGRAM_ARB);
       
  5231     GLuint program = qt_gl_program_cache()->getProgram(device->context(),
       
  5232                                                        fragment_brush,
       
  5233                                                        fragment_composition_mode, false);
       
  5234     glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program);
       
  5235 
       
  5236     mask_offset_data[0] = maskOffset.x();
       
  5237     mask_offset_data[1] = -maskOffset.y();
       
  5238 
       
  5239     updateFragmentProgramData(locations);
       
  5240 
       
  5241     glDrawArrays(primitive, 0, vertexCount);
       
  5242 
       
  5243     glDisable(GL_FRAGMENT_PROGRAM_ARB);
       
  5244     glDisableClientState(GL_VERTEX_ARRAY);
       
  5245 
       
  5246     for (int i = 0; i < num_textures; ++i)
       
  5247         if (texture_locations[i] >= 0) {
       
  5248             glActiveTexture(GL_TEXTURE0 + texture_locations[i]);
       
  5249             glBindTexture(texture_targets[i], 0);
       
  5250         }
       
  5251 
       
  5252     if (brush_texture_location >= 0) {
       
  5253         glActiveTexture(GL_TEXTURE0 + brush_texture_location);
       
  5254         glBindTexture(GL_TEXTURE_2D, 0);
       
  5255     }
       
  5256 
       
  5257     glActiveTexture(GL_TEXTURE0);
       
  5258 
       
  5259     if (!has_fast_composition_mode)
       
  5260         q->updateCompositionMode(composition_mode);
       
  5261 #endif
       
  5262 }
       
  5263 
       
  5264 void QOpenGLPaintEnginePrivate::cacheItemErased(int channel, const QRect &rect)
       
  5265 {
       
  5266     bool isInDrawQueue = false;
       
  5267 
       
  5268     foreach (const QDrawQueueItem &item, drawQueue) {
       
  5269         if (item.location.channel == channel && item.location.rect == rect) {
       
  5270             isInDrawQueue = true;
       
  5271             break;
       
  5272         }
       
  5273     }
       
  5274 
       
  5275     if (isInDrawQueue)
       
  5276         flushDrawQueue();
       
  5277 }
       
  5278 
       
  5279 void QOpenGLPaintEnginePrivate::addItem(const QGLMaskTextureCache::CacheLocation &location)
       
  5280 {
       
  5281     drawQueue << QDrawQueueItem(opacity, cbrush, brush_origin, composition_mode, matrix, location);
       
  5282 }
       
  5283 
       
  5284 void QOpenGLPaintEnginePrivate::drawItem(const QDrawQueueItem &item)
       
  5285 {
       
  5286     Q_Q(QOpenGLPaintEngine);
       
  5287 
       
  5288     opacity = item.opacity;
       
  5289     brush_origin = item.brush_origin;
       
  5290     q->updateCompositionMode(item.composition_mode);
       
  5291     matrix = item.matrix;
       
  5292     cbrush = item.brush;
       
  5293     brush_style = item.brush.style();
       
  5294 
       
  5295     mask_channel_data[0] = item.location.channel == 0;
       
  5296     mask_channel_data[1] = item.location.channel == 1;
       
  5297     mask_channel_data[2] = item.location.channel == 2;
       
  5298     mask_channel_data[3] = item.location.channel == 3;
       
  5299 
       
  5300     setGradientOps(item.brush, item.location.screen_rect);
       
  5301 
       
  5302     composite(item.location.screen_rect, item.location.rect.topLeft() - item.location.screen_rect.topLeft()
       
  5303                                          - QPoint(0, offscreen.offscreenSize().height() - device->size().height()));
       
  5304 }
       
  5305 
       
  5306 void QOpenGLPaintEnginePrivate::flushDrawQueue()
       
  5307 {
       
  5308 #ifndef QT_OPENGL_ES
       
  5309     Q_Q(QOpenGLPaintEngine);
       
  5310 
       
  5311     offscreen.release();
       
  5312 
       
  5313     if (!drawQueue.isEmpty()) {
       
  5314         DEBUG_ONCE qDebug() << "QOpenGLPaintEngine::flushDrawQueue():" << drawQueue.size() << "items";
       
  5315 
       
  5316         glPushMatrix();
       
  5317         glLoadIdentity();
       
  5318         qreal old_opacity = opacity;
       
  5319         QPointF old_brush_origin = brush_origin;
       
  5320         QPainter::CompositionMode old_composition_mode = composition_mode;
       
  5321         QTransform old_matrix = matrix;
       
  5322         QBrush old_brush = cbrush;
       
  5323 
       
  5324         bool hqaa_old = high_quality_antialiasing;
       
  5325 
       
  5326         high_quality_antialiasing = true;
       
  5327 
       
  5328         foreach (const QDrawQueueItem &item, drawQueue)
       
  5329             drawItem(item);
       
  5330 
       
  5331         opacity = old_opacity;
       
  5332         brush_origin = old_brush_origin;
       
  5333         q->updateCompositionMode(old_composition_mode);
       
  5334         matrix = old_matrix;
       
  5335         cbrush = old_brush;
       
  5336         brush_style = old_brush.style();
       
  5337 
       
  5338         high_quality_antialiasing = hqaa_old;
       
  5339 
       
  5340         setGLBrush(old_brush.color());
       
  5341         qt_glColor4ubv(brush_color);
       
  5342 
       
  5343         drawQueue.clear();
       
  5344 
       
  5345         glPopMatrix();
       
  5346     }
       
  5347 #endif
       
  5348 }
       
  5349 
       
  5350 void QOpenGLPaintEngine::clipEnabledChanged()
       
  5351 {
       
  5352     Q_D(QOpenGLPaintEngine);
       
  5353 
       
  5354     d->updateDepthClip();
       
  5355 }
       
  5356 
       
  5357 void QOpenGLPaintEngine::penChanged()
       
  5358 {
       
  5359     updatePen(state()->pen);
       
  5360 }
       
  5361 
       
  5362 void QOpenGLPaintEngine::brushChanged()
       
  5363 {
       
  5364     updateBrush(state()->brush, state()->brushOrigin);
       
  5365 }
       
  5366 
       
  5367 void QOpenGLPaintEngine::brushOriginChanged()
       
  5368 {
       
  5369     updateBrush(state()->brush, state()->brushOrigin);
       
  5370 }
       
  5371 
       
  5372 void QOpenGLPaintEngine::opacityChanged()
       
  5373 {
       
  5374     Q_D(QOpenGLPaintEngine);
       
  5375     QPainterState *s = state();
       
  5376     d->opacity = s->opacity;
       
  5377     updateBrush(s->brush, s->brushOrigin);
       
  5378     updatePen(s->pen);
       
  5379 }
       
  5380 
       
  5381 void QOpenGLPaintEngine::compositionModeChanged()
       
  5382 {
       
  5383     updateCompositionMode(state()->composition_mode);
       
  5384 }
       
  5385 
       
  5386 void QOpenGLPaintEngine::renderHintsChanged()
       
  5387 {
       
  5388     updateRenderHints(state()->renderHints);
       
  5389 }
       
  5390 
       
  5391 void QOpenGLPaintEngine::transformChanged()
       
  5392 {
       
  5393     updateMatrix(state()->matrix);
       
  5394 }
       
  5395 
       
  5396 static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
       
  5397 {
       
  5398     const qreal *points = path.points();
       
  5399     const QPainterPath::ElementType *types = path.elements();
       
  5400 
       
  5401     QPainterPath p;
       
  5402     if (types) {
       
  5403         int id = 0;
       
  5404         for (int i=0; i<path.elementCount(); ++i) {
       
  5405             switch(types[i]) {
       
  5406             case QPainterPath::MoveToElement:
       
  5407                 p.moveTo(QPointF(points[id], points[id+1]));
       
  5408                 id+=2;
       
  5409                 break;
       
  5410             case QPainterPath::LineToElement:
       
  5411                 p.lineTo(QPointF(points[id], points[id+1]));
       
  5412                 id+=2;
       
  5413                 break;
       
  5414             case QPainterPath::CurveToElement: {
       
  5415                 QPointF p1(points[id], points[id+1]);
       
  5416                 QPointF p2(points[id+2], points[id+3]);
       
  5417                 QPointF p3(points[id+4], points[id+5]);
       
  5418                 p.cubicTo(p1, p2, p3);
       
  5419                 id+=6;
       
  5420                 break;
       
  5421             }
       
  5422             case QPainterPath::CurveToDataElement:
       
  5423                 ;
       
  5424                 break;
       
  5425             }
       
  5426         }
       
  5427     } else {
       
  5428         p.moveTo(QPointF(points[0], points[1]));
       
  5429         int id = 2;
       
  5430         for (int i=1; i<path.elementCount(); ++i) {
       
  5431             p.lineTo(QPointF(points[id], points[id+1]));
       
  5432             id+=2;
       
  5433         }
       
  5434     }
       
  5435     if (path.hints() & QVectorPath::WindingFill)
       
  5436         p.setFillRule(Qt::WindingFill);
       
  5437 
       
  5438     return p;
       
  5439 }
       
  5440 
       
  5441 void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
       
  5442 {
       
  5443     Q_D(QOpenGLPaintEngine);
       
  5444 
       
  5445     if (brush.style() == Qt::NoBrush)
       
  5446         return;
       
  5447 
       
  5448     if (!d->use_fragment_programs && needsEmulation(brush.style())) {
       
  5449         QPainter *p = painter();
       
  5450         QBrush oldBrush = p->brush();
       
  5451         p->setBrush(brush);
       
  5452         qt_draw_helper(p->d_ptr.data(), painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
       
  5453         p->setBrush(oldBrush);
       
  5454         return;
       
  5455     }
       
  5456 
       
  5457     QBrush old_brush = state()->brush;
       
  5458     updateBrush(brush, state()->brushOrigin);
       
  5459 
       
  5460     const qreal *points = path.points();
       
  5461     const QPainterPath::ElementType *types = path.elements();
       
  5462     if (!types && path.shape() == QVectorPath::RectangleHint) {
       
  5463         QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
       
  5464         QPen old_pen = state()->pen;
       
  5465         updatePen(Qt::NoPen);
       
  5466         drawRects(&r, 1);
       
  5467         updatePen(old_pen);
       
  5468     } else {
       
  5469         d->fillPath(painterPathFromVectorPath(path));
       
  5470     }
       
  5471 
       
  5472     updateBrush(old_brush, state()->brushOrigin);
       
  5473 }
       
  5474 
       
  5475 template <typename T> static inline bool isRect(const T *pts, int elementCount) {
       
  5476     return (elementCount == 5 // 5-point polygon, check for closed rect
       
  5477             && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
       
  5478             && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
       
  5479             && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
       
  5480             ) ||
       
  5481            (elementCount == 4 // 4-point polygon, check for unclosed rect
       
  5482             && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
       
  5483             && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
       
  5484             );
       
  5485 }
       
  5486 
       
  5487 void QOpenGLPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
       
  5488 {
       
  5489     const qreal *points = path.points();
       
  5490     const QPainterPath::ElementType *types = path.elements();
       
  5491     if (!types && path.shape() == QVectorPath::RectangleHint) {
       
  5492         QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
       
  5493         updateClipRegion(QRegion(r.toRect()), op);
       
  5494         return;
       
  5495     }
       
  5496 
       
  5497     QPainterPath p;
       
  5498     if (types) {
       
  5499         int id = 0;
       
  5500         for (int i=0; i<path.elementCount(); ++i) {
       
  5501             switch(types[i]) {
       
  5502             case QPainterPath::MoveToElement:
       
  5503                 p.moveTo(QPointF(points[id], points[id+1]));
       
  5504                 id+=2;
       
  5505                 break;
       
  5506             case QPainterPath::LineToElement:
       
  5507                 p.lineTo(QPointF(points[id], points[id+1]));
       
  5508                 id+=2;
       
  5509                 break;
       
  5510             case QPainterPath::CurveToElement: {
       
  5511                 QPointF p1(points[id], points[id+1]);
       
  5512                 QPointF p2(points[id+2], points[id+3]);
       
  5513                 QPointF p3(points[id+4], points[id+5]);
       
  5514                 p.cubicTo(p1, p2, p3);
       
  5515                 id+=6;
       
  5516                 break;
       
  5517             }
       
  5518             case QPainterPath::CurveToDataElement:
       
  5519                 ;
       
  5520                 break;
       
  5521             }
       
  5522         }
       
  5523     } else if (!path.isEmpty()) {
       
  5524         p.moveTo(QPointF(points[0], points[1]));
       
  5525         int id = 2;
       
  5526         for (int i=1; i<path.elementCount(); ++i) {
       
  5527             p.lineTo(QPointF(points[id], points[id+1]));
       
  5528             id+=2;
       
  5529         }
       
  5530     }
       
  5531     if (path.hints() & QVectorPath::WindingFill)
       
  5532         p.setFillRule(Qt::WindingFill);
       
  5533 
       
  5534     updateClipRegion(QRegion(p.toFillPolygon().toPolygon(), p.fillRule()), op);
       
  5535     return;
       
  5536 }
       
  5537 
       
  5538 void QOpenGLPaintEngine::setState(QPainterState *s)
       
  5539 {
       
  5540     Q_D(QOpenGLPaintEngine);
       
  5541     QOpenGLPaintEngineState *new_state = static_cast<QOpenGLPaintEngineState *>(s);
       
  5542     QOpenGLPaintEngineState *old_state = state();
       
  5543 
       
  5544     QPaintEngineEx::setState(s);
       
  5545 
       
  5546     // are we in a save() ?
       
  5547     if (s == d->last_created_state) {
       
  5548         d->last_created_state = 0;
       
  5549         return;
       
  5550     }
       
  5551 
       
  5552     if (isActive()) {
       
  5553         if (old_state->depthClipId != new_state->depthClipId)
       
  5554             d->updateDepthClip();
       
  5555         penChanged();
       
  5556         brushChanged();
       
  5557         opacityChanged();
       
  5558         compositionModeChanged();
       
  5559         renderHintsChanged();
       
  5560         transformChanged();
       
  5561     }
       
  5562 }
       
  5563 
       
  5564 QPainterState *QOpenGLPaintEngine::createState(QPainterState *orig) const
       
  5565 {
       
  5566     const Q_D(QOpenGLPaintEngine);
       
  5567 
       
  5568     QOpenGLPaintEngineState *s;
       
  5569     if (!orig)
       
  5570         s = new QOpenGLPaintEngineState();
       
  5571     else
       
  5572         s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig));
       
  5573 
       
  5574     d->last_created_state = s;
       
  5575     return s;
       
  5576 }
       
  5577 
       
  5578 //
       
  5579 //  QOpenGLPaintEngineState
       
  5580 //
       
  5581 
       
  5582 QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other)
       
  5583     : QPainterState(other)
       
  5584 {
       
  5585     clipRegion = other.clipRegion;
       
  5586     hasClipping = other.hasClipping;
       
  5587     fastClip = other.fastClip;
       
  5588     depthClipId = other.depthClipId;
       
  5589 }
       
  5590 
       
  5591 QOpenGLPaintEngineState::QOpenGLPaintEngineState()
       
  5592 {
       
  5593     hasClipping = false;
       
  5594     depthClipId = 0;
       
  5595 }
       
  5596 
       
  5597 QOpenGLPaintEngineState::~QOpenGLPaintEngineState()
       
  5598 {
       
  5599 }
       
  5600 
       
  5601 void QOpenGLPaintEnginePrivate::ensureDrawableTexture()
       
  5602 {
       
  5603     if (!dirty_drawable_texture)
       
  5604         return;
       
  5605 
       
  5606     dirty_drawable_texture = false;
       
  5607 
       
  5608 #ifndef QT_OPENGL_ES
       
  5609     glGenTextures(1, &drawable_texture);
       
  5610     glBindTexture(GL_TEXTURE_2D, drawable_texture);
       
  5611 
       
  5612     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
       
  5613             drawable_texture_size.width(),
       
  5614             drawable_texture_size.height(), 0,
       
  5615             GL_RGBA, GL_UNSIGNED_BYTE, NULL);
       
  5616 
       
  5617     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       
  5618     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       
  5619     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
       
  5620     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
       
  5621 #endif
       
  5622 }
       
  5623 
       
  5624 QT_END_NAMESPACE
       
  5625 
       
  5626 #include "qpaintengine_opengl.moc"