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