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