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