Cause fatal error when trying to make a context current in wrong thread.
[profile/ivi/qtbase.git] / src / gui / opengl / qpaintengineex_opengl2.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43     When the active program changes, we need to update it's uniforms.
44     We could track state for each program and only update stale uniforms
45         - Could lead to lots of overhead if there's a lot of programs
46     We could update all the uniforms when the program changes
47         - Could end up updating lots of uniforms which don't need updating
48
49     Updating uniforms should be cheap, so the overhead of updating up-to-date
50     uniforms should be minimal. It's also less complex.
51
52     Things which _may_ cause a different program to be used:
53         - Change in brush/pen style
54         - Change in painter opacity
55         - Change in composition mode
56
57     Whenever we set a mode on the shader manager - it needs to tell us if it had
58     to switch to a different program.
59
60     The shader manager should only switch when we tell it to. E.g. if we set a new
61     brush style and then switch to transparent painter, we only want it to compile
62     and use the correct program when we really need it.
63 */
64
65 // #define QT_OPENGL_CACHE_AS_VBOS
66
67 #include "qopenglgradientcache_p.h"
68 #include "qpaintengineex_opengl2_p.h"
69
70 #include <string.h> //for memcpy
71 #include <qmath.h>
72
73 #include <private/qopengl_p.h>
74 #include <private/qopenglcontext_p.h>
75 #include <private/qopenglextensions_p.h>
76 #include <private/qmath_p.h>
77 #include <private/qpaintengineex_p.h>
78 #include <QPaintEngine>
79 #include <private/qpainter_p.h>
80 #include <private/qfontengine_p.h>
81 #include <private/qdatabuffer_p.h>
82 #include <private/qstatictext_p.h>
83 #include <private/qtriangulator_p.h>
84
85 #include "qopenglengineshadermanager_p.h"
86 #include "qopengl2pexvertexarray_p.h"
87 #include "qtriangulatingstroker_p.h"
88 #include "qtextureglyphcache_gl_p.h"
89
90 #include <QDebug>
91
92 QT_BEGIN_NAMESPACE
93
94 #if defined(Q_WS_WIN)
95 extern Q_GUI_EXPORT bool qt_cleartype_enabled;
96 #endif
97
98 #ifdef Q_WS_MAC
99 extern bool qt_applefontsmoothing_enabled;
100 #endif
101
102 Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
103
104 ////////////////////////////////// Private Methods //////////////////////////////////////////
105
106 QOpenGL2PaintEngineExPrivate::~QOpenGL2PaintEngineExPrivate()
107 {
108     delete shaderManager;
109
110     while (pathCaches.size()) {
111         QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
112         e->cleanup(e->engine, e->data);
113         e->data = 0;
114         e->engine = 0;
115     }
116
117     if (elementIndicesVBOId != 0) {
118         funcs.glDeleteBuffers(1, &elementIndicesVBOId);
119         elementIndicesVBOId = 0;
120     }
121 }
122
123 void QOpenGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
124 {
125 //    funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit?
126     if (id != GLuint(-1) && id == lastTextureUsed)
127         return;
128
129     lastTextureUsed = id;
130
131     if (smoothPixmapTransform) {
132         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
133         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
134     } else {
135         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
136         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
137     }
138     glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
139     glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
140 }
141
142
143 inline QColor qt_premultiplyColor(QColor c, GLfloat opacity)
144 {
145     qreal alpha = c.alphaF() * opacity;
146     c.setAlphaF(alpha);
147     c.setRedF(c.redF() * alpha);
148     c.setGreenF(c.greenF() * alpha);
149     c.setBlueF(c.blueF() * alpha);
150     return c;
151 }
152
153
154 void QOpenGL2PaintEngineExPrivate::setBrush(const QBrush& brush)
155 {
156     if (qbrush_fast_equals(currentBrush, brush))
157         return;
158
159     const Qt::BrushStyle newStyle = qbrush_style(brush);
160     Q_ASSERT(newStyle != Qt::NoBrush);
161
162     currentBrush = brush;
163     if (!currentBrushPixmap.isNull())
164         currentBrushPixmap = QPixmap();
165     brushUniformsDirty = true; // All brushes have at least one uniform
166
167     if (newStyle > Qt::SolidPattern)
168         brushTextureDirty = true;
169
170     if (currentBrush.style() == Qt::TexturePattern
171         && qHasPixmapTexture(brush) && brush.texture().isQBitmap())
172     {
173         shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::TextureSrcWithPattern);
174     } else {
175         shaderManager->setSrcPixelType(newStyle);
176     }
177     shaderManager->optimiseForBrushTransform(currentBrush.transform().type());
178 }
179
180
181 void QOpenGL2PaintEngineExPrivate::useSimpleShader()
182 {
183     shaderManager->useSimpleProgram();
184
185     if (matrixDirty)
186         updateMatrix();
187 }
188
189 void QOpenGL2PaintEngineExPrivate::updateBrushTexture()
190 {
191     Q_Q(QOpenGL2PaintEngineEx);
192 //     qDebug("QOpenGL2PaintEngineExPrivate::updateBrushTexture()");
193     Qt::BrushStyle style = currentBrush.style();
194
195     if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
196         // Get the image data for the pattern
197         QImage texImage = qt_imageForBrush(style, false);
198
199         funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
200         //ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, QOpenGLContext::InternalBindOption);
201         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
202     }
203     else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
204         // Gradiant brush: All the gradiants use the same texture
205
206         const QGradient* g = currentBrush.gradient();
207
208         // We apply global opacity in the fragment shaders, so we always pass 1.0
209         // for opacity to the cache.
210         GLuint texId = QOpenGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0);
211
212         funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
213         glBindTexture(GL_TEXTURE_2D, texId);
214
215         if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
216             updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
217         else if (g->spread() == QGradient::ReflectSpread)
218             updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
219         else
220             updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform);
221     }
222     else if (style == Qt::TexturePattern) {
223         currentBrushPixmap = currentBrush.texture();
224
225         int max_texture_size = ctx->d_func()->maxTextureSize();
226         if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size)
227             currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
228
229         funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
230         QOpenGLTexture *tex = 0;//ctx->d_func()->bindTexture(currentBrushPixmap, GL_TEXTURE_2D, GL_RGBA,
231                                 //                     QOpenGLContext::InternalBindOption |
232                                 //                     QOpenGLContext::CanFlipNativePixmapBindOption);
233         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
234         textureInvertedY = tex->invertedY();
235     }
236     brushTextureDirty = false;
237 }
238
239
240 void QOpenGL2PaintEngineExPrivate::updateBrushUniforms()
241 {
242 //     qDebug("QOpenGL2PaintEngineExPrivate::updateBrushUniforms()");
243     Qt::BrushStyle style = currentBrush.style();
244
245     if (style == Qt::NoBrush)
246         return;
247
248     QTransform brushQTransform = currentBrush.transform();
249
250     if (style == Qt::SolidPattern) {
251         QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
252         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::FragmentColor), col);
253     }
254     else {
255         // All other brushes have a transform and thus need the translation point:
256         QPointF translationPoint;
257
258         if (style <= Qt::DiagCrossPattern) {
259             QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
260
261             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
262
263             QVector2D halfViewportSize(width*0.5, height*0.5);
264             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
265         }
266         else if (style == Qt::LinearGradientPattern) {
267             const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient());
268
269             QPointF realStart = g->start();
270             QPointF realFinal = g->finalStop();
271             translationPoint = realStart;
272
273             QPointF l = realFinal - realStart;
274
275             QVector3D linearData(
276                 l.x(),
277                 l.y(),
278                 1.0f / (l.x() * l.x() + l.y() * l.y())
279             );
280
281             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::LinearData), linearData);
282
283             QVector2D halfViewportSize(width*0.5, height*0.5);
284             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
285         }
286         else if (style == Qt::ConicalGradientPattern) {
287             const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient());
288             translationPoint   = g->center();
289
290             GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0;
291
292             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Angle), angle);
293
294             QVector2D halfViewportSize(width*0.5, height*0.5);
295             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
296         }
297         else if (style == Qt::RadialGradientPattern) {
298             const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient());
299             QPointF realCenter = g->center();
300             QPointF realFocal  = g->focalPoint();
301             qreal   realRadius = g->centerRadius() - g->focalRadius();
302             translationPoint   = realFocal;
303
304             QPointF fmp = realCenter - realFocal;
305             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp), fmp);
306
307             GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
308             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
309             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Inverse2Fmp2MRadius2),
310                                                              GLfloat(1.0 / (2.0*fmp2_m_radius2)));
311             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::SqrFr),
312                                                              GLfloat(g->focalRadius() * g->focalRadius()));
313             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BRadius),
314                                                              GLfloat(2 * (g->centerRadius() - g->focalRadius()) * g->focalRadius()),
315                                                              g->focalRadius(),
316                                                              g->centerRadius() - g->focalRadius());
317
318             QVector2D halfViewportSize(width*0.5, height*0.5);
319             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
320         }
321         else if (style == Qt::TexturePattern) {
322             const QPixmap& texPixmap = currentBrush.texture();
323
324             if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) {
325                 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
326                 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
327             }
328
329             QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
330             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
331
332             QVector2D halfViewportSize(width*0.5, height*0.5);
333             shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
334         }
335         else
336             qWarning("QOpenGL2PaintEngineEx: Unimplemented fill style");
337
338         const QPointF &brushOrigin = q->state()->brushOrigin;
339         QTransform matrix = q->state()->matrix;
340         matrix.translate(brushOrigin.x(), brushOrigin.y());
341
342         QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
343         qreal m22 = -1;
344         qreal dy = height;
345         if (device->isFlipped()) {
346             m22 = 1;
347             dy = 0;
348         }
349         QTransform gl_to_qt(1, 0, 0, m22, 0, dy);
350         QTransform inv_matrix;
351         if (style == Qt::TexturePattern && textureInvertedY == -1)
352             inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate;
353         else
354             inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
355
356         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTransform), inv_matrix);
357         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
358     }
359     brushUniformsDirty = false;
360 }
361
362
363 // This assumes the shader manager has already setup the correct shader program
364 void QOpenGL2PaintEngineExPrivate::updateMatrix()
365 {
366 //     qDebug("QOpenGL2PaintEngineExPrivate::updateMatrix()");
367
368     const QTransform& transform = q->state()->matrix;
369
370     // The projection matrix converts from Qt's coordinate system to GL's coordinate system
371     //    * GL's viewport is 2x2, Qt's is width x height
372     //    * GL has +y -> -y going from bottom -> top, Qt is the other way round
373     //    * GL has [0,0] in the center, Qt has it in the top-left
374     //
375     // This results in the Projection matrix below, which is multiplied by the painter's
376     // transformation matrix, as shown below:
377     //
378     //                Projection Matrix                      Painter Transform
379     // ------------------------------------------------   ------------------------
380     // | 2.0 / width  |      0.0      |     -1.0      |   |  m11  |  m21  |  dx  |
381     // |     0.0      | -2.0 / height |      1.0      | * |  m12  |  m22  |  dy  |
382     // |     0.0      |      0.0      |      1.0      |   |  m13  |  m23  |  m33 |
383     // ------------------------------------------------   ------------------------
384     //
385     // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies
386
387     const GLfloat wfactor = 2.0f / width;
388     GLfloat hfactor = -2.0f / height;
389
390     GLfloat dx = transform.dx();
391     GLfloat dy = transform.dy();
392
393     if (device->isFlipped()) {
394         hfactor *= -1;
395         dy -= height;
396     }
397
398     // Non-integer translates can have strange effects for some rendering operations such as
399     // anti-aliased text rendering. In such cases, we snap the translate to the pixel grid.
400     if (snapToPixelGrid && transform.type() == QTransform::TxTranslate) {
401         // 0.50 needs to rounded down to 0.0 for consistency with raster engine:
402         dx = ceilf(dx - 0.5f);
403         dy = ceilf(dy - 0.5f);
404     }
405     pmvMatrix[0][0] = (wfactor * transform.m11())  - transform.m13();
406     pmvMatrix[1][0] = (wfactor * transform.m21())  - transform.m23();
407     pmvMatrix[2][0] = (wfactor * dx) - transform.m33();
408     pmvMatrix[0][1] = (hfactor * transform.m12())  + transform.m13();
409     pmvMatrix[1][1] = (hfactor * transform.m22())  + transform.m23();
410     pmvMatrix[2][1] = (hfactor * dy) + transform.m33();
411     pmvMatrix[0][2] = transform.m13();
412     pmvMatrix[1][2] = transform.m23();
413     pmvMatrix[2][2] = transform.m33();
414
415     // 1/10000 == 0.0001, so we have good enough res to cover curves
416     // that span the entire widget...
417     inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
418                                   qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
419                         qreal(0.0001));
420
421     matrixDirty = false;
422     matrixUniformDirty = true;
423
424     // Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only
425     // need to do this once for every matrix change and persists across all shader programs.
426     funcs.glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]);
427     funcs.glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]);
428     funcs.glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]);
429
430     dasher.setInvScale(inverseScale);
431     stroker.setInvScale(inverseScale);
432 }
433
434
435 void QOpenGL2PaintEngineExPrivate::updateCompositionMode()
436 {
437     // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
438     //       composition modes look odd.
439 //     qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
440     switch(q->state()->composition_mode) {
441     case QPainter::CompositionMode_SourceOver:
442         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
443         break;
444     case QPainter::CompositionMode_DestinationOver:
445         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
446         break;
447     case QPainter::CompositionMode_Clear:
448         glBlendFunc(GL_ZERO, GL_ZERO);
449         break;
450     case QPainter::CompositionMode_Source:
451         glBlendFunc(GL_ONE, GL_ZERO);
452         break;
453     case QPainter::CompositionMode_Destination:
454         glBlendFunc(GL_ZERO, GL_ONE);
455         break;
456     case QPainter::CompositionMode_SourceIn:
457         glBlendFunc(GL_DST_ALPHA, GL_ZERO);
458         break;
459     case QPainter::CompositionMode_DestinationIn:
460         glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
461         break;
462     case QPainter::CompositionMode_SourceOut:
463         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
464         break;
465     case QPainter::CompositionMode_DestinationOut:
466         glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
467         break;
468     case QPainter::CompositionMode_SourceAtop:
469         glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
470         break;
471     case QPainter::CompositionMode_DestinationAtop:
472         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
473         break;
474     case QPainter::CompositionMode_Xor:
475         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
476         break;
477     case QPainter::CompositionMode_Plus:
478         glBlendFunc(GL_ONE, GL_ONE);
479         break;
480     default:
481         qWarning("Unsupported composition mode");
482         break;
483     }
484
485     compositionModeDirty = false;
486 }
487
488 static inline void setCoords(GLfloat *coords, const QOpenGLRect &rect)
489 {
490     coords[0] = rect.left;
491     coords[1] = rect.top;
492     coords[2] = rect.right;
493     coords[3] = rect.top;
494     coords[4] = rect.right;
495     coords[5] = rect.bottom;
496     coords[6] = rect.left;
497     coords[7] = rect.bottom;
498 }
499
500 void QOpenGL2PaintEngineExPrivate::drawTexture(const QOpenGLRect& dest, const QOpenGLRect& src, const QSize &textureSize, bool opaque, bool pattern)
501 {
502     // Setup for texture drawing
503     currentBrush = noBrush;
504     shaderManager->setSrcPixelType(pattern ? QOpenGLEngineShaderManager::PatternSrc : QOpenGLEngineShaderManager::ImageSrc);
505
506     if (snapToPixelGrid) {
507         snapToPixelGrid = false;
508         matrixDirty = true;
509     }
510
511     if (prepareForDraw(opaque))
512         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
513
514     if (pattern) {
515         QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
516         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
517     }
518
519     GLfloat dx = 1.0 / textureSize.width();
520     GLfloat dy = 1.0 / textureSize.height();
521
522     QOpenGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy);
523
524     setCoords(staticVertexCoordinateArray, dest);
525     setCoords(staticTextureCoordinateArray, srcTextureRect);
526
527     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
528 }
529
530 void QOpenGL2PaintEngineEx::beginNativePainting()
531 {
532     Q_D(QOpenGL2PaintEngineEx);
533     ensureActive();
534     d->transferMode(BrushDrawingMode);
535
536     d->nativePaintingActive = true;
537
538     d->funcs.glUseProgram(0);
539
540     // Disable all the vertex attribute arrays:
541     for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
542         d->funcs.glDisableVertexAttribArray(i);
543
544 #ifndef QT_OPENGL_ES_2
545     const QSurfaceFormat &fmt = d->device->format();
546     if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1)
547         || fmt.profile() == QSurfaceFormat::CompatibilityProfile)
548     {
549         // be nice to people who mix OpenGL 1.x code with QPainter commands
550         // by setting modelview and projection matrices to mirror the GL 1
551         // paint engine
552         const QTransform& mtx = state()->matrix;
553
554         float mv_matrix[4][4] =
555         {
556             { float(mtx.m11()), float(mtx.m12()),     0, float(mtx.m13()) },
557             { float(mtx.m21()), float(mtx.m22()),     0, float(mtx.m23()) },
558             {                0,                0,     1,                0 },
559             {  float(mtx.dx()),  float(mtx.dy()),     0, float(mtx.m33()) }
560         };
561
562         const QSize sz = d->device->size();
563
564         glMatrixMode(GL_PROJECTION);
565         glLoadIdentity();
566         glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
567
568         glMatrixMode(GL_MODELVIEW);
569         glLoadMatrixf(&mv_matrix[0][0]);
570     }
571 #endif
572
573     d->lastTextureUsed = GLuint(-1);
574     d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
575     d->resetGLState();
576
577     d->shaderManager->setDirty();
578
579     d->needsSync = true;
580 }
581
582 void QOpenGL2PaintEngineExPrivate::resetGLState()
583 {
584     glDisable(GL_BLEND);
585     funcs.glActiveTexture(GL_TEXTURE0);
586     glDisable(GL_STENCIL_TEST);
587     glDisable(GL_DEPTH_TEST);
588     glDisable(GL_SCISSOR_TEST);
589     glDepthMask(true);
590     glDepthFunc(GL_LESS);
591     funcs.glClearDepthf(1);
592     glStencilMask(0xff);
593     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
594     glStencilFunc(GL_ALWAYS, 0, 0xff);
595     setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
596     setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false);
597     setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
598 #ifndef QT_OPENGL_ES_2
599     // gl_Color, corresponding to vertex attribute 3, may have been changed
600     float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
601     funcs.glVertexAttrib4fv(3, color);
602 #endif
603 }
604
605 void QOpenGL2PaintEngineEx::endNativePainting()
606 {
607     Q_D(QOpenGL2PaintEngineEx);
608     d->needsSync = true;
609     d->nativePaintingActive = false;
610 }
611
612 void QOpenGL2PaintEngineEx::invalidateState()
613 {
614     Q_D(QOpenGL2PaintEngineEx);
615     d->needsSync = true;
616 }
617
618 bool QOpenGL2PaintEngineEx::isNativePaintingActive() const {
619     Q_D(const QOpenGL2PaintEngineEx);
620     return d->nativePaintingActive;
621 }
622
623 void QOpenGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
624 {
625     if (newMode == mode)
626         return;
627
628     if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) {
629         lastTextureUsed = GLuint(-1);
630     }
631
632     if (newMode == TextDrawingMode) {
633         shaderManager->setHasComplexGeometry(true);
634     } else {
635         shaderManager->setHasComplexGeometry(false);
636     }
637
638     if (newMode == ImageDrawingMode) {
639         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray);
640         setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray);
641     }
642
643     if (newMode == ImageArrayDrawingMode) {
644         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
645         setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
646         setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data());
647     }
648
649     // This needs to change when we implement high-quality anti-aliasing...
650     if (newMode != TextDrawingMode)
651         shaderManager->setMaskType(QOpenGLEngineShaderManager::NoMask);
652
653     mode = newMode;
654 }
655
656 struct QOpenGL2PEVectorPathCache
657 {
658 #ifdef QT_OPENGL_CACHE_AS_VBOS
659     GLuint vbo;
660     GLuint ibo;
661 #else
662     float *vertices;
663     void *indices;
664 #endif
665     int vertexCount;
666     int indexCount;
667     GLenum primitiveType;
668     qreal iscale;
669 };
670
671 void QOpenGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data)
672 {
673     QOpenGL2PEVectorPathCache *c = (QOpenGL2PEVectorPathCache *) data;
674 #ifdef QT_OPENGL_CACHE_AS_VBOS
675     Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
676     static_cast<QOpenGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo;
677     if (c->ibo)
678         d->unusedIBOSToClean << c->ibo;
679 #else
680     Q_UNUSED(engine);
681     qFree(c->vertices);
682     qFree(c->indices);
683 #endif
684     delete c;
685 }
686
687 // Assumes everything is configured for the brush you want to use
688 void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path)
689 {
690     transferMode(BrushDrawingMode);
691
692     if (snapToPixelGrid) {
693         snapToPixelGrid = false;
694         matrixDirty = true;
695     }
696
697     // Might need to call updateMatrix to re-calculate inverseScale
698     if (matrixDirty)
699         updateMatrix();
700
701     const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
702
703     // Check to see if there's any hints
704     if (path.shape() == QVectorPath::RectangleHint) {
705         QOpenGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
706         prepareForDraw(currentBrush.isOpaque());
707         composite(rect);
708     } else if (path.isConvex()) {
709
710         if (path.isCacheable()) {
711             QVectorPath::CacheEntry *data = path.lookupCacheData(q);
712             QOpenGL2PEVectorPathCache *cache;
713
714             bool updateCache = false;
715
716             if (data) {
717                 cache = (QOpenGL2PEVectorPathCache *) data->data;
718                 // Check if scale factor is exceeded for curved paths and generate curves if so...
719                 if (path.isCurved()) {
720                     qreal scaleFactor = cache->iscale / inverseScale;
721                     if (scaleFactor < 0.5 || scaleFactor > 2.0) {
722 #ifdef QT_OPENGL_CACHE_AS_VBOS
723                         glDeleteBuffers(1, &cache->vbo);
724                         cache->vbo = 0;
725                         Q_ASSERT(cache->ibo == 0);
726 #else
727                         qFree(cache->vertices);
728                         Q_ASSERT(cache->indices == 0);
729 #endif
730                         updateCache = true;
731                     }
732                 }
733             } else {
734                 cache = new QOpenGL2PEVectorPathCache;
735                 data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
736                 updateCache = true;
737             }
738
739             // Flatten the path at the current scale factor and fill it into the cache struct.
740             if (updateCache) {
741                 vertexCoordinateArray.clear();
742                 vertexCoordinateArray.addPath(path, inverseScale, false);
743                 int vertexCount = vertexCoordinateArray.vertexCount();
744                 int floatSizeInBytes = vertexCount * 2 * sizeof(float);
745                 cache->vertexCount = vertexCount;
746                 cache->indexCount = 0;
747                 cache->primitiveType = GL_TRIANGLE_FAN;
748                 cache->iscale = inverseScale;
749 #ifdef QT_OPENGL_CACHE_AS_VBOS
750                 glGenBuffers(1, &cache->vbo);
751                 glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
752                 glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
753                 cache->ibo = 0;
754 #else
755                 cache->vertices = (float *) qMalloc(floatSizeInBytes);
756                 memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
757                 cache->indices = 0;
758 #endif
759             }
760
761             prepareForDraw(currentBrush.isOpaque());
762 #ifdef QT_OPENGL_CACHE_AS_VBOS
763             glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
764             setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
765 #else
766             setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
767 #endif
768             glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
769
770         } else {
771       //        printf(" - Marking path as cachable...\n");
772             // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
773             path.makeCacheable();
774             vertexCoordinateArray.clear();
775             vertexCoordinateArray.addPath(path, inverseScale, false);
776             prepareForDraw(currentBrush.isOpaque());
777             drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
778         }
779
780     } else {
781         bool useCache = path.isCacheable();
782         if (useCache) {
783             QRectF bbox = path.controlPointRect();
784             // If the path doesn't fit within these limits, it is possible that the triangulation will fail.
785             useCache &= (bbox.left() > -0x8000 * inverseScale)
786                      && (bbox.right() < 0x8000 * inverseScale)
787                      && (bbox.top() > -0x8000 * inverseScale)
788                      && (bbox.bottom() < 0x8000 * inverseScale);
789         }
790
791         if (useCache) {
792             QVectorPath::CacheEntry *data = path.lookupCacheData(q);
793             QOpenGL2PEVectorPathCache *cache;
794
795             bool updateCache = false;
796
797             if (data) {
798                 cache = (QOpenGL2PEVectorPathCache *) data->data;
799                 // Check if scale factor is exceeded for curved paths and generate curves if so...
800                 if (path.isCurved()) {
801                     qreal scaleFactor = cache->iscale / inverseScale;
802                     if (scaleFactor < 0.5 || scaleFactor > 2.0) {
803 #ifdef QT_OPENGL_CACHE_AS_VBOS
804                         glDeleteBuffers(1, &cache->vbo);
805                         glDeleteBuffers(1, &cache->ibo);
806 #else
807                         qFree(cache->vertices);
808                         qFree(cache->indices);
809 #endif
810                         updateCache = true;
811                     }
812                 }
813             } else {
814                 cache = new QOpenGL2PEVectorPathCache;
815                 data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
816                 updateCache = true;
817             }
818
819             // Flatten the path at the current scale factor and fill it into the cache struct.
820             if (updateCache) {
821                 QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale));
822                 cache->vertexCount = polys.vertices.size() / 2;
823                 cache->indexCount = polys.indices.size();
824                 cache->primitiveType = GL_TRIANGLES;
825                 cache->iscale = inverseScale;
826 #ifdef QT_OPENGL_CACHE_AS_VBOS
827                 glGenBuffers(1, &cache->vbo);
828                 glGenBuffers(1, &cache->ibo);
829                 glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
830                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
831
832                 if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint))
833                     funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
834                 else
835                     funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint16) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
836
837                 QVarLengthArray<float> vertices(polys.vertices.size());
838                 for (int i = 0; i < polys.vertices.size(); ++i)
839                     vertices[i] = float(inverseScale * polys.vertices.at(i));
840                 funcs.glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
841 #else
842                 cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size());
843                 if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) {
844                     cache->indices = (quint32 *) qMalloc(sizeof(quint32) * polys.indices.size());
845                     memcpy(cache->indices, polys.indices.data(), sizeof(quint32) * polys.indices.size());
846                 } else {
847                     cache->indices = (quint16 *) qMalloc(sizeof(quint16) * polys.indices.size());
848                     memcpy(cache->indices, polys.indices.data(), sizeof(quint16) * polys.indices.size());
849                 }
850                 for (int i = 0; i < polys.vertices.size(); ++i)
851                     cache->vertices[i] = float(inverseScale * polys.vertices.at(i));
852 #endif
853             }
854
855             prepareForDraw(currentBrush.isOpaque());
856 #ifdef QT_OPENGL_CACHE_AS_VBOS
857             glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
858             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
859             setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
860             if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint))
861                 glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0);
862             else
863                 glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, 0);
864             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
865             glBindBuffer(GL_ARRAY_BUFFER, 0);
866 #else
867             setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
868             if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint))
869                 glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, (qint32 *)cache->indices);
870             else
871                 glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, (qint16 *)cache->indices);
872 #endif
873
874         } else {
875       //        printf(" - Marking path as cachable...\n");
876             // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
877             path.makeCacheable();
878
879             if (!device->format().stencilBufferSize()) {
880                 // If there is no stencil buffer, triangulate the path instead.
881
882                 QRectF bbox = path.controlPointRect();
883                 // If the path doesn't fit within these limits, it is possible that the triangulation will fail.
884                 bool withinLimits = (bbox.left() > -0x8000 * inverseScale)
885                                   && (bbox.right() < 0x8000 * inverseScale)
886                                   && (bbox.top() > -0x8000 * inverseScale)
887                                   && (bbox.bottom() < 0x8000 * inverseScale);
888                 if (withinLimits) {
889                     QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale));
890
891                     QVarLengthArray<float> vertices(polys.vertices.size());
892                     for (int i = 0; i < polys.vertices.size(); ++i)
893                         vertices[i] = float(inverseScale * polys.vertices.at(i));
894
895                     prepareForDraw(currentBrush.isOpaque());
896                     setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertices.constData());
897                     if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint))
898                         glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_INT, polys.indices.data());
899                     else
900                         glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_SHORT, polys.indices.data());
901                 } else {
902                     // We can't handle big, concave painter paths with OpenGL without stencil buffer.
903                     qWarning("Painter path exceeds +/-32767 pixels.");
904                 }
905                 return;
906             }
907
908             // The path is too complicated & needs the stencil technique
909             vertexCoordinateArray.clear();
910             vertexCoordinateArray.addPath(path, inverseScale, false);
911
912             fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
913
914             glStencilMask(0xff);
915             glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
916
917             if (q->state()->clipTestEnabled) {
918                 // Pass when high bit is set, replace stencil value with current clip
919                 glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT);
920             } else if (path.hasWindingFill()) {
921                 // Pass when any bit is set, replace stencil value with 0
922                 glStencilFunc(GL_NOTEQUAL, 0, 0xff);
923             } else {
924                 // Pass when high bit is set, replace stencil value with 0
925                 glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
926             }
927             prepareForDraw(currentBrush.isOpaque());
928
929             // Stencil the brush onto the dest buffer
930             composite(vertexCoordinateArray.boundingRect());
931             glStencilMask(0);
932             updateClipScissorTest();
933         }
934     }
935 }
936
937
938 void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
939                                                           int count,
940                                                           int *stops,
941                                                           int stopCount,
942                                                           const QOpenGLRect &bounds,
943                                                           StencilFillMode mode)
944 {
945     Q_ASSERT(count || stops);
946
947 //     qDebug("QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
948     glStencilMask(0xff); // Enable stencil writes
949
950     if (dirtyStencilRegion.intersects(currentScissorBounds)) {
951         QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects();
952         glClearStencil(0); // Clear to zero
953         for (int i = 0; i < clearRegion.size(); ++i) {
954 #ifndef QT_GL_NO_SCISSOR_TEST
955             setScissor(clearRegion.at(i));
956 #endif
957             glClear(GL_STENCIL_BUFFER_BIT);
958         }
959
960         dirtyStencilRegion -= currentScissorBounds;
961
962 #ifndef QT_GL_NO_SCISSOR_TEST
963         updateClipScissorTest();
964 #endif
965     }
966
967     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
968     useSimpleShader();
969     glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
970
971     if (mode == WindingFillMode) {
972         Q_ASSERT(stops && !count);
973         if (q->state()->clipTestEnabled) {
974             // Flatten clip values higher than current clip, and set high bit to match current clip
975             glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
976             glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
977             composite(bounds);
978
979             glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT);
980         } else if (!stencilClean) {
981             // Clear stencil buffer within bounding rect
982             glStencilFunc(GL_ALWAYS, 0, 0xff);
983             glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
984             composite(bounds);
985         }
986
987         // Inc. for front-facing triangle
988         funcs.glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
989         // Dec. for back-facing "holes"
990         funcs.glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
991         glStencilMask(~GL_STENCIL_HIGH_BIT);
992         drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
993
994         if (q->state()->clipTestEnabled) {
995             // Clear high bit of stencil outside of path
996             glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
997             glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
998             glStencilMask(GL_STENCIL_HIGH_BIT);
999             composite(bounds);
1000         }
1001     } else if (mode == OddEvenFillMode) {
1002         glStencilMask(GL_STENCIL_HIGH_BIT);
1003         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
1004         drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
1005
1006     } else { // TriStripStrokeFillMode
1007         Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops
1008         glStencilMask(GL_STENCIL_HIGH_BIT);
1009 #if 0
1010         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
1011         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
1012         glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1013 #else
1014
1015         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1016         if (q->state()->clipTestEnabled) {
1017             glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT,
1018                           ~GL_STENCIL_HIGH_BIT);
1019         } else {
1020             glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff);
1021         }
1022         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
1023         glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1024 #endif
1025     }
1026
1027     // Enable color writes & disable stencil writes
1028     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1029 }
1030
1031 /*
1032     If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1,
1033     restore the stencil buffer to a pristine state.  The current clip region
1034     is set to 1, and the rest to 0.
1035 */
1036 void QOpenGL2PaintEngineExPrivate::resetClipIfNeeded()
1037 {
1038     if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
1039         return;
1040
1041     Q_Q(QOpenGL2PaintEngineEx);
1042
1043     useSimpleShader();
1044     glEnable(GL_STENCIL_TEST);
1045     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1046
1047     QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height));
1048     QOpenGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
1049
1050     // Set high bit on clip region
1051     glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff);
1052     glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
1053     glStencilMask(GL_STENCIL_HIGH_BIT);
1054     composite(rect);
1055
1056     // Reset clipping to 1 and everything else to zero
1057     glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT);
1058     glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE);
1059     glStencilMask(0xff);
1060     composite(rect);
1061
1062     q->state()->currentClip = 1;
1063     q->state()->canRestoreClip = false;
1064
1065     maxClip = 1;
1066
1067     glStencilMask(0x0);
1068     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1069 }
1070
1071 bool QOpenGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
1072 {
1073     if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
1074         updateBrushTexture();
1075
1076     if (compositionModeDirty)
1077         updateCompositionMode();
1078
1079     if (matrixDirty)
1080         updateMatrix();
1081
1082     const bool stateHasOpacity = q->state()->opacity < 0.99f;
1083     if (q->state()->composition_mode == QPainter::CompositionMode_Source
1084         || (q->state()->composition_mode == QPainter::CompositionMode_SourceOver
1085             && srcPixelsAreOpaque && !stateHasOpacity))
1086     {
1087         glDisable(GL_BLEND);
1088     } else {
1089         glEnable(GL_BLEND);
1090     }
1091
1092     QOpenGLEngineShaderManager::OpacityMode opacityMode;
1093     if (mode == ImageArrayDrawingMode) {
1094         opacityMode = QOpenGLEngineShaderManager::AttributeOpacity;
1095     } else {
1096         opacityMode = stateHasOpacity ? QOpenGLEngineShaderManager::UniformOpacity
1097                                       : QOpenGLEngineShaderManager::NoOpacity;
1098         if (stateHasOpacity && (mode != ImageDrawingMode)) {
1099             // Using a brush
1100             bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) &&
1101                                   (currentBrush.style() <= Qt::DiagCrossPattern);
1102
1103             if ((currentBrush.style() == Qt::SolidPattern) || brushIsPattern)
1104                 opacityMode = QOpenGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader
1105         }
1106     }
1107     shaderManager->setOpacityMode(opacityMode);
1108
1109     bool changed = shaderManager->useCorrectShaderProg();
1110     // If the shader program needs changing, we change it and mark all uniforms as dirty
1111     if (changed) {
1112         // The shader program has changed so mark all uniforms as dirty:
1113         brushUniformsDirty = true;
1114         opacityUniformDirty = true;
1115         matrixUniformDirty = true;
1116     }
1117
1118     if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode)
1119         updateBrushUniforms();
1120
1121     if (opacityMode == QOpenGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
1122         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
1123         opacityUniformDirty = false;
1124     }
1125
1126     if (matrixUniformDirty && shaderManager->hasComplexGeometry()) {
1127         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Matrix),
1128                                                          pmvMatrix);
1129         matrixUniformDirty = false;
1130     }
1131
1132     return changed;
1133 }
1134
1135 void QOpenGL2PaintEngineExPrivate::composite(const QOpenGLRect& boundingRect)
1136 {
1137     setCoords(staticVertexCoordinateArray, boundingRect);
1138     setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray);
1139     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1140 }
1141
1142 // Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans.
1143 void QOpenGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount,
1144                                                 GLenum primitive)
1145 {
1146     // Now setup the pointer to the vertex array:
1147     setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)data);
1148
1149     int previousStop = 0;
1150     for (int i=0; i<stopCount; ++i) {
1151         int stop = stops[i];
1152 /*
1153         qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1);
1154         for (int i=previousStop; i<stop; ++i)
1155             qDebug("   %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y);
1156 */
1157         glDrawArrays(primitive, previousStop, stop - previousStop);
1158         previousStop = stop;
1159     }
1160 }
1161
1162 /////////////////////////////////// Public Methods //////////////////////////////////////////
1163
1164 QOpenGL2PaintEngineEx::QOpenGL2PaintEngineEx()
1165     : QPaintEngineEx(*(new QOpenGL2PaintEngineExPrivate(this)))
1166 {
1167 }
1168
1169 QOpenGL2PaintEngineEx::~QOpenGL2PaintEngineEx()
1170 {
1171 }
1172
1173 void QOpenGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
1174 {
1175     Q_D(QOpenGL2PaintEngineEx);
1176
1177     if (qbrush_style(brush) == Qt::NoBrush)
1178         return;
1179     ensureActive();
1180     d->setBrush(brush);
1181     d->fill(path);
1182 }
1183
1184 Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
1185
1186
1187 void QOpenGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
1188 {
1189     Q_D(QOpenGL2PaintEngineEx);
1190
1191     const QBrush &penBrush = qpen_brush(pen);
1192     if (qpen_style(pen) == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush)
1193         return;
1194
1195     QOpenGL2PaintEngineState *s = state();
1196     if (pen.isCosmetic() && !qt_scaleForTransform(s->transform(), 0)) {
1197         // QTriangulatingStroker class is not meant to support cosmetically sheared strokes.
1198         QPaintEngineEx::stroke(path, pen);
1199         return;
1200     }
1201
1202     ensureActive();
1203     d->setBrush(penBrush);
1204     d->stroke(path, pen);
1205 }
1206
1207 void QOpenGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen)
1208 {
1209     const QOpenGL2PaintEngineState *s = q->state();
1210     if (snapToPixelGrid) {
1211         snapToPixelGrid = false;
1212         matrixDirty = true;
1213     }
1214
1215     const Qt::PenStyle penStyle = qpen_style(pen);
1216     const QBrush &penBrush = qpen_brush(pen);
1217     const bool opaque = penBrush.isOpaque() && s->opacity > 0.99;
1218
1219     transferMode(BrushDrawingMode);
1220
1221     // updateMatrix() is responsible for setting the inverse scale on
1222     // the strokers, so we need to call it here and not wait for
1223     // prepareForDraw() down below.
1224     updateMatrix();
1225
1226     QRectF clip = q->state()->matrix.inverted().mapRect(q->state()->clipEnabled
1227                                                         ? q->state()->rectangleClip
1228                                                         : QRectF(0, 0, width, height));
1229
1230     if (penStyle == Qt::SolidLine) {
1231         stroker.process(path, pen, clip);
1232
1233     } else { // Some sort of dash
1234         dasher.process(path, pen, clip);
1235
1236         QVectorPath dashStroke(dasher.points(),
1237                                dasher.elementCount(),
1238                                dasher.elementTypes());
1239         stroker.process(dashStroke, pen, clip);
1240     }
1241
1242     if (!stroker.vertexCount())
1243         return;
1244
1245     if (opaque) {
1246         prepareForDraw(opaque);
1247         setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, stroker.vertices());
1248         glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2);
1249
1250 //         QBrush b(Qt::green);
1251 //         d->setBrush(&b);
1252 //         d->prepareForDraw(true);
1253 //         glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2);
1254
1255     } else {
1256         qreal width = qpen_widthf(pen) / 2;
1257         if (width == 0)
1258             width = 0.5;
1259         qreal extra = pen.joinStyle() == Qt::MiterJoin
1260                       ? qMax(pen.miterLimit() * width, width)
1261                       : width;
1262
1263         if (pen.isCosmetic())
1264             extra = extra * inverseScale;
1265
1266         QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra);
1267
1268         fillStencilWithVertexArray(stroker.vertices(), stroker.vertexCount() / 2,
1269                                       0, 0, bounds, QOpenGL2PaintEngineExPrivate::TriStripStrokeFillMode);
1270
1271         glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1272
1273         // Pass when any bit is set, replace stencil value with 0
1274         glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT);
1275         prepareForDraw(false);
1276
1277         // Stencil the brush onto the dest buffer
1278         composite(bounds);
1279
1280         glStencilMask(0);
1281
1282         updateClipScissorTest();
1283     }
1284 }
1285
1286 void QOpenGL2PaintEngineEx::penChanged() { }
1287 void QOpenGL2PaintEngineEx::brushChanged() { }
1288 void QOpenGL2PaintEngineEx::brushOriginChanged() { }
1289
1290 void QOpenGL2PaintEngineEx::opacityChanged()
1291 {
1292 //    qDebug("QOpenGL2PaintEngineEx::opacityChanged()");
1293     Q_D(QOpenGL2PaintEngineEx);
1294     state()->opacityChanged = true;
1295
1296     Q_ASSERT(d->shaderManager);
1297     d->brushUniformsDirty = true;
1298     d->opacityUniformDirty = true;
1299 }
1300
1301 void QOpenGL2PaintEngineEx::compositionModeChanged()
1302 {
1303 //     qDebug("QOpenGL2PaintEngineEx::compositionModeChanged()");
1304     Q_D(QOpenGL2PaintEngineEx);
1305     state()->compositionModeChanged = true;
1306     d->compositionModeDirty = true;
1307 }
1308
1309 void QOpenGL2PaintEngineEx::renderHintsChanged()
1310 {
1311     state()->renderHintsChanged = true;
1312
1313 #if !defined(QT_OPENGL_ES_2)
1314     if ((state()->renderHints & QPainter::Antialiasing)
1315         || (state()->renderHints & QPainter::HighQualityAntialiasing))
1316         glEnable(GL_MULTISAMPLE);
1317     else
1318         glDisable(GL_MULTISAMPLE);
1319 #endif
1320
1321     Q_D(QOpenGL2PaintEngineEx);
1322     d->lastTextureUsed = GLuint(-1);
1323     d->brushTextureDirty = true;
1324 //    qDebug("QOpenGL2PaintEngineEx::renderHintsChanged() not implemented!");
1325 }
1326
1327 void QOpenGL2PaintEngineEx::transformChanged()
1328 {
1329     Q_D(QOpenGL2PaintEngineEx);
1330     d->matrixDirty = true;
1331     state()->matrixChanged = true;
1332 }
1333
1334
1335 static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
1336 {
1337     return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
1338 }
1339
1340 void QOpenGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
1341 {
1342     Q_D(QOpenGL2PaintEngineEx);
1343     QOpenGLContext *ctx = d->ctx;
1344
1345     int max_texture_size = ctx->d_func()->maxTextureSize();
1346     if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) {
1347         QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1348
1349         const qreal sx = scaled.width() / qreal(pixmap.width());
1350         const qreal sy = scaled.height() / qreal(pixmap.height());
1351
1352         drawPixmap(dest, scaled, scaleRect(src, sx, sy));
1353         return;
1354     }
1355
1356     ensureActive();
1357     d->transferMode(ImageDrawingMode);
1358
1359     d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1360     QOpenGLTexture *texture = 0;
1361 //        ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions);
1362
1363     GLfloat top = texture->invertedY() ? (pixmap.height() - src.top()) : src.top();
1364     GLfloat bottom = texture->invertedY() ? (pixmap.height() - src.bottom()) : src.bottom();
1365     QOpenGLRect srcRect(src.left(), top, src.right(), bottom);
1366
1367     bool isBitmap = pixmap.isQBitmap();
1368     bool isOpaque = !isBitmap && !pixmap.hasAlpha();
1369
1370     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1371                            state()->renderHints & QPainter::SmoothPixmapTransform, texture->id());
1372     d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
1373 }
1374
1375 void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
1376                         Qt::ImageConversionFlags)
1377 {
1378     Q_D(QOpenGL2PaintEngineEx);
1379     QOpenGLContext *ctx = d->ctx;
1380
1381     int max_texture_size = ctx->d_func()->maxTextureSize();
1382     if (image.width() > max_texture_size || image.height() > max_texture_size) {
1383         QImage scaled = image.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1384
1385         const qreal sx = scaled.width() / qreal(image.width());
1386         const qreal sy = scaled.height() / qreal(image.height());
1387
1388         drawImage(dest, scaled, scaleRect(src, sx, sy));
1389         return;
1390     }
1391
1392     ensureActive();
1393     d->transferMode(ImageDrawingMode);
1394
1395     d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1396
1397     QOpenGLTexture *texture = 0;//ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions);
1398     GLuint id = texture->id();
1399
1400     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1401                            state()->renderHints & QPainter::SmoothPixmapTransform, id);
1402     d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
1403 }
1404
1405 void QOpenGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
1406 {
1407     Q_D(QOpenGL2PaintEngineEx);
1408
1409     ensureActive();
1410
1411     QPainterState *s = state();
1412     float det = s->matrix.determinant();
1413
1414     // don't try to cache huge fonts or vastly transformed fonts
1415     QFontEngine *fontEngine = textItem->fontEngine();
1416     const qreal pixelSize = fontEngine->fontDef.pixelSize;
1417     if (shouldDrawCachedGlyphs(pixelSize, s->matrix) || det < 0.25f || det > 4.f) {
1418         QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0
1419                                                 ? QFontEngineGlyphCache::Type(textItem->fontEngine()->glyphFormat)
1420                                                 : d->glyphCacheType;
1421         if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1422             if (d->device->alphaRequested() || s->matrix.type() > QTransform::TxTranslate
1423                 || (s->composition_mode != QPainter::CompositionMode_Source
1424                 && s->composition_mode != QPainter::CompositionMode_SourceOver))
1425             {
1426                 glyphType = QFontEngineGlyphCache::Raster_A8;
1427             }
1428         }
1429
1430         d->drawCachedGlyphs(glyphType, textItem);
1431     } else {
1432         QPaintEngineEx::drawStaticTextItem(textItem);
1433     }
1434 }
1435
1436 bool QOpenGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
1437 {
1438     Q_D(QOpenGL2PaintEngineEx);
1439     if (!d->shaderManager)
1440         return false;
1441
1442     ensureActive();
1443     d->transferMode(ImageDrawingMode);
1444
1445     d->funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1446     glBindTexture(GL_TEXTURE_2D, textureId);
1447
1448     QOpenGLRect srcRect(src.left(), src.bottom(), src.right(), src.top());
1449
1450     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1451                            state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
1452     d->drawTexture(dest, srcRect, size, false);
1453     return true;
1454 }
1455
1456 void QOpenGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
1457 {
1458     Q_D(QOpenGL2PaintEngineEx);
1459
1460     ensureActive();
1461     QOpenGL2PaintEngineState *s = state();
1462
1463     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1464
1465     QTransform::TransformationType txtype = s->matrix.type();
1466
1467     float det = s->matrix.determinant();
1468     bool drawCached = txtype < QTransform::TxProject;
1469
1470     // don't try to cache huge fonts or vastly transformed fonts
1471     const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
1472     if (shouldDrawCachedGlyphs(pixelSize, s->matrix) || det < 0.25f || det > 4.f)
1473         drawCached = false;
1474
1475     QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
1476                                             ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
1477                                             : d->glyphCacheType;
1478
1479
1480     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1481         if (d->device->alphaRequested() || txtype > QTransform::TxTranslate
1482             || (state()->composition_mode != QPainter::CompositionMode_Source
1483             && state()->composition_mode != QPainter::CompositionMode_SourceOver))
1484         {
1485             glyphType = QFontEngineGlyphCache::Raster_A8;
1486         }
1487     }
1488
1489     if (drawCached) {
1490         QVarLengthArray<QFixedPoint> positions;
1491         QVarLengthArray<glyph_t> glyphs;
1492         QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
1493         ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1494
1495         {
1496             QStaticTextItem staticTextItem;
1497             staticTextItem.chars = const_cast<QChar *>(ti.chars);
1498             staticTextItem.setFontEngine(ti.fontEngine);
1499             staticTextItem.glyphs = glyphs.data();
1500             staticTextItem.numChars = ti.num_chars;
1501             staticTextItem.numGlyphs = glyphs.size();
1502             staticTextItem.glyphPositions = positions.data();
1503
1504             d->drawCachedGlyphs(glyphType, &staticTextItem);
1505         }
1506         return;
1507     }
1508
1509     QPaintEngineEx::drawTextItem(p, ti);
1510 }
1511
1512 namespace {
1513
1514     class QOpenGLStaticTextUserData: public QStaticTextUserData
1515     {
1516     public:
1517         QOpenGLStaticTextUserData()
1518             : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0)
1519         {
1520         }
1521
1522         ~QOpenGLStaticTextUserData()
1523         {
1524         }
1525
1526         QSize cacheSize;
1527         QOpenGL2PEXVertexArray vertexCoordinateArray;
1528         QOpenGL2PEXVertexArray textureCoordinateArray;
1529         QFontEngineGlyphCache::Type glyphType;
1530         int cacheSerialNumber;
1531     };
1532
1533 }
1534
1535 #if defined(Q_WS_WIN)
1536 static bool fontSmoothingApproximately(qreal target)
1537 {
1538     extern Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; // qapplication_win.cpp
1539     return (qAbs(qt_fontsmoothing_gamma - target) < 0.2);
1540 }
1541 #endif
1542
1543 // #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
1544
1545 void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType,
1546                                                 QStaticTextItem *staticTextItem)
1547 {
1548     Q_Q(QOpenGL2PaintEngineEx);
1549
1550     QOpenGL2PaintEngineState *s = q->state();
1551
1552     void *cacheKey = ctx->shareGroup();
1553     bool recreateVertexArrays = false;
1554
1555     QOpenGLTextureGlyphCache *cache =
1556             (QOpenGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform());
1557     if (!cache || cache->cacheType() != glyphType || cache->contextGroup() == 0) {
1558         cache = new QOpenGLTextureGlyphCache(glyphType, QTransform());
1559         staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache);
1560         recreateVertexArrays = true;
1561     }
1562
1563     if (staticTextItem->userDataNeedsUpdate) {
1564         recreateVertexArrays = true;
1565     } else if (staticTextItem->userData() == 0) {
1566         recreateVertexArrays = true;
1567     } else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1568         recreateVertexArrays = true;
1569     } else {
1570         QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData());
1571         if (userData->glyphType != glyphType) {
1572             recreateVertexArrays = true;
1573         } else if (userData->cacheSerialNumber != cache->serialNumber()) {
1574             recreateVertexArrays = true;
1575         }
1576     }
1577
1578     // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays.
1579     // If the cache size has changed, we do need to regenerate the vertices, but we don't need to repopulate the
1580     // cache so this text is performed before we test if the cache size has changed.
1581     if (recreateVertexArrays) {
1582         cache->setPaintEnginePrivate(this);
1583         if (!cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs,
1584                              staticTextItem->glyphs, staticTextItem->glyphPositions)) {
1585             // No space for glyphs in cache. We need to reset it and try again.
1586             cache->clear();
1587             cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs,
1588                             staticTextItem->glyphs, staticTextItem->glyphPositions);
1589         }
1590         cache->fillInPendingGlyphs();
1591     }
1592
1593     if (cache->width() == 0 || cache->height() == 0)
1594         return;
1595
1596     transferMode(TextDrawingMode);
1597
1598     int margin = cache->glyphMargin();
1599
1600     GLfloat dx = 1.0 / cache->width();
1601     GLfloat dy = 1.0 / cache->height();
1602
1603     // Use global arrays by default
1604     QOpenGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
1605     QOpenGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
1606
1607     if (staticTextItem->useBackendOptimizations) {
1608         QOpenGLStaticTextUserData *userData = 0;
1609
1610         if (staticTextItem->userData() == 0
1611             || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1612
1613             userData = new QOpenGLStaticTextUserData();
1614             staticTextItem->setUserData(userData);
1615
1616         } else {
1617             userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData());
1618         }
1619
1620         userData->glyphType = glyphType;
1621         userData->cacheSerialNumber = cache->serialNumber();
1622
1623         // Use cache if backend optimizations is turned on
1624         vertexCoordinates = &userData->vertexCoordinateArray;
1625         textureCoordinates = &userData->textureCoordinateArray;
1626
1627         QSize size(cache->width(), cache->height());
1628         if (userData->cacheSize != size) {
1629             recreateVertexArrays = true;
1630             userData->cacheSize = size;
1631         }
1632     }
1633
1634     if (recreateVertexArrays) {
1635         vertexCoordinates->clear();
1636         textureCoordinates->clear();
1637
1638         bool supportsSubPixelPositions = staticTextItem->fontEngine()->supportsSubPixelPositions();
1639         for (int i=0; i<staticTextItem->numGlyphs; ++i) {
1640             QFixed subPixelPosition;
1641             if (supportsSubPixelPositions)
1642                 subPixelPosition = cache->subPixelPositionForX(staticTextItem->glyphPositions[i].x);
1643
1644             QTextureGlyphCache::GlyphAndSubPixelPosition glyph(staticTextItem->glyphs[i], subPixelPosition);
1645
1646             const QTextureGlyphCache::Coord &c = cache->coords[glyph];
1647             if (c.isNull())
1648                 continue;
1649
1650             int x = qFloor(staticTextItem->glyphPositions[i].x) + c.baseLineX - margin;
1651             int y = qFloor(staticTextItem->glyphPositions[i].y) - c.baseLineY - margin;
1652
1653             vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
1654             textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
1655         }
1656
1657         staticTextItem->userDataNeedsUpdate = false;
1658     }
1659
1660     int numGlyphs = vertexCoordinates->vertexCount() / 4;
1661     if (numGlyphs == 0)
1662         return;
1663
1664     if (elementIndices.size() < numGlyphs*6) {
1665         Q_ASSERT(elementIndices.size() % 6 == 0);
1666         int j = elementIndices.size() / 6 * 4;
1667         while (j < numGlyphs*4) {
1668             elementIndices.append(j + 0);
1669             elementIndices.append(j + 0);
1670             elementIndices.append(j + 1);
1671             elementIndices.append(j + 2);
1672             elementIndices.append(j + 3);
1673             elementIndices.append(j + 3);
1674
1675             j += 4;
1676         }
1677
1678 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1679         if (elementIndicesVBOId == 0)
1680             glGenBuffers(1, &elementIndicesVBOId);
1681
1682         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1683         glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort),
1684                      elementIndices.constData(), GL_STATIC_DRAW);
1685 #endif
1686     } else {
1687 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1688         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1689 #endif
1690     }
1691
1692     setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data());
1693     setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data());
1694
1695     if (!snapToPixelGrid) {
1696         snapToPixelGrid = true;
1697         matrixDirty = true;
1698     }
1699
1700     QBrush pensBrush = q->state()->pen.brush();
1701     setBrush(pensBrush);
1702
1703     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1704
1705         // Subpixel antialiasing without gamma correction
1706
1707         QPainter::CompositionMode compMode = q->state()->composition_mode;
1708         Q_ASSERT(compMode == QPainter::CompositionMode_Source
1709             || compMode == QPainter::CompositionMode_SourceOver);
1710
1711         shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass1);
1712
1713         if (pensBrush.style() == Qt::SolidPattern) {
1714             // Solid patterns can get away with only one pass.
1715             QColor c = pensBrush.color();
1716             qreal oldOpacity = q->state()->opacity;
1717             if (compMode == QPainter::CompositionMode_Source) {
1718                 c = qt_premultiplyColor(c, q->state()->opacity);
1719                 q->state()->opacity = 1;
1720                 opacityUniformDirty = true;
1721             }
1722
1723             compositionModeDirty = false; // I can handle this myself, thank you very much
1724             prepareForDraw(false); // Text always causes src pixels to be transparent
1725
1726             // prepareForDraw() have set the opacity on the current shader, so the opacity state can now be reset.
1727             if (compMode == QPainter::CompositionMode_Source) {
1728                 q->state()->opacity = oldOpacity;
1729                 opacityUniformDirty = true;
1730             }
1731
1732             glEnable(GL_BLEND);
1733             glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
1734             funcs.glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
1735         } else {
1736             // Other brush styles need two passes.
1737
1738             qreal oldOpacity = q->state()->opacity;
1739             if (compMode == QPainter::CompositionMode_Source) {
1740                 q->state()->opacity = 1;
1741                 opacityUniformDirty = true;
1742                 pensBrush = Qt::white;
1743                 setBrush(pensBrush);
1744             }
1745
1746             compositionModeDirty = false; // I can handle this myself, thank you very much
1747             prepareForDraw(false); // Text always causes src pixels to be transparent
1748             glEnable(GL_BLEND);
1749             glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1750
1751             funcs.glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1752             glBindTexture(GL_TEXTURE_2D, cache->texture());
1753             updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
1754
1755 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1756             glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
1757 #else
1758             glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
1759 #endif
1760
1761             shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2);
1762
1763             if (compMode == QPainter::CompositionMode_Source) {
1764                 q->state()->opacity = oldOpacity;
1765                 opacityUniformDirty = true;
1766                 pensBrush = q->state()->pen.brush();
1767                 setBrush(pensBrush);
1768             }
1769
1770             compositionModeDirty = false;
1771             prepareForDraw(false); // Text always causes src pixels to be transparent
1772             glEnable(GL_BLEND);
1773             glBlendFunc(GL_ONE, GL_ONE);
1774         }
1775         compositionModeDirty = true;
1776     } else {
1777         // Greyscale/mono glyphs
1778
1779         shaderManager->setMaskType(QOpenGLEngineShaderManager::PixelMask);
1780         prepareForDraw(false); // Text always causes src pixels to be transparent
1781     }
1782
1783     QOpenGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate)?QOpenGLTextureGlyphCache::Linear:QOpenGLTextureGlyphCache::Nearest;
1784     if (lastMaskTextureUsed != cache->texture() || cache->filterMode() != filterMode) {
1785
1786         funcs.glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1787         if (lastMaskTextureUsed != cache->texture()) {
1788             glBindTexture(GL_TEXTURE_2D, cache->texture());
1789             lastMaskTextureUsed = cache->texture();
1790         }
1791
1792         if (cache->filterMode() != filterMode) {
1793             if (filterMode == QOpenGLTextureGlyphCache::Linear) {
1794                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1795                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1796             } else {
1797                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1798                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1799             }
1800             cache->setFilterMode(filterMode);
1801         }
1802     }
1803
1804     bool srgbFrameBufferEnabled = false;
1805     if (funcs.hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer)) {
1806 #if defined(Q_WS_MAC)
1807         if (glyphType == QFontEngineGlyphCache::Raster_RGBMask)
1808 #elif defined(Q_WS_WIN)
1809         if (glyphType != QFontEngineGlyphCache::Raster_RGBMask || fontSmoothingApproximately(2.1))
1810 #else
1811         if (false)
1812 #endif
1813         {
1814             glEnable(GL_FRAMEBUFFER_SRGB);
1815             srgbFrameBufferEnabled = true;
1816         }
1817     }
1818
1819 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1820     glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
1821     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1822 #else
1823     glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
1824 #endif
1825
1826     if (srgbFrameBufferEnabled)
1827         glDisable(GL_FRAMEBUFFER_SRGB);
1828
1829 }
1830
1831 void QOpenGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
1832                                             QPainter::PixmapFragmentHints hints)
1833 {
1834     Q_D(QOpenGL2PaintEngineEx);
1835     // Use fallback for extended composition modes.
1836     if (state()->composition_mode > QPainter::CompositionMode_Plus) {
1837         QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
1838         return;
1839     }
1840
1841     ensureActive();
1842     int max_texture_size = d->ctx->d_func()->maxTextureSize();
1843     if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) {
1844         QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1845         d->drawPixmapFragments(fragments, fragmentCount, scaled, hints);
1846     } else {
1847         d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
1848     }
1849 }
1850
1851
1852 void QOpenGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments,
1853                                                    int fragmentCount, const QPixmap &pixmap,
1854                                                    QPainter::PixmapFragmentHints hints)
1855 {
1856     GLfloat dx = 1.0f / pixmap.size().width();
1857     GLfloat dy = 1.0f / pixmap.size().height();
1858
1859     vertexCoordinateArray.clear();
1860     textureCoordinateArray.clear();
1861     opacityArray.reset();
1862
1863     if (snapToPixelGrid) {
1864         snapToPixelGrid = false;
1865         matrixDirty = true;
1866     }
1867
1868     bool allOpaque = true;
1869
1870     for (int i = 0; i < fragmentCount; ++i) {
1871         qreal s = 0;
1872         qreal c = 1;
1873         if (fragments[i].rotation != 0) {
1874             s = qFastSin(fragments[i].rotation * Q_PI / 180);
1875             c = qFastCos(fragments[i].rotation * Q_PI / 180);
1876         }
1877
1878         qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
1879         qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
1880         QOpenGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
1881         QOpenGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
1882
1883         vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
1884         vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y);
1885         vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
1886         vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
1887         vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
1888         vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
1889
1890         QOpenGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
1891                     (fragments[i].sourceLeft + fragments[i].width) * dx,
1892                     (fragments[i].sourceTop + fragments[i].height) * dy);
1893
1894         textureCoordinateArray.addVertex(src.right, src.bottom);
1895         textureCoordinateArray.addVertex(src.right, src.top);
1896         textureCoordinateArray.addVertex(src.left, src.top);
1897         textureCoordinateArray.addVertex(src.left, src.top);
1898         textureCoordinateArray.addVertex(src.left, src.bottom);
1899         textureCoordinateArray.addVertex(src.right, src.bottom);
1900
1901         qreal opacity = fragments[i].opacity * q->state()->opacity;
1902         opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
1903         allOpaque &= (opacity >= 0.99f);
1904     }
1905
1906     funcs.glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1907     QOpenGLTexture *texture = 0;//ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
1908                                 //                     QOpenGLContext::InternalBindOption
1909                                 //                     | QOpenGLContext::CanFlipNativePixmapBindOption);
1910
1911     if (texture->invertedY()) {
1912         // Flip texture y-coordinate.
1913         QOpenGLPoint *data = textureCoordinateArray.data();
1914         for (int i = 0; i < 6 * fragmentCount; ++i)
1915             data[i].y = 1 - data[i].y;
1916     }
1917
1918     transferMode(ImageArrayDrawingMode);
1919
1920     bool isBitmap = pixmap.isQBitmap();
1921     bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque;
1922
1923     updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1924                            q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id());
1925
1926     // Setup for texture drawing
1927     currentBrush = noBrush;
1928     shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc
1929                                             : QOpenGLEngineShaderManager::ImageSrc);
1930     if (prepareForDraw(isOpaque))
1931         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
1932
1933     if (isBitmap) {
1934         QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
1935         shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
1936     }
1937
1938     glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
1939 }
1940
1941 bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev)
1942 {
1943     Q_D(QOpenGL2PaintEngineEx);
1944
1945 //     qDebug("QOpenGL2PaintEngineEx::begin()");
1946     if (pdev->devType() == QInternal::OpenGL)
1947         d->device = static_cast<QOpenGLPaintDevice*>(pdev);
1948     else
1949         d->device = QOpenGLPaintDevice::getDevice(pdev);
1950
1951     if (!d->device)
1952         return false;
1953
1954     if (d->device->group() != QOpenGLContextGroup::currentContextGroup()) {
1955         qWarning("QPainter::begin(): OpenGL resource not valid in current context");
1956         return false;
1957     }
1958
1959     d->ctx = QOpenGLContext::currentContext();
1960     d->ctx->d_func()->active_engine = this;
1961
1962     d->funcs.initializeGLFunctions();
1963
1964     for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
1965         d->vertexAttributeArraysEnabledState[i] = false;
1966
1967     const QSize sz = d->device->size();
1968     d->width = sz.width();
1969     d->height = sz.height();
1970     d->mode = BrushDrawingMode;
1971     d->brushTextureDirty = true;
1972     d->brushUniformsDirty = true;
1973     d->matrixUniformDirty = true;
1974     d->matrixDirty = true;
1975     d->compositionModeDirty = true;
1976     d->opacityUniformDirty = true;
1977     d->needsSync = true;
1978     d->useSystemClip = !systemClip().isEmpty();
1979     d->currentBrush = QBrush();
1980
1981     d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
1982     d->stencilClean = true;
1983
1984     // Calling begin paint should make the correct context current. So, any
1985     // code which calls into GL or otherwise needs a current context *must*
1986     // go after beginPaint:
1987     d->device->beginPaint();
1988     d->shaderManager = new QOpenGLEngineShaderManager(d->ctx);
1989
1990     glDisable(GL_STENCIL_TEST);
1991     glDisable(GL_DEPTH_TEST);
1992     glDisable(GL_SCISSOR_TEST);
1993
1994 #if !defined(QT_OPENGL_ES_2)
1995     glDisable(GL_MULTISAMPLE);
1996 #endif
1997
1998     d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
1999
2000 #if !defined(QT_OPENGL_ES_2)
2001 #if defined(Q_WS_WIN)
2002     if (qt_cleartype_enabled
2003         && (fontSmoothingApproximately(1.0) || fontSmoothingApproximately(2.1)))
2004 #endif
2005 #if defined(Q_WS_MAC)
2006     if (qt_applefontsmoothing_enabled)
2007 #endif
2008         d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
2009 #endif
2010
2011 #if defined(QT_OPENGL_ES_2)
2012     // OpenGL ES can't switch MSAA off, so if the gl paint device is
2013     // multisampled, it's always multisampled.
2014     d->multisamplingAlwaysEnabled = d->device->format().samples() > 1;
2015 #else
2016     d->multisamplingAlwaysEnabled = false;
2017 #endif
2018
2019     return true;
2020 }
2021
2022 bool QOpenGL2PaintEngineEx::end()
2023 {
2024     Q_D(QOpenGL2PaintEngineEx);
2025
2026     QOpenGLContext *ctx = d->ctx;
2027     d->funcs.glUseProgram(0);
2028     d->transferMode(BrushDrawingMode);
2029     d->device->endPaint();
2030
2031     ctx->d_func()->active_engine = 0;
2032
2033     d->resetGLState();
2034
2035     delete d->shaderManager;
2036     d->shaderManager = 0;
2037     d->currentBrush = QBrush();
2038
2039 #ifdef QT_OPENGL_CACHE_AS_VBOS
2040     if (!d->unusedVBOSToClean.isEmpty()) {
2041         glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
2042         d->unusedVBOSToClean.clear();
2043     }
2044     if (!d->unusedIBOSToClean.isEmpty()) {
2045         glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData());
2046         d->unusedIBOSToClean.clear();
2047     }
2048 #endif
2049
2050     return false;
2051 }
2052
2053 void QOpenGL2PaintEngineEx::ensureActive()
2054 {
2055     Q_D(QOpenGL2PaintEngineEx);
2056     QOpenGLContext *ctx = d->ctx;
2057
2058     if (isActive() && ctx->d_func()->active_engine != this) {
2059         ctx->d_func()->active_engine = this;
2060         d->needsSync = true;
2061     }
2062
2063     d->device->ensureActiveTarget();
2064
2065     if (d->needsSync) {
2066         d->transferMode(BrushDrawingMode);
2067         glViewport(0, 0, d->width, d->height);
2068         d->needsSync = false;
2069         d->lastMaskTextureUsed = 0;
2070         d->shaderManager->setDirty();
2071         d->syncGlState();
2072         for (int i = 0; i < 3; ++i)
2073             d->vertexAttribPointers[i] = (GLfloat*)-1; // Assume the pointers are clobbered
2074         setState(state());
2075     }
2076 }
2077
2078 void QOpenGL2PaintEngineExPrivate::updateClipScissorTest()
2079 {
2080     Q_Q(QOpenGL2PaintEngineEx);
2081     if (q->state()->clipTestEnabled) {
2082         glEnable(GL_STENCIL_TEST);
2083         glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
2084     } else {
2085         glDisable(GL_STENCIL_TEST);
2086         glStencilFunc(GL_ALWAYS, 0, 0xff);
2087     }
2088
2089 #ifdef QT_GL_NO_SCISSOR_TEST
2090     currentScissorBounds = QRect(0, 0, width, height);
2091 #else
2092     QRect bounds = q->state()->rectangleClip;
2093     if (!q->state()->clipEnabled) {
2094         if (useSystemClip)
2095             bounds = systemClip.boundingRect();
2096         else
2097             bounds = QRect(0, 0, width, height);
2098     } else {
2099         if (useSystemClip)
2100             bounds = bounds.intersected(systemClip.boundingRect());
2101         else
2102             bounds = bounds.intersected(QRect(0, 0, width, height));
2103     }
2104
2105     currentScissorBounds = bounds;
2106
2107     if (bounds == QRect(0, 0, width, height)) {
2108         glDisable(GL_SCISSOR_TEST);
2109     } else {
2110         glEnable(GL_SCISSOR_TEST);
2111         setScissor(bounds);
2112     }
2113 #endif
2114 }
2115
2116 void QOpenGL2PaintEngineExPrivate::setScissor(const QRect &rect)
2117 {
2118     const int left = rect.left();
2119     const int width = rect.width();
2120     int bottom = height - (rect.top() + rect.height());
2121     if (device->isFlipped()) {
2122         bottom = rect.top();
2123     }
2124     const int height = rect.height();
2125
2126     glScissor(left, bottom, width, height);
2127 }
2128
2129 void QOpenGL2PaintEngineEx::clipEnabledChanged()
2130 {
2131     Q_D(QOpenGL2PaintEngineEx);
2132
2133     state()->clipChanged = true;
2134
2135     if (painter()->hasClipping())
2136         d->regenerateClip();
2137     else
2138         d->systemStateChanged();
2139 }
2140
2141 void QOpenGL2PaintEngineExPrivate::clearClip(uint value)
2142 {
2143     dirtyStencilRegion -= currentScissorBounds;
2144
2145     glStencilMask(0xff);
2146     glClearStencil(value);
2147     glClear(GL_STENCIL_BUFFER_BIT);
2148     glStencilMask(0x0);
2149
2150     q->state()->needsClipBufferClear = false;
2151 }
2152
2153 void QOpenGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value)
2154 {
2155     transferMode(BrushDrawingMode);
2156
2157     if (snapToPixelGrid) {
2158         snapToPixelGrid = false;
2159         matrixDirty = true;
2160     }
2161
2162     if (matrixDirty)
2163         updateMatrix();
2164
2165     stencilClean = false;
2166
2167     const bool singlePass = !path.hasWindingFill()
2168         && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
2169             || q->state()->needsClipBufferClear);
2170     const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
2171
2172     if (q->state()->needsClipBufferClear)
2173         clearClip(1);
2174
2175     if (path.isEmpty()) {
2176         glEnable(GL_STENCIL_TEST);
2177         glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2178         return;
2179     }
2180
2181     if (q->state()->clipTestEnabled)
2182         glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
2183     else
2184         glStencilFunc(GL_ALWAYS, 0, 0xff);
2185
2186     vertexCoordinateArray.clear();
2187     vertexCoordinateArray.addPath(path, inverseScale, false);
2188
2189     if (!singlePass)
2190         fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
2191
2192     glColorMask(false, false, false, false);
2193     glEnable(GL_STENCIL_TEST);
2194     useSimpleShader();
2195
2196     if (singlePass) {
2197         // Under these conditions we can set the new stencil value in a single
2198         // pass, by using the current value and the "new value" as the toggles
2199
2200         glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT);
2201         glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
2202         glStencilMask(value ^ referenceClipValue);
2203
2204         drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
2205     } else {
2206         glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
2207         glStencilMask(0xff);
2208
2209         if (!q->state()->clipTestEnabled && path.hasWindingFill()) {
2210             // Pass when any clip bit is set, set high bit
2211             glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT);
2212             composite(vertexCoordinateArray.boundingRect());
2213         }
2214
2215         // Pass when high bit is set, replace stencil value with new clip value
2216         glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT);
2217
2218         composite(vertexCoordinateArray.boundingRect());
2219     }
2220
2221     glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2222     glStencilMask(0);
2223
2224     glColorMask(true, true, true, true);
2225 }
2226
2227 void QOpenGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
2228 {
2229 //     qDebug("QOpenGL2PaintEngineEx::clip()");
2230     Q_D(QOpenGL2PaintEngineEx);
2231
2232     state()->clipChanged = true;
2233
2234     ensureActive();
2235
2236     if (op == Qt::ReplaceClip) {
2237         op = Qt::IntersectClip;
2238         if (d->hasClipOperations()) {
2239             d->systemStateChanged();
2240             state()->canRestoreClip = false;
2241         }
2242     }
2243
2244 #ifndef QT_GL_NO_SCISSOR_TEST
2245     if (!path.isEmpty() && op == Qt::IntersectClip && (path.shape() == QVectorPath::RectangleHint)) {
2246         const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
2247         QRectF rect(points[0], points[2]);
2248
2249         if (state()->matrix.type() <= QTransform::TxScale
2250             || (state()->matrix.type() == QTransform::TxRotate
2251                 && qFuzzyIsNull(state()->matrix.m11())
2252                 && qFuzzyIsNull(state()->matrix.m22())))
2253         {
2254             state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect());
2255             d->updateClipScissorTest();
2256             return;
2257         }
2258     }
2259 #endif
2260
2261     const QRect pathRect = state()->matrix.mapRect(path.controlPointRect()).toAlignedRect();
2262
2263     switch (op) {
2264     case Qt::NoClip:
2265         if (d->useSystemClip) {
2266             state()->clipTestEnabled = true;
2267             state()->currentClip = 1;
2268         } else {
2269             state()->clipTestEnabled = false;
2270         }
2271         state()->rectangleClip = QRect(0, 0, d->width, d->height);
2272         state()->canRestoreClip = false;
2273         d->updateClipScissorTest();
2274         break;
2275     case Qt::IntersectClip:
2276         state()->rectangleClip = state()->rectangleClip.intersected(pathRect);
2277         d->updateClipScissorTest();
2278         d->resetClipIfNeeded();
2279         ++d->maxClip;
2280         d->writeClip(path, d->maxClip);
2281         state()->currentClip = d->maxClip;
2282         state()->clipTestEnabled = true;
2283         break;
2284     default:
2285         break;
2286     }
2287 }
2288
2289 void QOpenGL2PaintEngineExPrivate::regenerateClip()
2290 {
2291     systemStateChanged();
2292     replayClipOperations();
2293 }
2294
2295 void QOpenGL2PaintEngineExPrivate::systemStateChanged()
2296 {
2297     Q_Q(QOpenGL2PaintEngineEx);
2298
2299     q->state()->clipChanged = true;
2300
2301     if (systemClip.isEmpty()) {
2302         useSystemClip = false;
2303     } else {
2304         if (q->paintDevice()->devType() == QInternal::Widget && currentClipDevice) {
2305             //QWidgetPrivate *widgetPrivate = qt_widget_private(static_cast<QWidget *>(currentClipDevice)->window());
2306             //useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
2307             useSystemClip = true;
2308         } else {
2309             useSystemClip = true;
2310         }
2311     }
2312
2313     q->state()->clipTestEnabled = false;
2314     q->state()->needsClipBufferClear = true;
2315
2316     q->state()->currentClip = 1;
2317     maxClip = 1;
2318
2319     q->state()->rectangleClip = useSystemClip ? systemClip.boundingRect() : QRect(0, 0, width, height);
2320     updateClipScissorTest();
2321
2322     if (systemClip.rectCount() == 1) {
2323         if (systemClip.boundingRect() == QRect(0, 0, width, height))
2324             useSystemClip = false;
2325 #ifndef QT_GL_NO_SCISSOR_TEST
2326         // scissoring takes care of the system clip
2327         return;
2328 #endif
2329     }
2330
2331     if (useSystemClip) {
2332         clearClip(0);
2333
2334         QPainterPath path;
2335         path.addRegion(systemClip);
2336
2337         q->state()->currentClip = 0;
2338         writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1);
2339         q->state()->currentClip = 1;
2340         q->state()->clipTestEnabled = true;
2341     }
2342 }
2343
2344 void QOpenGL2PaintEngineEx::setState(QPainterState *new_state)
2345 {
2346     //     qDebug("QOpenGL2PaintEngineEx::setState()");
2347
2348     Q_D(QOpenGL2PaintEngineEx);
2349
2350     QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state);
2351     QOpenGL2PaintEngineState *old_state = state();
2352
2353     QPaintEngineEx::setState(s);
2354
2355     if (s->isNew) {
2356         // Newly created state object.  The call to setState()
2357         // will either be followed by a call to begin(), or we are
2358         // setting the state as part of a save().
2359         s->isNew = false;
2360         return;
2361     }
2362
2363     // Setting the state as part of a restore().
2364
2365     if (old_state == s || old_state->renderHintsChanged)
2366         renderHintsChanged();
2367
2368     if (old_state == s || old_state->matrixChanged)
2369         d->matrixDirty = true;
2370
2371     if (old_state == s || old_state->compositionModeChanged)
2372         d->compositionModeDirty = true;
2373
2374     if (old_state == s || old_state->opacityChanged)
2375         d->opacityUniformDirty = true;
2376
2377     if (old_state == s || old_state->clipChanged) {
2378         if (old_state && old_state != s && old_state->canRestoreClip) {
2379             d->updateClipScissorTest();
2380             glDepthFunc(GL_LEQUAL);
2381         } else {
2382             d->regenerateClip();
2383         }
2384     }
2385 }
2386
2387 QPainterState *QOpenGL2PaintEngineEx::createState(QPainterState *orig) const
2388 {
2389     if (orig)
2390         const_cast<QOpenGL2PaintEngineEx *>(this)->ensureActive();
2391
2392     QOpenGL2PaintEngineState *s;
2393     if (!orig)
2394         s = new QOpenGL2PaintEngineState();
2395     else
2396         s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig));
2397
2398     s->matrixChanged = false;
2399     s->compositionModeChanged = false;
2400     s->opacityChanged = false;
2401     s->renderHintsChanged = false;
2402     s->clipChanged = false;
2403
2404     return s;
2405 }
2406
2407 QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other)
2408     : QPainterState(other)
2409 {
2410     isNew = true;
2411     needsClipBufferClear = other.needsClipBufferClear;
2412     clipTestEnabled = other.clipTestEnabled;
2413     currentClip = other.currentClip;
2414     canRestoreClip = other.canRestoreClip;
2415     rectangleClip = other.rectangleClip;
2416 }
2417
2418 QOpenGL2PaintEngineState::QOpenGL2PaintEngineState()
2419 {
2420     isNew = true;
2421     needsClipBufferClear = true;
2422     clipTestEnabled = false;
2423     canRestoreClip = true;
2424 }
2425
2426 QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState()
2427 {
2428 }
2429
2430 void QOpenGL2PaintEngineExPrivate::setVertexAttribArrayEnabled(int arrayIndex, bool enabled)
2431 {
2432     Q_ASSERT(arrayIndex < QT_GL_VERTEX_ARRAY_TRACKED_COUNT);
2433
2434     if (vertexAttributeArraysEnabledState[arrayIndex] && !enabled)
2435         funcs.glDisableVertexAttribArray(arrayIndex);
2436
2437     if (!vertexAttributeArraysEnabledState[arrayIndex] && enabled)
2438         funcs.glEnableVertexAttribArray(arrayIndex);
2439
2440     vertexAttributeArraysEnabledState[arrayIndex] = enabled;
2441 }
2442
2443 void QOpenGL2PaintEngineExPrivate::syncGlState()
2444 {
2445     for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) {
2446         if (vertexAttributeArraysEnabledState[i])
2447             funcs.glEnableVertexAttribArray(i);
2448         else
2449             funcs.glDisableVertexAttribArray(i);
2450     }
2451 }
2452
2453
2454 QT_END_NAMESPACE