~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/gui/opengl/qopenglpaintengine.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

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