Merge branch 'master' of scm.dev.nokia.troll.no:qt/qtbase-staging
[profile/ivi/qtbase.git] / src / opengl / gl2paintengineex / 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 QtOpenGL module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
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 "qglgradientcache_p.h"
68 #include "qpaintengineex_opengl2_p.h"
69
70 #include <string.h> //for memcpy
71 #include <qmath.h>
72
73 #include <private/qgl_p.h>
74 #include <private/qmath_p.h>
75 #include <private/qpaintengineex_p.h>
76 #include <QPaintEngine>
77 #include <private/qpainter_p.h>
78 #include <private/qfontengine_p.h>
79 #include <private/qpixmapdata_gl_p.h>
80 #include <private/qdatabuffer_p.h>
81 #include <private/qstatictext_p.h>
82 #include <private/qtriangulator_p.h>
83
84 #include "qglengineshadermanager_p.h"
85 #include "qgl2pexvertexarray_p.h"
86 #include "qtriangulatingstroker_p.h"
87 #include "qtextureglyphcache_gl_p.h"
88
89 #include <QDebug>
90
91 QT_BEGIN_NAMESPACE
92
93 #if defined(Q_WS_WIN)
94 extern Q_GUI_EXPORT bool qt_cleartype_enabled;
95 #endif
96
97 #ifdef Q_WS_MAC
98 extern bool qt_applefontsmoothing_enabled;
99 #endif
100
101 #if !defined(QT_MAX_CACHED_GLYPH_SIZE)
102 #  define QT_MAX_CACHED_GLYPH_SIZE 64
103 #endif
104
105 Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
106
107 ////////////////////////////////// Private Methods //////////////////////////////////////////
108
109 QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
110 {
111     delete shaderManager;
112
113     while (pathCaches.size()) {
114         QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
115         e->cleanup(e->engine, e->data);
116         e->data = 0;
117         e->engine = 0;
118     }
119
120     if (elementIndicesVBOId != 0) {
121         glDeleteBuffers(1, &elementIndicesVBOId);
122         elementIndicesVBOId = 0;
123     }
124 }
125
126 void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
127 {
128 //    glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit?
129     if (id != GLuint(-1) && id == lastTextureUsed)
130         return;
131
132     lastTextureUsed = id;
133
134     if (smoothPixmapTransform) {
135         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
136         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
137     } else {
138         glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
139         glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
140     }
141     glTexParameterf(target, GL_TEXTURE_WRAP_S, wrapMode);
142     glTexParameterf(target, GL_TEXTURE_WRAP_T, wrapMode);
143 }
144
145
146 inline QColor qt_premultiplyColor(QColor c, GLfloat opacity)
147 {
148     qreal alpha = c.alphaF() * opacity;
149     c.setAlphaF(alpha);
150     c.setRedF(c.redF() * alpha);
151     c.setGreenF(c.greenF() * alpha);
152     c.setBlueF(c.blueF() * alpha);
153     return c;
154 }
155
156
157 void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush)
158 {
159     if (qbrush_fast_equals(currentBrush, brush))
160         return;
161
162     const Qt::BrushStyle newStyle = qbrush_style(brush);
163     Q_ASSERT(newStyle != Qt::NoBrush);
164
165     currentBrush = brush;
166     if (!currentBrushPixmap.isNull())
167         currentBrushPixmap = QPixmap();
168     brushUniformsDirty = true; // All brushes have at least one uniform
169
170     if (newStyle > Qt::SolidPattern)
171         brushTextureDirty = true;
172
173     if (currentBrush.style() == Qt::TexturePattern
174         && qHasPixmapTexture(brush) && brush.texture().isQBitmap())
175     {
176         shaderManager->setSrcPixelType(QGLEngineShaderManager::TextureSrcWithPattern);
177     } else {
178         shaderManager->setSrcPixelType(newStyle);
179     }
180     shaderManager->optimiseForBrushTransform(currentBrush.transform().type());
181 }
182
183
184 void QGL2PaintEngineExPrivate::useSimpleShader()
185 {
186     shaderManager->useSimpleProgram();
187
188     if (matrixDirty)
189         updateMatrix();
190 }
191
192 void QGL2PaintEngineExPrivate::updateBrushTexture()
193 {
194     Q_Q(QGL2PaintEngineEx);
195 //     qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()");
196     Qt::BrushStyle style = currentBrush.style();
197
198     if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
199         // Get the image data for the pattern
200         QImage texImage = qt_imageForBrush(style, false);
201
202         glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
203         ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
204         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
205     }
206     else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
207         // Gradiant brush: All the gradiants use the same texture
208
209         const QGradient* g = currentBrush.gradient();
210
211         // We apply global opacity in the fragment shaders, so we always pass 1.0
212         // for opacity to the cache.
213         GLuint texId = QGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0);
214
215         glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
216         glBindTexture(GL_TEXTURE_2D, texId);
217
218         if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
219             updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
220         else if (g->spread() == QGradient::ReflectSpread)
221             updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, q->state()->renderHints & QPainter::SmoothPixmapTransform);
222         else
223             updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform);
224     }
225     else if (style == Qt::TexturePattern) {
226         currentBrushPixmap = currentBrush.texture();
227
228         int max_texture_size = ctx->d_func()->maxTextureSize();
229         if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size)
230             currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
231
232         glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
233         QGLTexture *tex = ctx->d_func()->bindTexture(currentBrushPixmap, GL_TEXTURE_2D, GL_RGBA,
234                                                      QGLContext::InternalBindOption |
235                                                      QGLContext::CanFlipNativePixmapBindOption);
236         updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform);
237         textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1;
238     }
239     brushTextureDirty = false;
240 }
241
242
243 void QGL2PaintEngineExPrivate::updateBrushUniforms()
244 {
245 //     qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()");
246     Qt::BrushStyle style = currentBrush.style();
247
248     if (style == Qt::NoBrush)
249         return;
250
251     QTransform brushQTransform = currentBrush.transform();
252
253     if (style == Qt::SolidPattern) {
254         QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
255         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col);
256     }
257     else {
258         // All other brushes have a transform and thus need the translation point:
259         QPointF translationPoint;
260
261         if (style <= Qt::DiagCrossPattern) {
262             QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
263
264             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
265
266             QVector2D halfViewportSize(width*0.5, height*0.5);
267             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
268         }
269         else if (style == Qt::LinearGradientPattern) {
270             const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient());
271
272             QPointF realStart = g->start();
273             QPointF realFinal = g->finalStop();
274             translationPoint = realStart;
275
276             QPointF l = realFinal - realStart;
277
278             QVector3D linearData(
279                 l.x(),
280                 l.y(),
281                 1.0f / (l.x() * l.x() + l.y() * l.y())
282             );
283
284             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::LinearData), linearData);
285
286             QVector2D halfViewportSize(width*0.5, height*0.5);
287             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
288         }
289         else if (style == Qt::ConicalGradientPattern) {
290             const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient());
291             translationPoint   = g->center();
292
293             GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0;
294
295             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Angle), angle);
296
297             QVector2D halfViewportSize(width*0.5, height*0.5);
298             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
299         }
300         else if (style == Qt::RadialGradientPattern) {
301             const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient());
302             QPointF realCenter = g->center();
303             QPointF realFocal  = g->focalPoint();
304             qreal   realRadius = g->radius();
305             translationPoint   = realFocal;
306
307             QPointF fmp = realCenter - realFocal;
308             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp), fmp);
309
310             GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
311             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
312             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Inverse2Fmp2MRadius2),
313                                                              GLfloat(1.0 / (2.0*fmp2_m_radius2)));
314
315             QVector2D halfViewportSize(width*0.5, height*0.5);
316             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
317         }
318         else if (style == Qt::TexturePattern) {
319             const QPixmap& texPixmap = currentBrush.texture();
320
321             if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) {
322                 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
323                 shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
324             }
325
326             QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
327             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
328
329             QVector2D halfViewportSize(width*0.5, height*0.5);
330             shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
331         }
332         else
333             qWarning("QGL2PaintEngineEx: Unimplemented fill style");
334
335         const QPointF &brushOrigin = q->state()->brushOrigin;
336         QTransform matrix = q->state()->matrix;
337         matrix.translate(brushOrigin.x(), brushOrigin.y());
338
339         QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
340         qreal m22 = -1;
341         qreal dy = height;
342         if (device->isFlipped()) {
343             m22 = 1;
344             dy = 0;
345         }
346         QTransform gl_to_qt(1, 0, 0, m22, 0, dy);
347         QTransform inv_matrix;
348         if (style == Qt::TexturePattern && textureInvertedY == -1)
349             inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate;
350         else
351             inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
352
353         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix);
354         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
355     }
356     brushUniformsDirty = false;
357 }
358
359
360 // This assumes the shader manager has already setup the correct shader program
361 void QGL2PaintEngineExPrivate::updateMatrix()
362 {
363 //     qDebug("QGL2PaintEngineExPrivate::updateMatrix()");
364
365     const QTransform& transform = q->state()->matrix;
366
367     // The projection matrix converts from Qt's coordinate system to GL's coordinate system
368     //    * GL's viewport is 2x2, Qt's is width x height
369     //    * GL has +y -> -y going from bottom -> top, Qt is the other way round
370     //    * GL has [0,0] in the center, Qt has it in the top-left
371     //
372     // This results in the Projection matrix below, which is multiplied by the painter's
373     // transformation matrix, as shown below:
374     //
375     //                Projection Matrix                      Painter Transform
376     // ------------------------------------------------   ------------------------
377     // | 2.0 / width  |      0.0      |     -1.0      |   |  m11  |  m21  |  dx  |
378     // |     0.0      | -2.0 / height |      1.0      | * |  m12  |  m22  |  dy  |
379     // |     0.0      |      0.0      |      1.0      |   |  m13  |  m23  |  m33 |
380     // ------------------------------------------------   ------------------------
381     //
382     // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies
383
384     const GLfloat wfactor = 2.0f / width;
385     GLfloat hfactor = -2.0f / height;
386
387     GLfloat dx = transform.dx();
388     GLfloat dy = transform.dy();
389
390     if (device->isFlipped()) {
391         hfactor *= -1;
392         dy -= height;
393     }
394
395     // Non-integer translates can have strange effects for some rendering operations such as
396     // anti-aliased text rendering. In such cases, we snap the translate to the pixel grid.
397     if (snapToPixelGrid && transform.type() == QTransform::TxTranslate) {
398         // 0.50 needs to rounded down to 0.0 for consistency with raster engine:
399         dx = ceilf(dx - 0.5f);
400         dy = ceilf(dy - 0.5f);
401     }
402     pmvMatrix[0][0] = (wfactor * transform.m11())  - transform.m13();
403     pmvMatrix[1][0] = (wfactor * transform.m21())  - transform.m23();
404     pmvMatrix[2][0] = (wfactor * dx) - transform.m33();
405     pmvMatrix[0][1] = (hfactor * transform.m12())  + transform.m13();
406     pmvMatrix[1][1] = (hfactor * transform.m22())  + transform.m23();
407     pmvMatrix[2][1] = (hfactor * dy) + transform.m33();
408     pmvMatrix[0][2] = transform.m13();
409     pmvMatrix[1][2] = transform.m23();
410     pmvMatrix[2][2] = transform.m33();
411
412     // 1/10000 == 0.0001, so we have good enough res to cover curves
413     // that span the entire widget...
414     inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())),
415                                   qMax(qAbs(transform.m12()), qAbs(transform.m21())) ),
416                         qreal(0.0001));
417
418     matrixDirty = false;
419     matrixUniformDirty = true;
420
421     // Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only
422     // need to do this once for every matrix change and persists across all shader programs.
423     glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]);
424     glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]);
425     glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]);
426
427     dasher.setInvScale(inverseScale);
428     stroker.setInvScale(inverseScale);
429 }
430
431
432 void QGL2PaintEngineExPrivate::updateCompositionMode()
433 {
434     // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
435     //       composition modes look odd.
436 //     qDebug() << "QGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
437     switch(q->state()->composition_mode) {
438     case QPainter::CompositionMode_SourceOver:
439         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
440         break;
441     case QPainter::CompositionMode_DestinationOver:
442         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
443         break;
444     case QPainter::CompositionMode_Clear:
445         glBlendFunc(GL_ZERO, GL_ZERO);
446         break;
447     case QPainter::CompositionMode_Source:
448         glBlendFunc(GL_ONE, GL_ZERO);
449         break;
450     case QPainter::CompositionMode_Destination:
451         glBlendFunc(GL_ZERO, GL_ONE);
452         break;
453     case QPainter::CompositionMode_SourceIn:
454         glBlendFunc(GL_DST_ALPHA, GL_ZERO);
455         break;
456     case QPainter::CompositionMode_DestinationIn:
457         glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
458         break;
459     case QPainter::CompositionMode_SourceOut:
460         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
461         break;
462     case QPainter::CompositionMode_DestinationOut:
463         glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
464         break;
465     case QPainter::CompositionMode_SourceAtop:
466         glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
467         break;
468     case QPainter::CompositionMode_DestinationAtop:
469         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
470         break;
471     case QPainter::CompositionMode_Xor:
472         glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
473         break;
474     case QPainter::CompositionMode_Plus:
475         glBlendFunc(GL_ONE, GL_ONE);
476         break;
477     default:
478         qWarning("Unsupported composition mode");
479         break;
480     }
481
482     compositionModeDirty = false;
483 }
484
485 static inline void setCoords(GLfloat *coords, const QGLRect &rect)
486 {
487     coords[0] = rect.left;
488     coords[1] = rect.top;
489     coords[2] = rect.right;
490     coords[3] = rect.top;
491     coords[4] = rect.right;
492     coords[5] = rect.bottom;
493     coords[6] = rect.left;
494     coords[7] = rect.bottom;
495 }
496
497 void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern)
498 {
499     // Setup for texture drawing
500     currentBrush = noBrush;
501     shaderManager->setSrcPixelType(pattern ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc);
502
503     if (snapToPixelGrid) {
504         snapToPixelGrid = false;
505         matrixDirty = true;
506     }
507
508     if (prepareForDraw(opaque))
509         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
510
511     if (pattern) {
512         QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
513         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
514     }
515
516     GLfloat dx = 1.0 / textureSize.width();
517     GLfloat dy = 1.0 / textureSize.height();
518
519     QGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy);
520
521     setCoords(staticVertexCoordinateArray, dest);
522     setCoords(staticTextureCoordinateArray, srcTextureRect);
523
524     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
525 }
526
527 void QGL2PaintEngineEx::beginNativePainting()
528 {
529     Q_D(QGL2PaintEngineEx);
530     ensureActive();
531     d->transferMode(BrushDrawingMode);
532
533     d->nativePaintingActive = true;
534
535     QGLContext *ctx = d->ctx;
536     glUseProgram(0);
537
538     // Disable all the vertex attribute arrays:
539     for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
540         glDisableVertexAttribArray(i);
541
542 #ifndef QT_OPENGL_ES_2
543     const QGLFormat &fmt = d->device->format();
544     if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1)
545         || fmt.profile() == QGLFormat::CompatibilityProfile)
546     {
547         // be nice to people who mix OpenGL 1.x code with QPainter commands
548         // by setting modelview and projection matrices to mirror the GL 1
549         // paint engine
550         const QTransform& mtx = state()->matrix;
551
552         float mv_matrix[4][4] =
553         {
554             { float(mtx.m11()), float(mtx.m12()),     0, float(mtx.m13()) },
555             { float(mtx.m21()), float(mtx.m22()),     0, float(mtx.m23()) },
556             {                0,                0,     1,                0 },
557             {  float(mtx.dx()),  float(mtx.dy()),     0, float(mtx.m33()) }
558         };
559
560         const QSize sz = d->device->size();
561
562         glMatrixMode(GL_PROJECTION);
563         glLoadIdentity();
564         glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999);
565
566         glMatrixMode(GL_MODELVIEW);
567         glLoadMatrixf(&mv_matrix[0][0]);
568     }
569 #else
570     Q_UNUSED(ctx);
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 QGL2PaintEngineExPrivate::resetGLState()
583 {
584     glDisable(GL_BLEND);
585     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     glClearDepth(1);
592     glStencilMask(0xff);
593     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
594     glStencilFunc(GL_ALWAYS, 0, 0xff);
595     ctx->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
596     ctx->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false);
597     ctx->d_func()->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     glVertexAttrib4fv(3, color);
602 #endif
603 }
604
605 void QGL2PaintEngineEx::endNativePainting()
606 {
607     Q_D(QGL2PaintEngineEx);
608     d->needsSync = true;
609     d->nativePaintingActive = false;
610 }
611
612 void QGL2PaintEngineEx::invalidateState()
613 {
614     Q_D(QGL2PaintEngineEx);
615     d->needsSync = true;
616 }
617
618 bool QGL2PaintEngineEx::isNativePaintingActive() const {
619     Q_D(const QGL2PaintEngineEx);
620     return d->nativePaintingActive;
621 }
622
623 void QGL2PaintEngineExPrivate::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(QGLEngineShaderManager::NoMask);
652
653     mode = newMode;
654 }
655
656 struct QGL2PEVectorPathCache
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 QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data)
672 {
673     QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
674 #ifdef QT_OPENGL_CACHE_AS_VBOS
675     Q_ASSERT(engine->type() == QPaintEngine::OpenGL2);
676     static_cast<QGL2PaintEngineEx *>(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 QGL2PaintEngineExPrivate::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         QGLRect 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             QGL2PEVectorPathCache *cache;
713
714             bool updateCache = false;
715
716             if (data) {
717                 cache = (QGL2PEVectorPathCache *) 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 QGL2PEVectorPathCache;
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             QGL2PEVectorPathCache *cache;
794
795             bool updateCache = false;
796
797             if (data) {
798                 cache = (QGL2PEVectorPathCache *) 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 QGL2PEVectorPathCache;
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 (QGLExtensions::glExtensions() & QGLExtensions::ElementIndexUint)
833                     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
834                 else
835                     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                 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 (QGLExtensions::glExtensions() & QGLExtensions::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 (QGLExtensions::glExtensions() & QGLExtensions::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 (QGLExtensions::glExtensions() & QGLExtensions::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().stencil()) {
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 (QGLExtensions::glExtensions() & QGLExtensions::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 QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
939                                                           int count,
940                                                           int *stops,
941                                                           int stopCount,
942                                                           const QGLRect &bounds,
943                                                           StencilFillMode mode)
944 {
945     Q_ASSERT(count || stops);
946
947 //     qDebug("QGL2PaintEngineExPrivate::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         glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
989         // Dec. for back-facing "holes"
990         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 QGL2PaintEngineExPrivate::resetClipIfNeeded()
1037 {
1038     if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
1039         return;
1040
1041     Q_Q(QGL2PaintEngineEx);
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     QGLRect 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 QGL2PaintEngineExPrivate::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     QGLEngineShaderManager::OpacityMode opacityMode;
1093     if (mode == ImageArrayDrawingMode) {
1094         opacityMode = QGLEngineShaderManager::AttributeOpacity;
1095     } else {
1096         opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity
1097                                       : QGLEngineShaderManager::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 = QGLEngineShaderManager::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 == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
1122         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
1123         opacityUniformDirty = false;
1124     }
1125
1126     if (matrixUniformDirty && shaderManager->hasComplexGeometry()) {
1127         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Matrix),
1128                                                          pmvMatrix);
1129         matrixUniformDirty = false;
1130     }
1131
1132     return changed;
1133 }
1134
1135 void QGL2PaintEngineExPrivate::composite(const QGLRect& 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 QGL2PaintEngineExPrivate::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 QGL2PaintEngineEx::QGL2PaintEngineEx()
1165     : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this)))
1166 {
1167 }
1168
1169 QGL2PaintEngineEx::~QGL2PaintEngineEx()
1170 {
1171 }
1172
1173 void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush)
1174 {
1175     Q_D(QGL2PaintEngineEx);
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 QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
1188 {
1189     Q_D(QGL2PaintEngineEx);
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 QGL2PaintEngineExPrivate::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, QGL2PaintEngineExPrivate::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 QGL2PaintEngineEx::penChanged() { }
1287 void QGL2PaintEngineEx::brushChanged() { }
1288 void QGL2PaintEngineEx::brushOriginChanged() { }
1289
1290 void QGL2PaintEngineEx::opacityChanged()
1291 {
1292 //    qDebug("QGL2PaintEngineEx::opacityChanged()");
1293     Q_D(QGL2PaintEngineEx);
1294     state()->opacityChanged = true;
1295
1296     Q_ASSERT(d->shaderManager);
1297     d->brushUniformsDirty = true;
1298     d->opacityUniformDirty = true;
1299 }
1300
1301 void QGL2PaintEngineEx::compositionModeChanged()
1302 {
1303 //     qDebug("QGL2PaintEngineEx::compositionModeChanged()");
1304     Q_D(QGL2PaintEngineEx);
1305     state()->compositionModeChanged = true;
1306     d->compositionModeDirty = true;
1307 }
1308
1309 void QGL2PaintEngineEx::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(QGL2PaintEngineEx);
1322     d->lastTextureUsed = GLuint(-1);
1323     d->brushTextureDirty = true;
1324 //    qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!");
1325 }
1326
1327 void QGL2PaintEngineEx::transformChanged()
1328 {
1329     Q_D(QGL2PaintEngineEx);
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 QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
1341 {
1342     Q_D(QGL2PaintEngineEx);
1343     QGLContext *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     QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption|QGLContext::CanFlipNativePixmapBindOption;
1360 #ifdef QGL_USE_TEXTURE_POOL
1361     bindOptions |= QGLContext::TemporarilyCachedBindOption;
1362 #endif
1363
1364     glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1365     QGLTexture *texture =
1366         ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions);
1367
1368     GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top();
1369     GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom();
1370     QGLRect srcRect(src.left(), top, src.right(), bottom);
1371
1372     bool isBitmap = pixmap.isQBitmap();
1373     bool isOpaque = !isBitmap && !pixmap.hasAlpha();
1374
1375     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1376                            state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
1377     d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
1378
1379     if (texture->options&QGLContext::TemporarilyCachedBindOption) {
1380         // pixmap was temporarily cached as a QImage texture by pooling system
1381         // and should be destroyed immediately
1382         QGLTextureCache::instance()->remove(ctx, texture->id);
1383     }
1384 }
1385
1386 void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
1387                         Qt::ImageConversionFlags)
1388 {
1389     Q_D(QGL2PaintEngineEx);
1390     QGLContext *ctx = d->ctx;
1391
1392     int max_texture_size = ctx->d_func()->maxTextureSize();
1393     if (image.width() > max_texture_size || image.height() > max_texture_size) {
1394         QImage scaled = image.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1395
1396         const qreal sx = scaled.width() / qreal(image.width());
1397         const qreal sy = scaled.height() / qreal(image.height());
1398
1399         drawImage(dest, scaled, scaleRect(src, sx, sy));
1400         return;
1401     }
1402
1403     ensureActive();
1404     d->transferMode(ImageDrawingMode);
1405
1406     glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1407
1408     QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption;
1409 #ifdef QGL_USE_TEXTURE_POOL
1410     bindOptions |= QGLContext::TemporarilyCachedBindOption;
1411 #endif
1412
1413     QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions);
1414     GLuint id = texture->id;
1415
1416     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1417                            state()->renderHints & QPainter::SmoothPixmapTransform, id);
1418     d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
1419
1420     if (texture->options&QGLContext::TemporarilyCachedBindOption) {
1421         // image was temporarily cached by texture pooling system
1422         // and should be destroyed immediately
1423         QGLTextureCache::instance()->remove(ctx, texture->id);
1424     }
1425 }
1426
1427 void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
1428 {
1429     Q_D(QGL2PaintEngineEx);
1430
1431     ensureActive();
1432
1433     QFontEngineGlyphCache::Type glyphType = textItem->fontEngine()->glyphFormat >= 0
1434                                             ? QFontEngineGlyphCache::Type(textItem->fontEngine()->glyphFormat)
1435                                             : d->glyphCacheType;
1436     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1437         if (d->device->alphaRequested() || state()->matrix.type() > QTransform::TxTranslate
1438             || (state()->composition_mode != QPainter::CompositionMode_Source
1439             && state()->composition_mode != QPainter::CompositionMode_SourceOver))
1440         {
1441             glyphType = QFontEngineGlyphCache::Raster_A8;
1442         }
1443     }
1444
1445     d->drawCachedGlyphs(glyphType, textItem);
1446 }
1447
1448 bool QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
1449 {
1450     Q_D(QGL2PaintEngineEx);
1451     if (!d->shaderManager)
1452         return false;
1453
1454     ensureActive();
1455     d->transferMode(ImageDrawingMode);
1456
1457 #ifndef QT_OPENGL_ES_2
1458     QGLContext *ctx = d->ctx;
1459 #endif
1460     glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1461     glBindTexture(GL_TEXTURE_2D, textureId);
1462
1463     QGLRect srcRect(src.left(), src.bottom(), src.right(), src.top());
1464
1465     d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1466                            state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
1467     d->drawTexture(dest, srcRect, size, false);
1468     return true;
1469 }
1470
1471 void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
1472 {
1473     Q_D(QGL2PaintEngineEx);
1474
1475     ensureActive();
1476     QOpenGL2PaintEngineState *s = state();
1477
1478     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1479
1480     QTransform::TransformationType txtype = s->matrix.type();
1481
1482     float det = s->matrix.determinant();
1483     bool drawCached = txtype < QTransform::TxProject;
1484
1485     // don't try to cache huge fonts or vastly transformed fonts
1486     const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
1487     if (pixelSize * pixelSize * qAbs(det) >= QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE ||
1488         det < 0.25f || det > 4.f)
1489         drawCached = false;
1490
1491     QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0
1492                                             ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat)
1493                                             : d->glyphCacheType;
1494
1495
1496     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1497         if (d->device->alphaRequested() || txtype > QTransform::TxTranslate
1498             || (state()->composition_mode != QPainter::CompositionMode_Source
1499             && state()->composition_mode != QPainter::CompositionMode_SourceOver))
1500         {
1501             glyphType = QFontEngineGlyphCache::Raster_A8;
1502         }
1503     }
1504
1505     if (drawCached) {
1506         QVarLengthArray<QFixedPoint> positions;
1507         QVarLengthArray<glyph_t> glyphs;
1508         QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
1509         ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1510
1511         {
1512             QStaticTextItem staticTextItem;
1513             staticTextItem.chars = const_cast<QChar *>(ti.chars);
1514             staticTextItem.setFontEngine(ti.fontEngine);
1515             staticTextItem.glyphs = glyphs.data();
1516             staticTextItem.numChars = ti.num_chars;
1517             staticTextItem.numGlyphs = glyphs.size();
1518             staticTextItem.glyphPositions = positions.data();
1519
1520             d->drawCachedGlyphs(glyphType, &staticTextItem);
1521         }
1522         return;
1523     }
1524
1525     QPaintEngineEx::drawTextItem(p, ti);
1526 }
1527
1528 namespace {
1529
1530     class QOpenGLStaticTextUserData: public QStaticTextUserData
1531     {
1532     public:
1533         QOpenGLStaticTextUserData()
1534             : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0)
1535         {
1536         }
1537
1538         ~QOpenGLStaticTextUserData()
1539         {
1540         }
1541
1542         QSize cacheSize;
1543         QGL2PEXVertexArray vertexCoordinateArray;
1544         QGL2PEXVertexArray textureCoordinateArray;
1545         QFontEngineGlyphCache::Type glyphType;
1546         int cacheSerialNumber;
1547     };
1548
1549 }
1550
1551 #if defined(Q_WS_WIN)
1552 static bool fontSmoothingApproximately(qreal target)
1553 {
1554     extern Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; // qapplication_win.cpp
1555     return (qAbs(qt_fontsmoothing_gamma - target) < 0.2);
1556 }
1557 #endif
1558
1559 // #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
1560
1561 void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyphType,
1562                                                 QStaticTextItem *staticTextItem)
1563 {
1564     Q_Q(QGL2PaintEngineEx);
1565
1566     QOpenGL2PaintEngineState *s = q->state();
1567
1568     void *cacheKey = const_cast<QGLContext *>(QGLContextPrivate::contextGroup(ctx)->context());
1569     bool recreateVertexArrays = false;
1570
1571     QGLTextureGlyphCache *cache =
1572             (QGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(cacheKey, glyphType, QTransform());
1573     if (!cache || cache->cacheType() != glyphType || cache->context() == 0) {
1574         cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform());
1575         staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache);
1576         cache->insert(ctx, cache);
1577         recreateVertexArrays = true;
1578     }
1579
1580     if (staticTextItem->userDataNeedsUpdate) {
1581         recreateVertexArrays = true;
1582     } else if (staticTextItem->userData() == 0) {
1583         recreateVertexArrays = true;
1584     } else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1585         recreateVertexArrays = true;
1586     } else {
1587         QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData());
1588         if (userData->glyphType != glyphType) {
1589             recreateVertexArrays = true;
1590         } else if (userData->cacheSerialNumber != cache->serialNumber()) {
1591             recreateVertexArrays = true;
1592         }
1593     }
1594
1595     // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays.
1596     // If the cache size has changed, we do need to regenerate the vertices, but we don't need to repopulate the
1597     // cache so this text is performed before we test if the cache size has changed.
1598     if (recreateVertexArrays) {
1599         cache->setPaintEnginePrivate(this);
1600         if (!cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs,
1601                              staticTextItem->glyphs, staticTextItem->glyphPositions)) {
1602             // No space for glyphs in cache. We need to reset it and try again.
1603             cache->clear();
1604             cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs,
1605                             staticTextItem->glyphs, staticTextItem->glyphPositions);
1606         }
1607         cache->fillInPendingGlyphs();
1608     }
1609
1610     if (cache->width() == 0 || cache->height() == 0)
1611         return;
1612
1613     transferMode(TextDrawingMode);
1614
1615     int margin = cache->glyphMargin();
1616
1617     GLfloat dx = 1.0 / cache->width();
1618     GLfloat dy = 1.0 / cache->height();
1619
1620     // Use global arrays by default
1621     QGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
1622     QGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
1623
1624     if (staticTextItem->useBackendOptimizations) {
1625         QOpenGLStaticTextUserData *userData = 0;
1626
1627         if (staticTextItem->userData() == 0
1628             || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1629
1630             userData = new QOpenGLStaticTextUserData();
1631             staticTextItem->setUserData(userData);
1632
1633         } else {
1634             userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData());
1635         }
1636
1637         userData->glyphType = glyphType;
1638         userData->cacheSerialNumber = cache->serialNumber();
1639
1640         // Use cache if backend optimizations is turned on
1641         vertexCoordinates = &userData->vertexCoordinateArray;
1642         textureCoordinates = &userData->textureCoordinateArray;
1643
1644         QSize size(cache->width(), cache->height());
1645         if (userData->cacheSize != size) {
1646             recreateVertexArrays = true;
1647             userData->cacheSize = size;
1648         }
1649     }
1650
1651     if (recreateVertexArrays) {
1652         vertexCoordinates->clear();
1653         textureCoordinates->clear();
1654
1655         bool supportsSubPixelPositions = staticTextItem->fontEngine()->supportsSubPixelPositions();
1656         for (int i=0; i<staticTextItem->numGlyphs; ++i) {
1657             QFixed subPixelPosition;
1658             if (supportsSubPixelPositions)
1659                 subPixelPosition = cache->subPixelPositionForX(staticTextItem->glyphPositions[i].x);
1660
1661             QTextureGlyphCache::GlyphAndSubPixelPosition glyph(staticTextItem->glyphs[i], subPixelPosition);
1662
1663             const QTextureGlyphCache::Coord &c = cache->coords[glyph];
1664             if (c.isNull())
1665                 continue;
1666
1667             int x = qFloor(staticTextItem->glyphPositions[i].x) + c.baseLineX - margin;
1668             int y = qFloor(staticTextItem->glyphPositions[i].y) - c.baseLineY - margin;
1669
1670             vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
1671             textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
1672         }
1673
1674         staticTextItem->userDataNeedsUpdate = false;
1675     }
1676
1677     int numGlyphs = vertexCoordinates->vertexCount() / 4;
1678
1679     if (elementIndices.size() < numGlyphs*6) {
1680         Q_ASSERT(elementIndices.size() % 6 == 0);
1681         int j = elementIndices.size() / 6 * 4;
1682         while (j < numGlyphs*4) {
1683             elementIndices.append(j + 0);
1684             elementIndices.append(j + 0);
1685             elementIndices.append(j + 1);
1686             elementIndices.append(j + 2);
1687             elementIndices.append(j + 3);
1688             elementIndices.append(j + 3);
1689
1690             j += 4;
1691         }
1692
1693 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1694         if (elementIndicesVBOId == 0)
1695             glGenBuffers(1, &elementIndicesVBOId);
1696
1697         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1698         glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort),
1699                      elementIndices.constData(), GL_STATIC_DRAW);
1700 #endif
1701     } else {
1702 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1703         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1704 #endif
1705     }
1706
1707     setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data());
1708     setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data());
1709
1710     if (!snapToPixelGrid) {
1711         snapToPixelGrid = true;
1712         matrixDirty = true;
1713     }
1714
1715     QBrush pensBrush = q->state()->pen.brush();
1716     setBrush(pensBrush);
1717
1718     if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) {
1719
1720         // Subpixel antialiasing without gamma correction
1721
1722         QPainter::CompositionMode compMode = q->state()->composition_mode;
1723         Q_ASSERT(compMode == QPainter::CompositionMode_Source
1724             || compMode == QPainter::CompositionMode_SourceOver);
1725
1726         shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass1);
1727
1728         if (pensBrush.style() == Qt::SolidPattern) {
1729             // Solid patterns can get away with only one pass.
1730             QColor c = pensBrush.color();
1731             qreal oldOpacity = q->state()->opacity;
1732             if (compMode == QPainter::CompositionMode_Source) {
1733                 c = qt_premultiplyColor(c, q->state()->opacity);
1734                 q->state()->opacity = 1;
1735                 opacityUniformDirty = true;
1736             }
1737
1738             compositionModeDirty = false; // I can handle this myself, thank you very much
1739             prepareForDraw(false); // Text always causes src pixels to be transparent
1740
1741             // prepareForDraw() have set the opacity on the current shader, so the opacity state can now be reset.
1742             if (compMode == QPainter::CompositionMode_Source) {
1743                 q->state()->opacity = oldOpacity;
1744                 opacityUniformDirty = true;
1745             }
1746
1747             glEnable(GL_BLEND);
1748             glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
1749             glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
1750         } else {
1751             // Other brush styles need two passes.
1752
1753             qreal oldOpacity = q->state()->opacity;
1754             if (compMode == QPainter::CompositionMode_Source) {
1755                 q->state()->opacity = 1;
1756                 opacityUniformDirty = true;
1757                 pensBrush = Qt::white;
1758                 setBrush(pensBrush);
1759             }
1760
1761             compositionModeDirty = false; // I can handle this myself, thank you very much
1762             prepareForDraw(false); // Text always causes src pixels to be transparent
1763             glEnable(GL_BLEND);
1764             glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1765
1766             glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1767             glBindTexture(GL_TEXTURE_2D, cache->texture());
1768             updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false);
1769
1770 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1771             glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
1772 #else
1773             glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
1774 #endif
1775
1776             shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass2);
1777
1778             if (compMode == QPainter::CompositionMode_Source) {
1779                 q->state()->opacity = oldOpacity;
1780                 opacityUniformDirty = true;
1781                 pensBrush = q->state()->pen.brush();
1782                 setBrush(pensBrush);
1783             }
1784
1785             compositionModeDirty = false;
1786             prepareForDraw(false); // Text always causes src pixels to be transparent
1787             glEnable(GL_BLEND);
1788             glBlendFunc(GL_ONE, GL_ONE);
1789         }
1790         compositionModeDirty = true;
1791     } else {
1792         // Greyscale/mono glyphs
1793
1794         shaderManager->setMaskType(QGLEngineShaderManager::PixelMask);
1795         prepareForDraw(false); // Text always causes src pixels to be transparent
1796     }
1797
1798     QGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate)?QGLTextureGlyphCache::Linear:QGLTextureGlyphCache::Nearest;
1799     if (lastMaskTextureUsed != cache->texture() || cache->filterMode() != filterMode) {
1800
1801         glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT);
1802         if (lastMaskTextureUsed != cache->texture()) {
1803             glBindTexture(GL_TEXTURE_2D, cache->texture());
1804             lastMaskTextureUsed = cache->texture();
1805         }
1806
1807         if (cache->filterMode() != filterMode) {
1808             if (filterMode == QGLTextureGlyphCache::Linear) {
1809                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1810                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1811             } else {
1812                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1813                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1814             }
1815             cache->setFilterMode(filterMode);
1816         }
1817     }
1818
1819     bool srgbFrameBufferEnabled = false;
1820     if (ctx->d_ptr->extension_flags & QGLExtensions::SRGBFrameBuffer) {
1821 #if defined(Q_WS_MAC)
1822         if (glyphType == QFontEngineGlyphCache::Raster_RGBMask)
1823 #elif defined(Q_WS_WIN)
1824         if (glyphType != QFontEngineGlyphCache::Raster_RGBMask || fontSmoothingApproximately(2.1))
1825 #else
1826         if (false)
1827 #endif
1828         {
1829             glEnable(FRAMEBUFFER_SRGB_EXT);
1830             srgbFrameBufferEnabled = true;
1831         }
1832     }
1833
1834 #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1835     glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
1836     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1837 #else
1838     glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data());
1839 #endif
1840
1841     if (srgbFrameBufferEnabled)
1842         glDisable(FRAMEBUFFER_SRGB_EXT);
1843
1844 }
1845
1846 void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
1847                                             QPainter::PixmapFragmentHints hints)
1848 {
1849     Q_D(QGL2PaintEngineEx);
1850     // Use fallback for extended composition modes.
1851     if (state()->composition_mode > QPainter::CompositionMode_Plus) {
1852         QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
1853         return;
1854     }
1855
1856     ensureActive();
1857     int max_texture_size = d->ctx->d_func()->maxTextureSize();
1858     if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) {
1859         QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1860         d->drawPixmapFragments(fragments, fragmentCount, scaled, hints);
1861     } else {
1862         d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
1863     }
1864 }
1865
1866
1867 void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments,
1868                                                    int fragmentCount, const QPixmap &pixmap,
1869                                                    QPainter::PixmapFragmentHints hints)
1870 {
1871     GLfloat dx = 1.0f / pixmap.size().width();
1872     GLfloat dy = 1.0f / pixmap.size().height();
1873
1874     vertexCoordinateArray.clear();
1875     textureCoordinateArray.clear();
1876     opacityArray.reset();
1877
1878     if (snapToPixelGrid) {
1879         snapToPixelGrid = false;
1880         matrixDirty = true;
1881     }
1882
1883     bool allOpaque = true;
1884
1885     for (int i = 0; i < fragmentCount; ++i) {
1886         qreal s = 0;
1887         qreal c = 1;
1888         if (fragments[i].rotation != 0) {
1889             s = qFastSin(fragments[i].rotation * Q_PI / 180);
1890             c = qFastCos(fragments[i].rotation * Q_PI / 180);
1891         }
1892
1893         qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
1894         qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
1895         QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
1896         QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
1897
1898         vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
1899         vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y);
1900         vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
1901         vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
1902         vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
1903         vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
1904
1905         QGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
1906                     (fragments[i].sourceLeft + fragments[i].width) * dx,
1907                     (fragments[i].sourceTop + fragments[i].height) * dy);
1908
1909         textureCoordinateArray.addVertex(src.right, src.bottom);
1910         textureCoordinateArray.addVertex(src.right, src.top);
1911         textureCoordinateArray.addVertex(src.left, src.top);
1912         textureCoordinateArray.addVertex(src.left, src.top);
1913         textureCoordinateArray.addVertex(src.left, src.bottom);
1914         textureCoordinateArray.addVertex(src.right, src.bottom);
1915
1916         qreal opacity = fragments[i].opacity * q->state()->opacity;
1917         opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
1918         allOpaque &= (opacity >= 0.99f);
1919     }
1920
1921     glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
1922     QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
1923                                                      QGLContext::InternalBindOption
1924                                                      | QGLContext::CanFlipNativePixmapBindOption);
1925
1926     if (texture->options & QGLContext::InvertedYBindOption) {
1927         // Flip texture y-coordinate.
1928         QGLPoint *data = textureCoordinateArray.data();
1929         for (int i = 0; i < 6 * fragmentCount; ++i)
1930             data[i].y = 1 - data[i].y;
1931     }
1932
1933     transferMode(ImageArrayDrawingMode);
1934
1935     bool isBitmap = pixmap.isQBitmap();
1936     bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque;
1937
1938     updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
1939                            q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
1940
1941     // Setup for texture drawing
1942     currentBrush = noBrush;
1943     shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc
1944                                             : QGLEngineShaderManager::ImageSrc);
1945     if (prepareForDraw(isOpaque))
1946         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
1947
1948     if (isBitmap) {
1949         QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
1950         shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
1951     }
1952
1953     glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
1954 }
1955
1956 bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
1957 {
1958     Q_D(QGL2PaintEngineEx);
1959
1960 //     qDebug("QGL2PaintEngineEx::begin()");
1961     if (pdev->devType() == QInternal::OpenGL)
1962         d->device = static_cast<QGLPaintDevice*>(pdev);
1963     else
1964         d->device = QGLPaintDevice::getDevice(pdev);
1965
1966     if (!d->device)
1967         return false;
1968
1969     d->ctx = d->device->context();
1970     d->ctx->d_ptr->active_engine = this;
1971
1972     const QSize sz = d->device->size();
1973     d->width = sz.width();
1974     d->height = sz.height();
1975     d->mode = BrushDrawingMode;
1976     d->brushTextureDirty = true;
1977     d->brushUniformsDirty = true;
1978     d->matrixUniformDirty = true;
1979     d->matrixDirty = true;
1980     d->compositionModeDirty = true;
1981     d->opacityUniformDirty = true;
1982     d->needsSync = true;
1983     d->useSystemClip = !systemClip().isEmpty();
1984     d->currentBrush = QBrush();
1985
1986     d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
1987     d->stencilClean = true;
1988
1989     // Calling begin paint should make the correct context current. So, any
1990     // code which calls into GL or otherwise needs a current context *must*
1991     // go after beginPaint:
1992     d->device->beginPaint();
1993
1994 #if !defined(QT_OPENGL_ES_2)
1995     bool success = qt_resolve_version_2_0_functions(d->ctx)
1996                    && qt_resolve_buffer_extensions(d->ctx);
1997     Q_ASSERT(success);
1998     Q_UNUSED(success);
1999 #endif
2000
2001     d->shaderManager = new QGLEngineShaderManager(d->ctx);
2002
2003     glDisable(GL_STENCIL_TEST);
2004     glDisable(GL_DEPTH_TEST);
2005     glDisable(GL_SCISSOR_TEST);
2006
2007 #if !defined(QT_OPENGL_ES_2)
2008     glDisable(GL_MULTISAMPLE);
2009 #endif
2010
2011     d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
2012
2013 #if !defined(QT_OPENGL_ES_2)
2014 #if defined(Q_WS_WIN)
2015     if (qt_cleartype_enabled
2016         && (fontSmoothingApproximately(1.0) || fontSmoothingApproximately(2.1)))
2017 #endif
2018 #if defined(Q_WS_MAC)
2019     if (qt_applefontsmoothing_enabled)
2020 #endif
2021         d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
2022 #endif
2023
2024 #if defined(QT_OPENGL_ES_2)
2025     // OpenGL ES can't switch MSAA off, so if the gl paint device is
2026     // multisampled, it's always multisampled.
2027     d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers();
2028 #else
2029     d->multisamplingAlwaysEnabled = false;
2030 #endif
2031
2032     return true;
2033 }
2034
2035 bool QGL2PaintEngineEx::end()
2036 {
2037     Q_D(QGL2PaintEngineEx);
2038     QGLContext *ctx = d->ctx;
2039
2040     glUseProgram(0);
2041     d->transferMode(BrushDrawingMode);
2042     d->device->endPaint();
2043
2044 #if defined(Q_WS_X11)
2045     // On some (probably all) drivers, deleting an X pixmap which has been bound to a texture
2046     // before calling glFinish/swapBuffers renders garbage. Presumably this is because X deletes
2047     // the pixmap behind the driver's back before it's had a chance to use it. To fix this, we
2048     // reference all QPixmaps which have been bound to stop them being deleted and only deref
2049     // them here, after swapBuffers, where they can be safely deleted.
2050     ctx->d_func()->boundPixmaps.clear();
2051 #endif
2052     d->ctx->d_ptr->active_engine = 0;
2053
2054     d->resetGLState();
2055
2056     delete d->shaderManager;
2057     d->shaderManager = 0;
2058     d->currentBrush = QBrush();
2059
2060 #ifdef QT_OPENGL_CACHE_AS_VBOS
2061     if (!d->unusedVBOSToClean.isEmpty()) {
2062         glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
2063         d->unusedVBOSToClean.clear();
2064     }
2065     if (!d->unusedIBOSToClean.isEmpty()) {
2066         glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData());
2067         d->unusedIBOSToClean.clear();
2068     }
2069 #endif
2070
2071     return false;
2072 }
2073
2074 void QGL2PaintEngineEx::ensureActive()
2075 {
2076     Q_D(QGL2PaintEngineEx);
2077     QGLContext *ctx = d->ctx;
2078
2079     if (isActive() && ctx->d_ptr->active_engine != this) {
2080         ctx->d_ptr->active_engine = this;
2081         d->needsSync = true;
2082     }
2083
2084     d->device->ensureActiveTarget();
2085
2086     if (d->needsSync) {
2087         d->transferMode(BrushDrawingMode);
2088         glViewport(0, 0, d->width, d->height);
2089         d->needsSync = false;
2090         d->lastMaskTextureUsed = 0;
2091         d->shaderManager->setDirty();
2092         d->ctx->d_func()->syncGlState();
2093         for (int i = 0; i < 3; ++i)
2094             d->vertexAttribPointers[i] = (GLfloat*)-1; // Assume the pointers are clobbered
2095         setState(state());
2096     }
2097 }
2098
2099 void QGL2PaintEngineExPrivate::updateClipScissorTest()
2100 {
2101     Q_Q(QGL2PaintEngineEx);
2102     if (q->state()->clipTestEnabled) {
2103         glEnable(GL_STENCIL_TEST);
2104         glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
2105     } else {
2106         glDisable(GL_STENCIL_TEST);
2107         glStencilFunc(GL_ALWAYS, 0, 0xff);
2108     }
2109
2110 #ifdef QT_GL_NO_SCISSOR_TEST
2111     currentScissorBounds = QRect(0, 0, width, height);
2112 #else
2113     QRect bounds = q->state()->rectangleClip;
2114     if (!q->state()->clipEnabled) {
2115         if (useSystemClip)
2116             bounds = systemClip.boundingRect();
2117         else
2118             bounds = QRect(0, 0, width, height);
2119     } else {
2120         if (useSystemClip)
2121             bounds = bounds.intersected(systemClip.boundingRect());
2122         else
2123             bounds = bounds.intersected(QRect(0, 0, width, height));
2124     }
2125
2126     currentScissorBounds = bounds;
2127
2128     if (bounds == QRect(0, 0, width, height)) {
2129         glDisable(GL_SCISSOR_TEST);
2130     } else {
2131         glEnable(GL_SCISSOR_TEST);
2132         setScissor(bounds);
2133     }
2134 #endif
2135 }
2136
2137 void QGL2PaintEngineExPrivate::setScissor(const QRect &rect)
2138 {
2139     const int left = rect.left();
2140     const int width = rect.width();
2141     int bottom = height - (rect.top() + rect.height());
2142     if (device->isFlipped()) {
2143         bottom = rect.top();
2144     }
2145     const int height = rect.height();
2146
2147     glScissor(left, bottom, width, height);
2148 }
2149
2150 void QGL2PaintEngineEx::clipEnabledChanged()
2151 {
2152     Q_D(QGL2PaintEngineEx);
2153
2154     state()->clipChanged = true;
2155
2156     if (painter()->hasClipping())
2157         d->regenerateClip();
2158     else
2159         d->systemStateChanged();
2160 }
2161
2162 void QGL2PaintEngineExPrivate::clearClip(uint value)
2163 {
2164     dirtyStencilRegion -= currentScissorBounds;
2165
2166     glStencilMask(0xff);
2167     glClearStencil(value);
2168     glClear(GL_STENCIL_BUFFER_BIT);
2169     glStencilMask(0x0);
2170
2171     q->state()->needsClipBufferClear = false;
2172 }
2173
2174 void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value)
2175 {
2176     transferMode(BrushDrawingMode);
2177
2178     if (snapToPixelGrid) {
2179         snapToPixelGrid = false;
2180         matrixDirty = true;
2181     }
2182
2183     if (matrixDirty)
2184         updateMatrix();
2185
2186     stencilClean = false;
2187
2188     const bool singlePass = !path.hasWindingFill()
2189         && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
2190             || q->state()->needsClipBufferClear);
2191     const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
2192
2193     if (q->state()->needsClipBufferClear)
2194         clearClip(1);
2195
2196     if (path.isEmpty()) {
2197         glEnable(GL_STENCIL_TEST);
2198         glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2199         return;
2200     }
2201
2202     if (q->state()->clipTestEnabled)
2203         glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
2204     else
2205         glStencilFunc(GL_ALWAYS, 0, 0xff);
2206
2207     vertexCoordinateArray.clear();
2208     vertexCoordinateArray.addPath(path, inverseScale, false);
2209
2210     if (!singlePass)
2211         fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
2212
2213     glColorMask(false, false, false, false);
2214     glEnable(GL_STENCIL_TEST);
2215     useSimpleShader();
2216
2217     if (singlePass) {
2218         // Under these conditions we can set the new stencil value in a single
2219         // pass, by using the current value and the "new value" as the toggles
2220
2221         glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT);
2222         glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
2223         glStencilMask(value ^ referenceClipValue);
2224
2225         drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
2226     } else {
2227         glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
2228         glStencilMask(0xff);
2229
2230         if (!q->state()->clipTestEnabled && path.hasWindingFill()) {
2231             // Pass when any clip bit is set, set high bit
2232             glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT);
2233             composite(vertexCoordinateArray.boundingRect());
2234         }
2235
2236         // Pass when high bit is set, replace stencil value with new clip value
2237         glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT);
2238
2239         composite(vertexCoordinateArray.boundingRect());
2240     }
2241
2242     glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
2243     glStencilMask(0);
2244
2245     glColorMask(true, true, true, true);
2246 }
2247
2248 void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
2249 {
2250 //     qDebug("QGL2PaintEngineEx::clip()");
2251     Q_D(QGL2PaintEngineEx);
2252
2253     state()->clipChanged = true;
2254
2255     ensureActive();
2256
2257     if (op == Qt::ReplaceClip) {
2258         op = Qt::IntersectClip;
2259         if (d->hasClipOperations()) {
2260             d->systemStateChanged();
2261             state()->canRestoreClip = false;
2262         }
2263     }
2264
2265 #ifndef QT_GL_NO_SCISSOR_TEST
2266     if (!path.isEmpty() && op == Qt::IntersectClip && (path.shape() == QVectorPath::RectangleHint)) {
2267         const QPointF* const points = reinterpret_cast<const QPointF*>(path.points());
2268         QRectF rect(points[0], points[2]);
2269
2270         if (state()->matrix.type() <= QTransform::TxScale
2271             || (state()->matrix.type() == QTransform::TxRotate
2272                 && qFuzzyIsNull(state()->matrix.m11())
2273                 && qFuzzyIsNull(state()->matrix.m22())))
2274         {
2275             state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect());
2276             d->updateClipScissorTest();
2277             return;
2278         }
2279     }
2280 #endif
2281
2282     const QRect pathRect = state()->matrix.mapRect(path.controlPointRect()).toAlignedRect();
2283
2284     switch (op) {
2285     case Qt::NoClip:
2286         if (d->useSystemClip) {
2287             state()->clipTestEnabled = true;
2288             state()->currentClip = 1;
2289         } else {
2290             state()->clipTestEnabled = false;
2291         }
2292         state()->rectangleClip = QRect(0, 0, d->width, d->height);
2293         state()->canRestoreClip = false;
2294         d->updateClipScissorTest();
2295         break;
2296     case Qt::IntersectClip:
2297         state()->rectangleClip = state()->rectangleClip.intersected(pathRect);
2298         d->updateClipScissorTest();
2299         d->resetClipIfNeeded();
2300         ++d->maxClip;
2301         d->writeClip(path, d->maxClip);
2302         state()->currentClip = d->maxClip;
2303         state()->clipTestEnabled = true;
2304         break;
2305     case Qt::UniteClip: {
2306         d->resetClipIfNeeded();
2307         ++d->maxClip;
2308         if (state()->rectangleClip.isValid()) {
2309             QPainterPath path;
2310             path.addRect(state()->rectangleClip);
2311
2312             // flush the existing clip rectangle to the depth buffer
2313             d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), d->maxClip);
2314         }
2315
2316         state()->clipTestEnabled = false;
2317 #ifndef QT_GL_NO_SCISSOR_TEST
2318         QRect oldRectangleClip = state()->rectangleClip;
2319
2320         state()->rectangleClip = state()->rectangleClip.united(pathRect);
2321         d->updateClipScissorTest();
2322
2323         QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip;
2324
2325         if (!extendRegion.isEmpty()) {
2326             QPainterPath extendPath;
2327             extendPath.addRegion(extendRegion);
2328
2329             // first clear the depth buffer in the extended region
2330             d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0);
2331         }
2332 #endif
2333         // now write the clip path
2334         d->writeClip(path, d->maxClip);
2335         state()->canRestoreClip = false;
2336         state()->currentClip = d->maxClip;
2337         state()->clipTestEnabled = true;
2338         break;
2339         }
2340     default:
2341         break;
2342     }
2343 }
2344
2345 void QGL2PaintEngineExPrivate::regenerateClip()
2346 {
2347     systemStateChanged();
2348     replayClipOperations();
2349 }
2350
2351 void QGL2PaintEngineExPrivate::systemStateChanged()
2352 {
2353     Q_Q(QGL2PaintEngineEx);
2354
2355     q->state()->clipChanged = true;
2356
2357     if (systemClip.isEmpty()) {
2358         useSystemClip = false;
2359     } else {
2360         if (q->paintDevice()->devType() == QInternal::Widget && currentClipWidget) {
2361             QWidgetPrivate *widgetPrivate = qt_widget_private(currentClipWidget->window());
2362             useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter;
2363         } else {
2364             useSystemClip = true;
2365         }
2366     }
2367
2368     q->state()->clipTestEnabled = false;
2369     q->state()->needsClipBufferClear = true;
2370
2371     q->state()->currentClip = 1;
2372     maxClip = 1;
2373
2374     q->state()->rectangleClip = useSystemClip ? systemClip.boundingRect() : QRect(0, 0, width, height);
2375     updateClipScissorTest();
2376
2377     if (systemClip.rectCount() == 1) {
2378         if (systemClip.boundingRect() == QRect(0, 0, width, height))
2379             useSystemClip = false;
2380 #ifndef QT_GL_NO_SCISSOR_TEST
2381         // scissoring takes care of the system clip
2382         return;
2383 #endif
2384     }
2385
2386     if (useSystemClip) {
2387         clearClip(0);
2388
2389         QPainterPath path;
2390         path.addRegion(systemClip);
2391
2392         q->state()->currentClip = 0;
2393         writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1);
2394         q->state()->currentClip = 1;
2395         q->state()->clipTestEnabled = true;
2396     }
2397 }
2398
2399 void QGL2PaintEngineEx::setState(QPainterState *new_state)
2400 {
2401     //     qDebug("QGL2PaintEngineEx::setState()");
2402
2403     Q_D(QGL2PaintEngineEx);
2404
2405     QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state);
2406     QOpenGL2PaintEngineState *old_state = state();
2407
2408     QPaintEngineEx::setState(s);
2409
2410     if (s->isNew) {
2411         // Newly created state object.  The call to setState()
2412         // will either be followed by a call to begin(), or we are
2413         // setting the state as part of a save().
2414         s->isNew = false;
2415         return;
2416     }
2417
2418     // Setting the state as part of a restore().
2419
2420     if (old_state == s || old_state->renderHintsChanged)
2421         renderHintsChanged();
2422
2423     if (old_state == s || old_state->matrixChanged)
2424         d->matrixDirty = true;
2425
2426     if (old_state == s || old_state->compositionModeChanged)
2427         d->compositionModeDirty = true;
2428
2429     if (old_state == s || old_state->opacityChanged)
2430         d->opacityUniformDirty = true;
2431
2432     if (old_state == s || old_state->clipChanged) {
2433         if (old_state && old_state != s && old_state->canRestoreClip) {
2434             d->updateClipScissorTest();
2435             glDepthFunc(GL_LEQUAL);
2436         } else {
2437             d->regenerateClip();
2438         }
2439     }
2440 }
2441
2442 QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const
2443 {
2444     if (orig)
2445         const_cast<QGL2PaintEngineEx *>(this)->ensureActive();
2446
2447     QOpenGL2PaintEngineState *s;
2448     if (!orig)
2449         s = new QOpenGL2PaintEngineState();
2450     else
2451         s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig));
2452
2453     s->matrixChanged = false;
2454     s->compositionModeChanged = false;
2455     s->opacityChanged = false;
2456     s->renderHintsChanged = false;
2457     s->clipChanged = false;
2458
2459     return s;
2460 }
2461
2462 QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other)
2463     : QPainterState(other)
2464 {
2465     isNew = true;
2466     needsClipBufferClear = other.needsClipBufferClear;
2467     clipTestEnabled = other.clipTestEnabled;
2468     currentClip = other.currentClip;
2469     canRestoreClip = other.canRestoreClip;
2470     rectangleClip = other.rectangleClip;
2471 }
2472
2473 QOpenGL2PaintEngineState::QOpenGL2PaintEngineState()
2474 {
2475     isNew = true;
2476     needsClipBufferClear = true;
2477     clipTestEnabled = false;
2478     canRestoreClip = true;
2479 }
2480
2481 QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState()
2482 {
2483 }
2484
2485 QT_END_NAMESPACE