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 QtOpenGL module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see 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 <private/qopenglgradientcache_p.h>
66#include <private/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 <private/qopenglengineshadermanager_p.h>
85#include <private/qopengl2pexvertexarray_p.h>
86#include <private/qopengltextureglyphcache_p.h>
87
88#include <QDebug>
89
90#include <qtopengl_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
112QT_BEGIN_NAMESPACE
113
114
115Q_OPENGL_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
116
117////////////////////////////////// Private Methods //////////////////////////////////////////
118
119QOpenGL2PaintEngineExPrivate::~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
135inline 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
146void 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
173void 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*/
195template<typename T>
196void 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
216void 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
230template<>
231GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const GLuint &textureId)
232{
233 if (textureId != lastTextureUsed)
234 funcs.glBindTexture(GL_TEXTURE_2D, textureId);
235
236 return textureId;
237}
238
239template<>
240GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QImage &image)
241{
242 return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, image);
243}
244
245template<>
246GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QPixmap &pixmap)
247{
248 return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, pixmap);
249}
250
251template<>
252GLuint 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
265struct ImageWithBindOptions
266{
267 const QImage &image;
268 QOpenGLTextureUploader::BindOptions options;
269};
270
271template<>
272GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const ImageWithBindOptions &imageWithOptions)
273{
274 return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, imageWithOptions.image, imageWithOptions.options);
275}
276
277inline static bool isPowerOfTwo(int x)
278{
279 // Assumption: x >= 1
280 return x == (x & -x);
281}
282
283void 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
334void 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
454void 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
525void 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
626static 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
638void 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
675void 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 !QT_CONFIG(opengles2) && !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_CONFIG(opengles2)
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
736void 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
765void QOpenGL2PaintEngineEx::endNativePainting()
766{
767 Q_D(QOpenGL2PaintEngineEx);
768 d->needsSync = true;
769 d->nativePaintingActive = false;
770}
771
772void QOpenGL2PaintEngineEx::invalidateState()
773{
774 Q_D(QOpenGL2PaintEngineEx);
775 d->needsSync = true;
776}
777
778bool QOpenGL2PaintEngineEx::isNativePaintingActive() const {
779 Q_D(const QOpenGL2PaintEngineEx);
780 return d->nativePaintingActive;
781}
782
783void 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
814struct 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
830void 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
847void 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
1096void 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*/
1195void 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
1230bool 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
1244bool 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
1308void 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.
1317void 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
1334QOpenGL2PaintEngineEx::QOpenGL2PaintEngineEx()
1335 : QPaintEngineEx(*(new QOpenGL2PaintEngineExPrivate(this)))
1336{
1337 gccaps &= ~QPaintEngine::RasterOpModes;
1338}
1339
1340QOpenGL2PaintEngineEx::~QOpenGL2PaintEngineEx()
1341{
1342}
1343
1344void 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
1355Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
1356
1357
1358void 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 (pen.isCosmetic() && !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
1378void 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 (pen.isCosmetic())
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
1452void QOpenGL2PaintEngineEx::penChanged() { }
1453void QOpenGL2PaintEngineEx::brushChanged() { }
1454void QOpenGL2PaintEngineEx::brushOriginChanged() { }
1455
1456void 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
1467void QOpenGL2PaintEngineEx::compositionModeChanged()
1468{
1469// qDebug("QOpenGL2PaintEngineEx::compositionModeChanged()");
1470 Q_D(QOpenGL2PaintEngineEx);
1471 state()->compositionModeChanged = true;
1472 d->compositionModeDirty = true;
1473}
1474
1475void QOpenGL2PaintEngineEx::renderHintsChanged()
1476{
1477 state()->renderHintsChanged = true;
1478
1479#if !QT_CONFIG(opengles2)
1480 if (!QOpenGLContext::currentContext()->isOpenGLES()) {
1481 Q_D(QOpenGL2PaintEngineEx);
1482 if (state()->renderHints & QPainter::Antialiasing)
1483 d->funcs.glEnable(GL_MULTISAMPLE);
1484 else
1485 d->funcs.glDisable(GL_MULTISAMPLE);
1486 }
1487#endif // !QT_CONFIG(opengles2)
1488
1489 Q_D(QOpenGL2PaintEngineEx);
1490
1491 // This is a somewhat sneaky way of conceptually making the next call to
1492 // updateTexture() use FoceUpdate for the TextureUpdateMode. We need this
1493 // as new render hints may require updating the filter mode.
1494 d->lastTextureUsed = GLuint(-1);
1495
1496 d->brushTextureDirty = true;
1497// qDebug("QOpenGL2PaintEngineEx::renderHintsChanged() not implemented!");
1498}
1499
1500void QOpenGL2PaintEngineEx::transformChanged()
1501{
1502 Q_D(QOpenGL2PaintEngineEx);
1503 d->matrixDirty = true;
1504 state()->matrixChanged = true;
1505}
1506
1507
1508static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy)
1509{
1510 return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy);
1511}
1512
1513void QOpenGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src)
1514{
1515 Q_D(QOpenGL2PaintEngineEx);
1516 QOpenGLContext *ctx = d->ctx;
1517
1518 // Draw pixmaps that are really images as images since drawImage has
1519 // better handling of non-default image formats.
1520 if (pixmap.paintEngine()->type() == QPaintEngine::Raster && !pixmap.isQBitmap())
1521 return drawImage(dest, pixmap.toImage(), src);
1522
1523 int max_texture_size = ctx->d_func()->maxTextureSize();
1524 if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) {
1525 QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1526
1527 const qreal sx = scaled.width() / qreal(pixmap.width());
1528 const qreal sy = scaled.height() / qreal(pixmap.height());
1529
1530 drawPixmap(dest, scaled, scaleRect(src, sx, sy));
1531 return;
1532 }
1533
1534 ensureActive();
1535 d->transferMode(ImageDrawingMode);
1536
1537 GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
1538 d->updateTexture(QT_IMAGE_TEXTURE_UNIT, pixmap, GL_CLAMP_TO_EDGE, filterMode);
1539
1540 bool isBitmap = pixmap.isQBitmap();
1541 bool isOpaque = !isBitmap && !pixmap.hasAlpha();
1542
1543 d->shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc : QOpenGLEngineShaderManager::ImageSrc);
1544
1545 QOpenGLRect srcRect(src.left(), src.top(), src.right(), src.bottom());
1546 d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
1547}
1548
1549void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
1550 Qt::ImageConversionFlags)
1551{
1552 Q_D(QOpenGL2PaintEngineEx);
1553 QOpenGLContext *ctx = d->ctx;
1554
1555 int max_texture_size = ctx->d_func()->maxTextureSize();
1556 if (image.width() > max_texture_size || image.height() > max_texture_size) {
1557 QImage scaled = image.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1558
1559 const qreal sx = scaled.width() / qreal(image.width());
1560 const qreal sy = scaled.height() / qreal(image.height());
1561
1562 drawImage(dest, scaled, scaleRect(src, sx, sy));
1563 return;
1564 }
1565
1566 ensureActive();
1567 d->transferMode(ImageDrawingMode);
1568
1569 QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
1570 // Use specialized bind for formats we have specialized shaders for.
1571 switch (image.format()) {
1572 case QImage::Format_RGBA8888:
1573 case QImage::Format_ARGB32:
1574 case QImage::Format_RGBA64:
1575 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc);
1576 bindOption = { };
1577 break;
1578 case QImage::Format_Alpha8:
1579 if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
1580 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc);
1581 bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption;
1582 } else
1583 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1584 break;
1585 case QImage::Format_Grayscale8:
1586 case QImage::Format_Grayscale16:
1587 if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
1588 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc);
1589 bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption;
1590 } else
1591 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1592 break;
1593 default:
1594 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1595 break;
1596 }
1597
1598 ImageWithBindOptions imageWithOptions = { image, bindOption };
1599 GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
1600 d->updateTexture(QT_IMAGE_TEXTURE_UNIT, imageWithOptions, GL_CLAMP_TO_EDGE, filterMode);
1601
1602 d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
1603}
1604
1605void QOpenGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
1606{
1607 Q_D(QOpenGL2PaintEngineEx);
1608
1609 ensureActive();
1610
1611 QPainterState *s = state();
1612
1613 QFontEngine *fontEngine = textItem->fontEngine();
1614 if (shouldDrawCachedGlyphs(fontEngine, s->matrix)) {
1615 QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None
1616 ? fontEngine->glyphFormat : d->glyphCacheFormat;
1617 if (glyphFormat == QFontEngine::Format_A32) {
1618 if (d->device->context()->format().alphaBufferSize() > 0 || s->matrix.type() > QTransform::TxTranslate
1619 || (s->composition_mode != QPainter::CompositionMode_Source
1620 && s->composition_mode != QPainter::CompositionMode_SourceOver))
1621 {
1622 glyphFormat = QFontEngine::Format_A8;
1623 }
1624 }
1625
1626 d->drawCachedGlyphs(glyphFormat, textItem);
1627 } else {
1628 QPaintEngineEx::drawStaticTextItem(textItem);
1629 }
1630}
1631
1632bool QOpenGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
1633{
1634 Q_D(QOpenGL2PaintEngineEx);
1635 if (!d->shaderManager)
1636 return false;
1637
1638 ensureActive();
1639 d->transferMode(ImageDrawingMode);
1640
1641 GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
1642 d->updateTexture(QT_IMAGE_TEXTURE_UNIT, textureId, GL_CLAMP_TO_EDGE, filterMode);
1643
1644 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1645
1646 QOpenGLRect srcRect(src.left(), src.bottom(), src.right(), src.top());
1647 d->drawTexture(dest, srcRect, size, false);
1648
1649 return true;
1650}
1651
1652void QOpenGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
1653{
1654 Q_D(QOpenGL2PaintEngineEx);
1655
1656 ensureActive();
1657 QOpenGL2PaintEngineState *s = state();
1658
1659 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1660
1661 QTransform::TransformationType txtype = s->matrix.type();
1662
1663 QFontEngine::GlyphFormat glyphFormat = ti.fontEngine->glyphFormat != QFontEngine::Format_None
1664 ? ti.fontEngine->glyphFormat : d->glyphCacheFormat;
1665
1666 if (glyphFormat == QFontEngine::Format_A32) {
1667 if (d->device->context()->format().alphaBufferSize() > 0 || txtype > QTransform::TxTranslate
1668 || (state()->composition_mode != QPainter::CompositionMode_Source
1669 && state()->composition_mode != QPainter::CompositionMode_SourceOver))
1670 {
1671 glyphFormat = QFontEngine::Format_A8;
1672 }
1673 }
1674
1675 if (shouldDrawCachedGlyphs(ti.fontEngine, s->matrix)) {
1676 QVarLengthArray<QFixedPoint> positions;
1677 QVarLengthArray<glyph_t> glyphs;
1678 QTransform matrix = QTransform::fromTranslate(p.x(), p.y());
1679 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1680
1681 {
1682 QStaticTextItem staticTextItem;
1683 staticTextItem.setFontEngine(ti.fontEngine);
1684 staticTextItem.glyphs = glyphs.data();
1685 staticTextItem.numGlyphs = glyphs.size();
1686 staticTextItem.glyphPositions = positions.data();
1687
1688 d->drawCachedGlyphs(glyphFormat, &staticTextItem);
1689 }
1690 return;
1691 }
1692
1693 QPaintEngineEx::drawTextItem(p, ti);
1694}
1695
1696namespace {
1697
1698 class QOpenGLStaticTextUserData: public QStaticTextUserData
1699 {
1700 public:
1701 QOpenGLStaticTextUserData()
1702 : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0)
1703 {
1704 }
1705
1706 ~QOpenGLStaticTextUserData()
1707 {
1708 }
1709
1710 QSize cacheSize;
1711 QOpenGL2PEXVertexArray vertexCoordinateArray;
1712 QOpenGL2PEXVertexArray textureCoordinateArray;
1713 QFontEngine::GlyphFormat glyphFormat;
1714 int cacheSerialNumber;
1715 };
1716
1717}
1718
1719
1720// #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO
1721
1722bool QOpenGL2PaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &t) const
1723{
1724 // The paint engine does not support projected cached glyph drawing
1725 if (t.type() == QTransform::TxProject)
1726 return false;
1727
1728 // The font engine might not support filling the glyph cache
1729 // with the given transform applied, in which case we need to
1730 // fall back to the QPainterPath code-path.
1731 if (!fontEngine->supportsTransformation(t)) {
1732 // Except that drawing paths is slow, so for scales between
1733 // 0.5 and 2.0 we leave the glyph cache untransformed and deal
1734 // with the transform ourselves when painting, resulting in
1735 // drawing 1x cached glyphs with a smooth-scale.
1736 float det = t.determinant();
1737 if (det >= 0.25f && det <= 4.f) {
1738 // Assuming the baseclass still agrees
1739 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t);
1740 }
1741
1742 return false; // Fall back to path-drawing
1743 }
1744
1745 return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t);
1746}
1747
1748void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat,
1749 QStaticTextItem *staticTextItem)
1750{
1751 Q_Q(QOpenGL2PaintEngineEx);
1752
1753 QOpenGL2PaintEngineState *s = q->state();
1754
1755 void *cacheKey = ctx; // use context, not the shareGroup() -> the GL glyph cache uses FBOs which may not be shareable
1756 bool recreateVertexArrays = false;
1757
1758 QTransform glyphCacheTransform;
1759 QFontEngine *fe = staticTextItem->fontEngine();
1760 if (fe->supportsTransformation(s->matrix)) {
1761 // The font-engine supports rendering glyphs with the current transform, so we
1762 // build a glyph-cache with the scale pre-applied, so that the cache contains
1763 // glyphs with the appropriate resolution in the case of retina displays.
1764 glyphCacheTransform = s->matrix.type() < QTransform::TxRotate ?
1765 QTransform::fromScale(qAbs(s->matrix.m11()), qAbs(s->matrix.m22())) :
1766 QTransform::fromScale(
1767 QVector2D(s->matrix.m11(), s->matrix.m12()).length(),
1768 QVector2D(s->matrix.m21(), s->matrix.m22()).length());
1769 }
1770
1771 QOpenGLTextureGlyphCache *cache =
1772 (QOpenGLTextureGlyphCache *) fe->glyphCache(cacheKey, glyphFormat, glyphCacheTransform);
1773 if (!cache || cache->glyphFormat() != glyphFormat || cache->contextGroup() == nullptr) {
1774 cache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
1775 fe->setGlyphCache(cacheKey, cache);
1776 recreateVertexArrays = true;
1777 }
1778
1779 if (staticTextItem->userDataNeedsUpdate) {
1780 recreateVertexArrays = true;
1781 } else if (staticTextItem->userData() == nullptr) {
1782 recreateVertexArrays = true;
1783 } else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1784 recreateVertexArrays = true;
1785 } else {
1786 QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData());
1787 if (userData->glyphFormat != glyphFormat) {
1788 recreateVertexArrays = true;
1789 } else if (userData->cacheSerialNumber != cache->serialNumber()) {
1790 recreateVertexArrays = true;
1791 }
1792 }
1793
1794 // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays.
1795 // If the cache size has changed, we do need to regenerate the vertices, but we don't need to repopulate the
1796 // cache so this text is performed before we test if the cache size has changed.
1797 if (recreateVertexArrays) {
1798 cache->setPaintEnginePrivate(this);
1799 if (!cache->populate(fe, staticTextItem->numGlyphs,
1800 staticTextItem->glyphs, staticTextItem->glyphPositions,
1801 s->renderHints)) {
1802 // No space for glyphs in cache. We need to reset it and try again.
1803 cache->clear();
1804 cache->populate(fe, staticTextItem->numGlyphs,
1805 staticTextItem->glyphs, staticTextItem->glyphPositions,
1806 s->renderHints);
1807 }
1808
1809 if (cache->hasPendingGlyphs()) {
1810 // Filling in the glyphs binds and sets parameters, so we need to
1811 // ensure that the glyph cache doesn't mess with whatever unit
1812 // is currently active. Note that the glyph cache internally
1813 // uses the image texture unit for blitting to the cache, while
1814 // we switch between image and mask units when drawing.
1815 static const GLenum glypchCacheTextureUnit = QT_IMAGE_TEXTURE_UNIT;
1816 activateTextureUnit(glypchCacheTextureUnit);
1817
1818 cache->fillInPendingGlyphs();
1819
1820 // We assume the cache can be trusted on which texture was bound
1821 lastTextureUsed = cache->texture();
1822
1823 // But since the brush and image texture units are possibly shared
1824 // we may have to re-bind brush textures after filling in the cache.
1825 brushTextureDirty = (QT_BRUSH_TEXTURE_UNIT == glypchCacheTextureUnit);
1826 }
1827 cache->setPaintEnginePrivate(nullptr);
1828 }
1829
1830 if (cache->width() == 0 || cache->height() == 0)
1831 return;
1832
1833 if (glyphFormat == QFontEngine::Format_ARGB)
1834 transferMode(ImageArrayDrawingMode);
1835 else
1836 transferMode(TextDrawingMode);
1837
1838 int margin = fe->glyphMargin(glyphFormat);
1839
1840 GLfloat dx = 1.0 / cache->width();
1841 GLfloat dy = 1.0 / cache->height();
1842
1843 // Use global arrays by default
1844 QOpenGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
1845 QOpenGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
1846
1847 if (staticTextItem->useBackendOptimizations) {
1848 QOpenGLStaticTextUserData *userData = nullptr;
1849
1850 if (staticTextItem->userData() == nullptr
1851 || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1852
1853 userData = new QOpenGLStaticTextUserData();
1854 staticTextItem->setUserData(userData);
1855
1856 } else {
1857 userData = static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData());
1858 }
1859
1860 userData->glyphFormat = glyphFormat;
1861 userData->cacheSerialNumber = cache->serialNumber();
1862
1863 // Use cache if backend optimizations is turned on
1864 vertexCoordinates = &userData->vertexCoordinateArray;
1865 textureCoordinates = &userData->textureCoordinateArray;
1866
1867 QSize size(cache->width(), cache->height());
1868 if (userData->cacheSize != size) {
1869 recreateVertexArrays = true;
1870 userData->cacheSize = size;
1871 }
1872 }
1873
1874 if (recreateVertexArrays) {
1875 vertexCoordinates->clear();
1876 textureCoordinates->clear();
1877
1878 bool supportsSubPixelPositions = fe->supportsSubPixelPositions();
1879 bool verticalSubPixelPositions = fe->supportsVerticalSubPixelPositions()
1880 && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
1881 for (int i=0; i<staticTextItem->numGlyphs; ++i) {
1882 QFixedPoint subPixelPosition;
1883 if (supportsSubPixelPositions) {
1884 subPixelPosition = fe->subPixelPositionFor(staticTextItem->glyphPositions[i]);
1885 if (!verticalSubPixelPositions)
1886 subPixelPosition.y = 0;
1887 }
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 = verticalSubPixelPositions
1897 ? qRound(staticTextItem->glyphPositions[i].y.toReal() * cache->transform().m22())
1898 : qFloor(staticTextItem->glyphPositions[i].y.toReal() * cache->transform().m22());
1899 y -= c.baseLineY + margin;
1900
1901 vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
1902 textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
1903 }
1904
1905 staticTextItem->userDataNeedsUpdate = false;
1906 }
1907
1908 int numGlyphs = vertexCoordinates->vertexCount() / 4;
1909 if (numGlyphs == 0)
1910 return;
1911
1912 if (elementIndices.size() < numGlyphs*6) {
1913 Q_ASSERT(elementIndices.size() % 6 == 0);
1914 int j = elementIndices.size() / 6 * 4;
1915 while (j < numGlyphs*4) {
1916 elementIndices.append(j + 0);
1917 elementIndices.append(j + 0);
1918 elementIndices.append(j + 1);
1919 elementIndices.append(j + 2);
1920 elementIndices.append(j + 3);
1921 elementIndices.append(j + 3);
1922
1923 j += 4;
1924 }
1925
1926#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1927 if (elementIndicesVBOId == 0)
1928 funcs.glGenBuffers(1, &elementIndicesVBOId);
1929
1930 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1931 funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort),
1932 elementIndices.constData(), GL_STATIC_DRAW);
1933#endif
1934 } else {
1935#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1936 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1937#endif
1938 }
1939
1940 if (glyphFormat != QFontEngine::Format_ARGB || recreateVertexArrays) {
1941 uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data(), vertexCoordinates->vertexCount() * 2);
1942 uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data(), textureCoordinates->vertexCount() * 2);
1943 }
1944
1945 if (!snapToPixelGrid) {
1946 snapToPixelGrid = true;
1947 matrixDirty = true;
1948 }
1949
1950 QBrush pensBrush = q->state()->pen.brush();
1951 setBrush(pensBrush);
1952
1953 if (glyphFormat == QFontEngine::Format_A32) {
1954
1955 // Subpixel antialiasing without gamma correction
1956
1957 QPainter::CompositionMode compMode = q->state()->composition_mode;
1958 Q_ASSERT(compMode == QPainter::CompositionMode_Source
1959 || compMode == QPainter::CompositionMode_SourceOver);
1960
1961 shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass1);
1962
1963 if (pensBrush.style() == Qt::SolidPattern) {
1964 // Solid patterns can get away with only one pass.
1965 QColor c = pensBrush.color();
1966 qreal oldOpacity = q->state()->opacity;
1967 if (compMode == QPainter::CompositionMode_Source) {
1968 c = qt_premultiplyColor(c, q->state()->opacity);
1969 q->state()->opacity = 1;
1970 opacityUniformDirty = true;
1971 }
1972
1973 compositionModeDirty = false; // I can handle this myself, thank you very much
1974 prepareForCachedGlyphDraw(*cache);
1975
1976 // prepareForCachedGlyphDraw() have set the opacity on the current shader, so the opacity state can now be reset.
1977 if (compMode == QPainter::CompositionMode_Source) {
1978 q->state()->opacity = oldOpacity;
1979 opacityUniformDirty = true;
1980 }
1981
1982 funcs.glEnable(GL_BLEND);
1983 funcs.glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
1984 funcs.glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
1985 } else {
1986 // Other brush styles need two passes.
1987
1988 qreal oldOpacity = q->state()->opacity;
1989 if (compMode == QPainter::CompositionMode_Source) {
1990 q->state()->opacity = 1;
1991 opacityUniformDirty = true;
1992 pensBrush = Qt::white;
1993 setBrush(pensBrush);
1994 }
1995
1996 compositionModeDirty = false; // I can handle this myself, thank you very much
1997 prepareForCachedGlyphDraw(*cache);
1998 funcs.glEnable(GL_BLEND);
1999 funcs.glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2000
2001 updateTexture(QT_MASK_TEXTURE_UNIT, cache->texture(), GL_REPEAT, GL_NEAREST, ForceUpdate);
2002
2003#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
2004 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
2005#else
2006 const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs);
2007 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data());
2008#endif
2009
2010 shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2);
2011
2012 if (compMode == QPainter::CompositionMode_Source) {
2013 q->state()->opacity = oldOpacity;
2014 opacityUniformDirty = true;
2015 pensBrush = q->state()->pen.brush();
2016 setBrush(pensBrush);
2017 }
2018
2019 compositionModeDirty = false;
2020 prepareForCachedGlyphDraw(*cache);
2021 funcs.glEnable(GL_BLEND);
2022 funcs.glBlendFunc(GL_ONE, GL_ONE);
2023 }
2024 compositionModeDirty = true;
2025 } else if (glyphFormat == QFontEngine::Format_ARGB) {
2026 currentBrush = noBrush;
2027 shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
2028 if (prepareForCachedGlyphDraw(*cache))
2029 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
2030 } else {
2031 // Grayscale/mono glyphs
2032
2033 shaderManager->setMaskType(QOpenGLEngineShaderManager::PixelMask);
2034 prepareForCachedGlyphDraw(*cache);
2035 }
2036
2037 GLenum textureUnit = QT_MASK_TEXTURE_UNIT;
2038 if (glyphFormat == QFontEngine::Format_ARGB)
2039 textureUnit = QT_IMAGE_TEXTURE_UNIT;
2040
2041 QOpenGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate) ?
2042 QOpenGLTextureGlyphCache::Linear : QOpenGLTextureGlyphCache::Nearest;
2043
2044 GLenum glFilterMode = filterMode == QOpenGLTextureGlyphCache::Linear ? GL_LINEAR : GL_NEAREST;
2045
2046 TextureUpdateMode updateMode = UpdateIfNeeded;
2047 if (cache->filterMode() != filterMode) {
2048 updateMode = ForceUpdate;
2049 cache->setFilterMode(filterMode);
2050 }
2051
2052 updateTexture(textureUnit, cache->texture(), GL_REPEAT, glFilterMode, updateMode);
2053
2054#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
2055 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
2056 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2057#else
2058 const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs);
2059 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data());
2060#endif
2061}
2062
2063void QOpenGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
2064 QPainter::PixmapFragmentHints hints)
2065{
2066 Q_D(QOpenGL2PaintEngineEx);
2067 // Use fallback for extended composition modes.
2068 if (state()->composition_mode > QPainter::CompositionMode_Plus) {
2069 QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
2070 return;
2071 }
2072
2073 ensureActive();
2074 int max_texture_size = d->ctx->d_func()->maxTextureSize();
2075 if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) {
2076 QPixmap scaled = pixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
2077 d->drawPixmapFragments(fragments, fragmentCount, scaled, hints);
2078 } else {
2079 d->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
2080 }
2081}
2082
2083
2084void QOpenGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments,
2085 int fragmentCount, const QPixmap &pixmap,
2086 QPainter::PixmapFragmentHints hints)
2087{
2088 GLfloat dx = 1.0f / pixmap.size().width();
2089 GLfloat dy = 1.0f / pixmap.size().height();
2090
2091 vertexCoordinateArray.clear();
2092 textureCoordinateArray.clear();
2093 opacityArray.reset();
2094
2095 if (snapToPixelGrid) {
2096 snapToPixelGrid = false;
2097 matrixDirty = true;
2098 }
2099
2100 bool allOpaque = true;
2101
2102 for (int i = 0; i < fragmentCount; ++i) {
2103 qreal s = 0;
2104 qreal c = 1;
2105 if (fragments[i].rotation != 0) {
2106 s = qFastSin(qDegreesToRadians(fragments[i].rotation));
2107 c = qFastCos(qDegreesToRadians(fragments[i].rotation));
2108 }
2109
2110 qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
2111 qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
2112 QOpenGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
2113 QOpenGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
2114
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 vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
2119 vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
2120 vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
2121
2122 QOpenGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
2123 (fragments[i].sourceLeft + fragments[i].width) * dx,
2124 (fragments[i].sourceTop + fragments[i].height) * dy);
2125
2126 textureCoordinateArray.addVertex(src.right, src.bottom);
2127 textureCoordinateArray.addVertex(src.right, src.top);
2128 textureCoordinateArray.addVertex(src.left, src.top);
2129 textureCoordinateArray.addVertex(src.left, src.top);
2130 textureCoordinateArray.addVertex(src.left, src.bottom);
2131 textureCoordinateArray.addVertex(src.right, src.bottom);
2132
2133 qreal opacity = fragments[i].opacity * q->state()->opacity;
2134 opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
2135 allOpaque &= (opacity >= 0.99f);
2136 }
2137
2138 transferMode(ImageOpacityArrayDrawingMode);
2139
2140 GLenum filterMode = q->state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
2141 updateTexture(QT_IMAGE_TEXTURE_UNIT, pixmap, GL_CLAMP_TO_EDGE, filterMode);
2142
2143 bool isBitmap = pixmap.isQBitmap();
2144 bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque;
2145
2146 // Setup for texture drawing
2147 currentBrush = noBrush;
2148 shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc
2149 : QOpenGLEngineShaderManager::ImageSrc);
2150 if (prepareForDraw(isOpaque))
2151 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT);
2152
2153 if (isBitmap) {
2154 QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
2155 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
2156 }
2157
2158 funcs.glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
2159}
2160
2161bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev)
2162{
2163 Q_D(QOpenGL2PaintEngineEx);
2164
2165 Q_ASSERT(pdev->devType() == QInternal::OpenGL);
2166 d->device = static_cast<QOpenGLPaintDevice*>(pdev);
2167
2168 if (!d->device)
2169 return false;
2170
2171 d->device->ensureActiveTarget();
2172
2173 if (d->device->context() != QOpenGLContext::currentContext() || !d->device->context()) {
2174 qWarning("QPainter::begin(): QOpenGLPaintDevice's context needs to be current");
2175 return false;
2176 }
2177
2178 if (d->ctx != QOpenGLContext::currentContext()
2179 || (d->ctx && QOpenGLContext::currentContext() && d->ctx->format() != QOpenGLContext::currentContext()->format())) {
2180 d->vertexBuffer.destroy();
2181 d->texCoordBuffer.destroy();
2182 d->opacityBuffer.destroy();
2183 d->indexBuffer.destroy();
2184 d->vao.destroy();
2185 }
2186
2187 d->ctx = QOpenGLContext::currentContext();
2188 d->ctx->d_func()->active_engine = this;
2189
2190 QOpenGLPaintDevicePrivate::get(d->device)->beginPaint();
2191
2192 d->funcs.initializeOpenGLFunctions();
2193
2194 // Generate a new Vertex Array Object if we don't have one already. We can
2195 // only hit the VAO-based path when using a core profile context. This is
2196 // because while non-core contexts can support VAOs via extensions, legacy
2197 // components like the QtOpenGL module do not know about VAOs. There are
2198 // still tests for QGL-QOpenGL paint engine interoperability, so keep the
2199 // status quo for now, and avoid introducing a VAO in non-core contexts.
2200 const bool needsVAO = d->ctx->format().profile() == QSurfaceFormat::CoreProfile
2201 && d->ctx->format().version() >= qMakePair(3, 2);
2202 if (needsVAO && !d->vao.isCreated()) {
2203 bool created = d->vao.create();
2204
2205 // If we managed to create it then we have a profile that supports VAOs
2206 if (created) {
2207 d->vao.bind();
2208
2209 // Generate a new Vertex Buffer Object if we don't have one already
2210 if (!d->vertexBuffer.isCreated()) {
2211 d->vertexBuffer.create();
2212 // Set its usage to StreamDraw, we will use this buffer only a few times before refilling it
2213 d->vertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2214 }
2215 if (!d->texCoordBuffer.isCreated()) {
2216 d->texCoordBuffer.create();
2217 d->texCoordBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2218 }
2219 if (!d->opacityBuffer.isCreated()) {
2220 d->opacityBuffer.create();
2221 d->opacityBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2222 }
2223 if (!d->indexBuffer.isCreated()) {
2224 d->indexBuffer.create();
2225 d->indexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2226 }
2227 }
2228 }
2229
2230 for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
2231 d->vertexAttributeArraysEnabledState[i] = false;
2232
2233 const QSize sz = d->device->size();
2234 d->width = sz.width();
2235 d->height = sz.height();
2236 d->mode = BrushDrawingMode;
2237 d->brushTextureDirty = true;
2238 d->brushUniformsDirty = true;
2239 d->matrixUniformDirty = true;
2240 d->matrixDirty = true;
2241 d->compositionModeDirty = true;
2242 d->opacityUniformDirty = true;
2243 d->needsSync = true;
2244 d->useSystemClip = !systemClip().isEmpty();
2245 d->currentBrush = QBrush();
2246
2247 d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
2248 d->stencilClean = true;
2249
2250 d->shaderManager = new QOpenGLEngineShaderManager(d->ctx);
2251
2252 d->funcs.glDisable(GL_STENCIL_TEST);
2253 d->funcs.glDisable(GL_DEPTH_TEST);
2254 d->funcs.glDisable(GL_SCISSOR_TEST);
2255
2256 d->glyphCacheFormat = QFontEngine::Format_A8;
2257
2258#if !QT_CONFIG(opengles2)
2259 if (!QOpenGLContext::currentContext()->isOpenGLES()) {
2260 d->funcs.glDisable(GL_MULTISAMPLE);
2261 d->glyphCacheFormat = QFontEngine::Format_A32;
2262 d->multisamplingAlwaysEnabled = false;
2263 } else
2264#endif // !QT_CONFIG(opengles2)
2265 {
2266 // OpenGL ES can't switch MSAA off, so if the gl paint device is
2267 // multisampled, it's always multisampled.
2268 d->multisamplingAlwaysEnabled = d->device->context()->format().samples() > 1;
2269 }
2270
2271 return true;
2272}
2273
2274bool QOpenGL2PaintEngineEx::end()
2275{
2276 Q_D(QOpenGL2PaintEngineEx);
2277
2278 QOpenGLPaintDevicePrivate::get(d->device)->endPaint();
2279
2280 QOpenGLContext *ctx = d->ctx;
2281 d->funcs.glUseProgram(0);
2282 d->transferMode(BrushDrawingMode);
2283