268 Q_Q(QOpenGL2PaintEngineEx);
270 Qt::BrushStyle style = currentBrush.style();
272 bool smoothPixmapTransform = q->state()->renderHints & QPainter::SmoothPixmapTransform;
273 GLenum filterMode = smoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
275 if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) {
277 QImage textureImage = qt_imageForBrush(style,
false);
281 else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
284 const QGradient *gradient = currentBrush.gradient();
286 GLenum wrapMode = GL_CLAMP_TO_EDGE;
287 if (gradient->spread() == QGradient::RepeatSpread || gradient->type() == QGradient::ConicalGradient)
288 wrapMode = GL_REPEAT;
289 else if (gradient->spread() == QGradient::ReflectSpread)
290 wrapMode = GL_MIRRORED_REPEAT;
294 else if (style == Qt::TexturePattern) {
295 currentBrushImage = currentBrush.textureImage();
297 int max_texture_size = ctx->d_func()->maxTextureSize();
298 QSize newSize = currentBrushImage.size();
299 newSize = newSize.boundedTo(QSize(max_texture_size, max_texture_size));
300 if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) {
301 if (!isPowerOfTwo(newSize.width()) || !isPowerOfTwo(newSize.height())) {
302 newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1));
303 newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1));
306 if (currentBrushImage.size() != newSize)
307 currentBrushImage = currentBrushImage.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
309 GLuint wrapMode = GL_REPEAT;
320 Qt::BrushStyle style = currentBrush.style();
322 if (style == Qt::NoBrush)
325 QTransform brushQTransform = currentBrush.transform();
326 bool isCosmetic =
false;
328 if (style == Qt::SolidPattern) {
329 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
330 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::FragmentColor), col);
334 QPointF translationPoint;
336 if (style <= Qt::DiagCrossPattern) {
337 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
339 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
342 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
344 isCosmetic = !q->painter()->testRenderHint(QPainter::NonCosmeticBrushPatterns);
346 else if (style == Qt::LinearGradientPattern) {
347 const QLinearGradient *g =
static_cast<
const QLinearGradient *>(currentBrush.gradient());
349 QPointF realStart = g->start();
350 QPointF realFinal = g->finalStop();
351 translationPoint = realStart;
353 QPointF l = realFinal - realStart;
355 QVector3D linearData(
358 1.0f / (l.x() * l.x() + l.y() * l.y())
361 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::LinearData), linearData);
364 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
366 else if (style == Qt::ConicalGradientPattern) {
367 const QConicalGradient *g =
static_cast<
const QConicalGradient *>(currentBrush.gradient());
368 translationPoint = g->center();
370 GLfloat angle = -qDegreesToRadians(g->angle());
372 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Angle), angle);
375 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
377 else if (style == Qt::RadialGradientPattern) {
378 const QRadialGradient *g =
static_cast<
const QRadialGradient *>(currentBrush.gradient());
379 QPointF realCenter = g->center();
380 QPointF realFocal = g->focalPoint();
381 qreal realRadius = g->centerRadius() - g->focalRadius();
382 translationPoint = realFocal;
384 QPointF fmp = realCenter - realFocal;
385 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp), fmp);
387 GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius;
388 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
389 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Inverse2Fmp2MRadius2),
390 GLfloat(1.0 / (2.0*fmp2_m_radius2)));
391 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::SqrFr),
392 GLfloat(g->focalRadius() * g->focalRadius()));
393 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BRadius),
394 GLfloat(2 * (g->centerRadius() - g->focalRadius()) * g->focalRadius()),
396 g->centerRadius() - g->focalRadius());
399 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
401 else if (style == Qt::TexturePattern) {
402 const QPixmap& texPixmap = currentBrush.texture();
404 if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) {
405 QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity);
406 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
409 QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
410 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
413 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize);
416 qWarning(
"QOpenGL2PaintEngineEx: Unimplemented fill style");
418 const QPointF &brushOrigin = q->state()->brushOrigin;
421 matrix = q->state()->matrix;
422 matrix.translate(brushOrigin.x(), brushOrigin.y());
424 matrix = brushQTransform * matrix;
426 QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
429 if (device->paintFlipped()) {
433 QTransform gl_to_qt(1, 0, 0, m22, 0, dy);
434 QTransform inv_matrix = gl_to_qt * matrix.inverted() * translate;
436 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTransform), inv_matrix);
437 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTexture),
QT_BRUSH_TEXTURE_UNIT);
520 if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::BlendEquationAdvanced)) {
521 if (q->state()->composition_mode <= QPainter::CompositionMode_Plus) {
523 funcs.glBlendEquation(GL_FUNC_ADD);
527 shaderManager->setCompositionMode(q->state()->composition_mode);
529 if (q->state()->composition_mode > QPainter::CompositionMode_Plus) {
530 qWarning(
"Unsupported composition mode");
535 switch(q->state()->composition_mode) {
536 case QPainter::CompositionMode_SourceOver:
537 funcs.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
539 case QPainter::CompositionMode_DestinationOver:
540 funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
542 case QPainter::CompositionMode_Clear:
543 funcs.glBlendFunc(GL_ZERO, GL_ZERO);
545 case QPainter::CompositionMode_Source:
546 funcs.glBlendFunc(GL_ONE, GL_ZERO);
548 case QPainter::CompositionMode_Destination:
549 funcs.glBlendFunc(GL_ZERO, GL_ONE);
551 case QPainter::CompositionMode_SourceIn:
552 funcs.glBlendFunc(GL_DST_ALPHA, GL_ZERO);
554 case QPainter::CompositionMode_DestinationIn:
555 funcs.glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
557 case QPainter::CompositionMode_SourceOut:
558 funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);
560 case QPainter::CompositionMode_DestinationOut:
561 funcs.glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
563 case QPainter::CompositionMode_SourceAtop:
564 funcs.glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
566 case QPainter::CompositionMode_DestinationAtop:
567 funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA);
569 case QPainter::CompositionMode_Xor:
570 funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
572 case QPainter::CompositionMode_Plus:
573 funcs.glBlendFunc(GL_ONE, GL_ONE);
575 case QPainter::CompositionMode_Multiply:
578 case QPainter::CompositionMode_Screen:
581 case QPainter::CompositionMode_Overlay:
584 case QPainter::CompositionMode_Darken:
587 case QPainter::CompositionMode_Lighten:
590 case QPainter::CompositionMode_ColorDodge:
593 case QPainter::CompositionMode_ColorBurn:
596 case QPainter::CompositionMode_HardLight:
599 case QPainter::CompositionMode_SoftLight:
602 case QPainter::CompositionMode_Difference:
605 case QPainter::CompositionMode_Exclusion:
609 qWarning(
"Unsupported composition mode");
850 if (currentBrush.style() > Qt::SolidPattern)
854 const bool supportsElementIndexUint = funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
856 const QPointF*
const points =
reinterpret_cast<
const QPointF*>(path.points());
859 if (path.shape() == QVectorPath::RectangleHint) {
860 QOpenGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y());
863 }
else if (path.isConvex()) {
865 if (path.isCacheable()) {
866 QVectorPath::CacheEntry *data = path.lookupCacheData(q);
867 QOpenGL2PEVectorPathCache *cache;
869 bool updateCache =
false;
872 cache = (QOpenGL2PEVectorPathCache *) data->data;
874 qreal scaleFactor = cache->iscale / inverseScale;
875 if (scaleFactor < 0.5 || scaleFactor > 2.0) {
876#ifdef QT_OPENGL_CACHE_AS_VBOS
877 glDeleteBuffers(1, &cache->vbo);
879 Q_ASSERT(cache->ibo == 0);
881 free(cache->vertices);
882 Q_ASSERT(cache->indices ==
nullptr);
887 cache =
new QOpenGL2PEVectorPathCache;
888 data =
const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
894 vertexCoordinateArray.clear();
895 vertexCoordinateArray.addPath(path, inverseScale,
false);
896 int vertexCount = vertexCoordinateArray.vertexCount();
897 int floatSizeInBytes = vertexCount * 2 *
sizeof(
float);
898 cache->vertexCount = vertexCount;
899 cache->indexCount = 0;
900 cache->primitiveType = GL_TRIANGLE_FAN;
901 cache->iscale = inverseScale;
902#ifdef QT_OPENGL_CACHE_AS_VBOS
903 funcs.glGenBuffers(1, &cache->vbo);
904 funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
905 funcs.glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
908 cache->vertices = (
float *) malloc(floatSizeInBytes);
909 memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
910 cache->indices =
nullptr;
915#ifdef QT_OPENGL_CACHE_AS_VBOS
916 funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
917 uploadData(QT_VERTEX_COORD_ATTR, 0, cache->vertexCount);
918 setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
920 uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2);
922 funcs.glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
927 path.makeCacheable();
928 vertexCoordinateArray.clear();
929 vertexCoordinateArray.addPath(path, inverseScale,
false);
931 drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
935 bool useCache = path.isCacheable();
937 QRectF bbox = path.controlPointRect();
939 useCache &= (bbox.left() > -0x8000 * inverseScale)
940 && (bbox.right() < 0x8000 * inverseScale)
941 && (bbox.top() > -0x8000 * inverseScale)
942 && (bbox.bottom() < 0x8000 * inverseScale);
946 QVectorPath::CacheEntry *data = path.lookupCacheData(q);
947 QOpenGL2PEVectorPathCache *cache;
949 bool updateCache =
false;
952 cache = (QOpenGL2PEVectorPathCache *) data->data;
954 qreal scaleFactor = cache->iscale / inverseScale;
955 if (scaleFactor < 0.5 || scaleFactor > 2.0) {
956#ifdef QT_OPENGL_CACHE_AS_VBOS
957 glDeleteBuffers(1, &cache->vbo);
958 glDeleteBuffers(1, &cache->ibo);
960 free(cache->vertices);
961 free(cache->indices);
966 cache =
new QOpenGL2PEVectorPathCache;
967 data =
const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath);
973 QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale), 1, supportsElementIndexUint);
974 cache->vertexCount = polys.vertices.size() / 2;
975 cache->indexCount = polys.indices.size();
976 cache->primitiveType = GL_TRIANGLES;
977 cache->iscale = inverseScale;
978 cache->indexType = polys.indices.type();
979#ifdef QT_OPENGL_CACHE_AS_VBOS
980 funcs.glGenBuffers(1, &cache->vbo);
981 funcs.glGenBuffers(1, &cache->ibo);
982 funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
983 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
985 if (polys.indices.type() == QVertexIndexVector::UnsignedInt)
986 funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
988 funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(quint16) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW);
990 QVarLengthArray<
float> vertices(polys.vertices.size());
991 for (
int i = 0; i < polys.vertices.size(); ++i)
992 vertices[i] =
float(inverseScale * polys.vertices.at(i));
993 funcs.glBufferData(GL_ARRAY_BUFFER,
sizeof(
float) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
995 cache->vertices = (
float *) malloc(
sizeof(
float) * polys.vertices.size());
996 if (polys.indices.type() == QVertexIndexVector::UnsignedInt) {
997 cache->indices = (quint32 *) malloc(
sizeof(quint32) * polys.indices.size());
998 memcpy(cache->indices, polys.indices.data(),
sizeof(quint32) * polys.indices.size());
1000 cache->indices = (quint16 *) malloc(
sizeof(quint16) * polys.indices.size());
1001 memcpy(cache->indices, polys.indices.data(),
sizeof(quint16) * polys.indices.size());
1003 for (
int i = 0; i < polys.vertices.size(); ++i)
1004 cache->vertices[i] =
float(inverseScale * polys.vertices.at(i));
1009#ifdef QT_OPENGL_CACHE_AS_VBOS
1010 funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
1011 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
1012 uploadData(QT_VERTEX_COORDS_ATTR, 0, cache->vertexCount);
1013 setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
1014 if (cache->indexType == QVertexIndexVector::UnsignedInt)
1015 funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0);
1017 funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, 0);
1018 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1019 funcs.glBindBuffer(GL_ARRAY_BUFFER, 0);
1021 uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2);
1022 const GLenum indexValueType = cache->indexType == QVertexIndexVector::UnsignedInt ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
1023 const bool useIndexVbo = uploadIndexData(cache->indices, indexValueType, cache->indexCount);
1024 funcs.glDrawElements(cache->primitiveType, cache->indexCount, indexValueType, useIndexVbo ?
nullptr : cache->indices);
1030 path.makeCacheable();
1032 if (device->context()->format().stencilBufferSize() <= 0) {
1035 QRectF bbox = path.controlPointRect();
1037 bool withinLimits = (bbox.left() > -0x8000 * inverseScale)
1038 && (bbox.right() < 0x8000 * inverseScale)
1039 && (bbox.top() > -0x8000 * inverseScale)
1040 && (bbox.bottom() < 0x8000 * inverseScale);
1042 QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale), 1, supportsElementIndexUint);
1044 QVarLengthArray<
float> vertices(polys.vertices.size());
1045 for (
int i = 0; i < polys.vertices.size(); ++i)
1046 vertices[i] =
float(inverseScale * polys.vertices.at(i));
1049 uploadData(QT_VERTEX_COORDS_ATTR, vertices.constData(), vertices.size());
1050 const GLenum indexValueType = funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
1051 const bool useIndexVbo = uploadIndexData(polys.indices.data(), indexValueType, polys.indices.size());
1052 funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), indexValueType, useIndexVbo ?
nullptr : polys.indices.data());
1055 qWarning(
"Painter path exceeds +/-32767 pixels.");
1061 vertexCoordinateArray.clear();
1062 vertexCoordinateArray.addPath(path, inverseScale,
false);
1064 fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
1066 funcs.glStencilMask(0xff);
1067 funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1069 if (q->state()->clipTestEnabled) {
1072 }
else if (path.hasWindingFill()) {
1074 funcs.glStencilFunc(GL_NOTEQUAL, 0, 0xff);
1082 composite(vertexCoordinateArray.boundingRect());
1083 funcs.glStencilMask(0);
1094 const QOpenGLRect &bounds,
1095 StencilFillMode mode)
1097 Q_ASSERT(count || stops);
1100 funcs.glStencilMask(0xff);
1102 if (dirtyStencilRegion.intersects(currentScissorBounds)) {
1103 const QRegion clearRegion = dirtyStencilRegion.intersected(currentScissorBounds);
1104 funcs.glClearStencil(0);
1105 for (
const QRect &rect : clearRegion) {
1106#ifndef QT_GL_NO_SCISSOR_TEST
1109 funcs.glClear(GL_STENCIL_BUFFER_BIT);
1112 dirtyStencilRegion -= currentScissorBounds;
1114#ifndef QT_GL_NO_SCISSOR_TEST
1119 funcs.glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1122 if (ctx->format().stencilBufferSize() <= 0)
1123 qWarning(
"OpenGL paint engine: attempted to use stencil test without requesting a stencil buffer.");
1125 funcs.glEnable(GL_STENCIL_TEST);
1127 if (mode == WindingFillMode) {
1128 Q_ASSERT(stops && !count);
1129 if (q->state()->clipTestEnabled) {
1132 funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1138 funcs.glStencilFunc(GL_ALWAYS, 0, 0xff);
1139 funcs.glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
1144 funcs.glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
1146 funcs.glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
1148 drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
1150 if (q->state()->clipTestEnabled) {
1153 funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1157 }
else if (mode == OddEvenFillMode) {
1159 funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
1160 drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN);
1163 Q_ASSERT(count && !stops);
1166 funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
1167 setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
1168 funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1171 funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1172 if (q->state()->clipTestEnabled) {
1179 uploadData(QT_VERTEX_COORDS_ATTR, data, count * 2);
1180 funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
1185 funcs.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1384 const Qt::PenStyle penStyle = qpen_style(pen);
1385 const QBrush &penBrush = qpen_brush(pen);
1386 const bool opaque = penBrush.isOpaque() && s->opacity > 0.99;
1395 QRectF clip = q->state()->matrix.inverted().mapRect(q->state()->clipEnabled
1396 ? q->state()->rectangleClip
1397 : QRectF(0, 0, width, height));
1399 if (penStyle == Qt::SolidLine) {
1400 stroker.process(path, pen, clip, s->renderHints);
1403 dasher.process(path, pen, clip, s->renderHints);
1405 QVectorPath dashStroke(dasher.points(),
1406 dasher.elementCount(),
1407 dasher.elementTypes());
1408 stroker.process(dashStroke, pen, clip, s->renderHints);
1411 if (!stroker.vertexCount())
1417 uploadData(QT_VERTEX_COORDS_ATTR, stroker.vertices(), stroker.vertexCount());
1418 funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2);
1420 qreal width = qpen_widthf(pen) / 2;
1423 qreal extra = pen.joinStyle() == Qt::MiterJoin
1424 ? qMax(pen.miterLimit() * width, width)
1427 if (pen.isCosmetic())
1428 extra = extra * inverseScale;
1430 QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra);
1432 fillStencilWithVertexArray(stroker.vertices(), stroker.vertexCount() / 2,
1433 nullptr, 0, bounds, QOpenGL2PaintEngineExPrivate::TriStripStrokeFillMode);
1435 funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
1444 funcs.glStencilMask(0);
1552void QOpenGL2PaintEngineEx::drawImage(
const QRectF& dest,
const QImage& image,
const QRectF& src,
1553 Qt::ImageConversionFlags)
1555 Q_D(QOpenGL2PaintEngineEx);
1556 QOpenGLContext *ctx = d->ctx;
1558 int max_texture_size = ctx->d_func()->maxTextureSize();
1559 if (image.width() > max_texture_size || image.height() > max_texture_size) {
1560 QImage scaled = image.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
1562 const qreal sx = scaled.width() / qreal(image.width());
1563 const qreal sy = scaled.height() / qreal(image.height());
1565 drawImage(dest, scaled, scaleRect(src, sx, sy));
1570 d->transferMode(ImageDrawingMode);
1572 QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
1574 switch (image.format()) {
1575 case QImage::Format_RGBA8888:
1576 case QImage::Format_ARGB32:
1577 case QImage::Format_RGBA64:
1578 case QImage::Format_RGBA16FPx4:
1579 case QImage::Format_RGBA32FPx4:
1580 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc);
1583 case QImage::Format_Alpha8:
1584 if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
1585 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc);
1586 bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption;
1588 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1590 case QImage::Format_Grayscale8:
1591 case QImage::Format_Grayscale16:
1592 if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
1593 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc);
1594 bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption;
1596 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1599 d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
1603 ImageWithBindOptions imageWithOptions = { image, bindOption };
1604 GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
1607 d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
1762 QStaticTextItem *staticTextItem)
1764 Q_Q(QOpenGL2PaintEngineEx);
1768 void *cacheKey = ctx;
1769 bool recreateVertexArrays =
false;
1771 QTransform glyphCacheTransform;
1772 QFontEngine *fe = staticTextItem->fontEngine();
1773 if (fe->supportsTransformation(s->matrix)) {
1777 glyphCacheTransform = s->matrix.type() < QTransform::TxRotate ?
1778 QTransform::fromScale(qAbs(s->matrix.m11()), qAbs(s->matrix.m22())) :
1779 QTransform::fromScale(
1780 QVector2D(s->matrix.m11(), s->matrix.m12()).length(),
1781 QVector2D(s->matrix.m21(), s->matrix.m22()).length());
1784 QOpenGLTextureGlyphCache *cache =
1785 (QOpenGLTextureGlyphCache *) fe->glyphCache(cacheKey, glyphFormat, glyphCacheTransform);
1786 if (!cache || cache->glyphFormat() != glyphFormat || cache->contextGroup() ==
nullptr) {
1787 cache =
new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
1788 fe->setGlyphCache(cacheKey, cache);
1789 recreateVertexArrays =
true;
1792 if (staticTextItem->userDataNeedsUpdate) {
1793 recreateVertexArrays =
true;
1794 }
else if (staticTextItem->userData() ==
nullptr) {
1795 recreateVertexArrays =
true;
1796 }
else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1797 recreateVertexArrays =
true;
1799 QOpenGLStaticTextUserData *userData =
static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData());
1800 if (userData->glyphFormat != glyphFormat) {
1801 recreateVertexArrays =
true;
1802 }
else if (userData->cacheSerialNumber != cache->serialNumber()) {
1803 recreateVertexArrays =
true;
1810 if (recreateVertexArrays) {
1811 cache->setPaintEnginePrivate(
this);
1812 if (!cache->populate(fe, staticTextItem->numGlyphs,
1813 staticTextItem->glyphs, staticTextItem->glyphPositions,
1817 cache->populate(fe, staticTextItem->numGlyphs,
1818 staticTextItem->glyphs, staticTextItem->glyphPositions,
1822 if (cache->hasPendingGlyphs()) {
1829 activateTextureUnit(glypchCacheTextureUnit);
1831 cache->fillInPendingGlyphs();
1834 lastTextureUsed = cache->texture();
1840 cache->setPaintEnginePrivate(
nullptr);
1843 if (cache->width() == 0 || cache->height() == 0)
1846 if (glyphFormat == QFontEngine::Format_ARGB)
1851 int margin = fe->glyphMargin(glyphFormat);
1853 GLfloat dx = 1.0 / cache->width();
1854 GLfloat dy = 1.0 / cache->height();
1857 QOpenGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray;
1858 QOpenGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray;
1860 if (staticTextItem->useBackendOptimizations) {
1861 QOpenGLStaticTextUserData *userData =
nullptr;
1863 if (staticTextItem->userData() ==
nullptr
1864 || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) {
1866 userData =
new QOpenGLStaticTextUserData();
1867 staticTextItem->setUserData(userData);
1870 userData =
static_cast<QOpenGLStaticTextUserData*>(staticTextItem->userData());
1873 userData->glyphFormat = glyphFormat;
1874 userData->cacheSerialNumber = cache->serialNumber();
1877 vertexCoordinates = &userData->vertexCoordinateArray;
1878 textureCoordinates = &userData->textureCoordinateArray;
1880 QSize size(cache->width(), cache->height());
1881 if (userData->cacheSize != size) {
1882 recreateVertexArrays =
true;
1883 userData->cacheSize = size;
1887 if (recreateVertexArrays) {
1888 vertexCoordinates->clear();
1889 textureCoordinates->clear();
1891 bool supportsSubPixelPositions = fe->supportsSubPixelPositions();
1892 bool verticalSubPixelPositions = fe->supportsVerticalSubPixelPositions()
1893 && (s->renderHints & QPainter::VerticalSubpixelPositioning) != 0;
1894 for (
int i=0; i<staticTextItem->numGlyphs; ++i) {
1895 QFixedPoint subPixelPosition;
1896 if (supportsSubPixelPositions) {
1897 subPixelPosition = fe->subPixelPositionFor(staticTextItem->glyphPositions[i]);
1898 if (!verticalSubPixelPositions)
1899 subPixelPosition.y = 0;
1902 QTextureGlyphCache::GlyphAndSubPixelPosition glyph(staticTextItem->glyphs[i], subPixelPosition);
1904 const QTextureGlyphCache::Coord &c = cache->coords[glyph];
1908 int x = qFloor(staticTextItem->glyphPositions[i].x.toReal() * cache->transform().m11()) + c.baseLineX - margin;
1909 int y = verticalSubPixelPositions
1910 ? qRound(staticTextItem->glyphPositions[i].y.toReal() * cache->transform().m22())
1911 : qFloor(staticTextItem->glyphPositions[i].y.toReal() * cache->transform().m22());
1912 y -= c.baseLineY + margin;
1914 vertexCoordinates->addQuad(QRectF(x, y, c.w, c.h));
1915 textureCoordinates->addQuad(QRectF(c.x*dx, c.y*dy, c.w * dx, c.h * dy));
1918 staticTextItem->userDataNeedsUpdate =
false;
1921 int numGlyphs = vertexCoordinates->vertexCount() / 4;
1925 if (elementIndices.size() < numGlyphs*6) {
1926 Q_ASSERT(elementIndices.size() % 6 == 0);
1927 int j = elementIndices.size() / 6 * 4;
1928 while (j < numGlyphs*4) {
1929 elementIndices.append(j + 0);
1930 elementIndices.append(j + 0);
1931 elementIndices.append(j + 1);
1932 elementIndices.append(j + 2);
1933 elementIndices.append(j + 3);
1934 elementIndices.append(j + 3);
1939#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1940 if (elementIndicesVBOId == 0)
1941 funcs.glGenBuffers(1, &elementIndicesVBOId);
1943 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1944 funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() *
sizeof(GLushort),
1945 elementIndices.constData(), GL_STATIC_DRAW);
1948#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
1949 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId);
1953 if (glyphFormat != QFontEngine::Format_ARGB || recreateVertexArrays) {
1954 uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data(), vertexCoordinates->vertexCount() * 2);
1955 uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data(), textureCoordinates->vertexCount() * 2);
1963 QBrush pensBrush = q->state()->pen.brush();
1964 setBrush(pensBrush);
1966 if (glyphFormat == QFontEngine::Format_A32) {
1970 QPainter::CompositionMode compMode = q->state()->composition_mode;
1971 Q_ASSERT(compMode == QPainter::CompositionMode_Source
1972 || compMode == QPainter::CompositionMode_SourceOver);
1974 shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass1);
1976 if (pensBrush.style() == Qt::SolidPattern) {
1978 QColor c = pensBrush.color();
1979 qreal oldOpacity = q->state()->opacity;
1980 if (compMode == QPainter::CompositionMode_Source) {
1981 c = qt_premultiplyColor(c, q->state()->opacity);
1982 q->state()->opacity = 1;
1987 prepareForCachedGlyphDraw(*cache);
1990 if (compMode == QPainter::CompositionMode_Source) {
1991 q->state()->opacity = oldOpacity;
1995 funcs.glEnable(GL_BLEND);
1996 funcs.glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
1997 funcs.glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF());
2001 qreal oldOpacity = q->state()->opacity;
2002 if (compMode == QPainter::CompositionMode_Source) {
2003 q->state()->opacity = 1;
2005 pensBrush = Qt::white;
2006 setBrush(pensBrush);
2010 prepareForCachedGlyphDraw(*cache);
2011 funcs.glEnable(GL_BLEND);
2012 funcs.glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2016#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
2017 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
2019 const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs);
2020 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ?
nullptr : elementIndices.data());
2023 shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2);
2025 if (compMode == QPainter::CompositionMode_Source) {
2026 q->state()->opacity = oldOpacity;
2028 pensBrush = q->state()->pen.brush();
2029 setBrush(pensBrush);
2033 prepareForCachedGlyphDraw(*cache);
2034 funcs.glEnable(GL_BLEND);
2035 funcs.glBlendFunc(GL_ONE, GL_ONE);
2038 }
else if (glyphFormat == QFontEngine::Format_ARGB) {
2039 currentBrush = noBrush;
2040 shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
2041 if (prepareForCachedGlyphDraw(*cache))
2042 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture),
QT_IMAGE_TEXTURE_UNIT);
2046 shaderManager->setMaskType(QOpenGLEngineShaderManager::PixelMask);
2047 prepareForCachedGlyphDraw(*cache);
2051 if (glyphFormat == QFontEngine::Format_ARGB)
2054 QOpenGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate) ?
2055 QOpenGLTextureGlyphCache::Linear : QOpenGLTextureGlyphCache::Nearest;
2057 GLenum glFilterMode = filterMode == QOpenGLTextureGlyphCache::Linear ? GL_LINEAR : GL_NEAREST;
2060 if (cache->filterMode() != filterMode) {
2062 cache->setFilterMode(filterMode);
2065 updateTexture(textureUnit, cache->texture(), GL_REPEAT, glFilterMode, updateMode);
2067#if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO)
2068 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0);
2069 funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2071 const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs);
2072 funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ?
nullptr : elementIndices.data());
2100 int fragmentCount,
const QPixmap &pixmap,
2101 QPainter::PixmapFragmentHints hints)
2103 GLfloat dx = 1.0f / pixmap.size().width();
2104 GLfloat dy = 1.0f / pixmap.size().height();
2106 vertexCoordinateArray.clear();
2107 textureCoordinateArray.clear();
2108 opacityArray.reset();
2115 bool allOpaque =
true;
2117 for (
int i = 0; i < fragmentCount; ++i) {
2120 if (fragments[i].rotation != 0) {
2121 s = qFastSin(qDegreesToRadians(fragments[i].rotation));
2122 c = qFastCos(qDegreesToRadians(fragments[i].rotation));
2125 qreal right = 0.5 * fragments[i].scaleX * fragments[i].width;
2126 qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height;
2127 QOpenGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c);
2128 QOpenGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c);
2130 vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
2131 vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y);
2132 vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
2133 vertexCoordinateArray.addVertex(-bottomRight.x + fragments[i].x, -bottomRight.y + fragments[i].y);
2134 vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y);
2135 vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y);
2137 QOpenGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy,
2138 (fragments[i].sourceLeft + fragments[i].width) * dx,
2139 (fragments[i].sourceTop + fragments[i].height) * dy);
2141 textureCoordinateArray.addVertex(src.right, src.bottom);
2142 textureCoordinateArray.addVertex(src.right, src.top);
2143 textureCoordinateArray.addVertex(src.left, src.top);
2144 textureCoordinateArray.addVertex(src.left, src.top);
2145 textureCoordinateArray.addVertex(src.left, src.bottom);
2146 textureCoordinateArray.addVertex(src.right, src.bottom);
2148 qreal opacity = fragments[i].opacity * q->state()->opacity;
2149 opacityArray << opacity << opacity << opacity << opacity << opacity << opacity;
2150 allOpaque &= (opacity >= 0.99f);
2155 uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data(), vertexCoordinateArray.vertexCount() * 2);
2156 uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data(), textureCoordinateArray.vertexCount() * 2);
2157 uploadData(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data(), opacityArray.size());
2159 GLenum filterMode = q->state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST;
2162 bool isBitmap = pixmap.isQBitmap();
2163 bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque;
2166 currentBrush = noBrush;
2167 shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc
2168 : QOpenGLEngineShaderManager::ImageSrc);
2169 if (prepareForDraw(isOpaque))
2170 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture),
QT_IMAGE_TEXTURE_UNIT);
2173 QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity);
2174 shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col);
2177 funcs.glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount);
2180bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev)
2182 Q_D(QOpenGL2PaintEngineEx);
2184 Q_ASSERT(pdev->devType() == QInternal::OpenGL);
2185 d->device =
static_cast<QOpenGLPaintDevice*>(pdev);
2190 d->device->ensureActiveTarget();
2192 if (d->device->context() != QOpenGLContext::currentContext() || !d->device->context()) {
2193 qWarning(
"QPainter::begin(): QOpenGLPaintDevice's context needs to be current");
2197 if (d->ctx != QOpenGLContext::currentContext()
2198 || (d->ctx && QOpenGLContext::currentContext() && d->ctx->format() != QOpenGLContext::currentContext()->format())) {
2199 d->vertexBuffer.destroy();
2200 d->texCoordBuffer.destroy();
2201 d->opacityBuffer.destroy();
2202 d->indexBuffer.destroy();
2206 d->ctx = QOpenGLContext::currentContext();
2207 d->ctx->d_func()->active_engine =
this;
2209 QOpenGLPaintDevicePrivate::get(d->device)->beginPaint();
2211 d->funcs.initializeOpenGLFunctions();
2219 const bool needsVAO = d->ctx->format().profile() == QSurfaceFormat::CoreProfile
2220 && d->ctx->format().version() >= std::pair(3, 2);
2221 if (needsVAO && !d->vao.isCreated()) {
2222 bool created = d->vao.create();
2230 if (!d->vertexBuffer.isCreated()) {
2231 d->vertexBuffer.create();
2233 d->vertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2235 if (!d->texCoordBuffer.isCreated()) {
2236 d->texCoordBuffer.create();
2237 d->texCoordBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2239 if (!d->opacityBuffer.isCreated()) {
2240 d->opacityBuffer.create();
2241 d->opacityBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2243 if (!d->indexBuffer.isCreated()) {
2244 d->indexBuffer.create();
2245 d->indexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
2249 d->vertexAttributeArraysEnabledState[i] =
false;
2251 const QSize sz = d->device->size();
2252 d->width = sz.width();
2253 d->height = sz.height();
2254 d->mode = BrushDrawingMode;
2255 d->brushTextureDirty =
true;
2256 d->brushUniformsDirty =
true;
2257 d->matrixUniformDirty =
true;
2258 d->matrixDirty =
true;
2259 d->compositionModeDirty =
true;
2260 d->opacityUniformDirty =
true;
2261 d->needsSync =
true;
2262 d->useSystemClip = !systemClip().isEmpty();
2263 d->currentBrush = QBrush();
2265 d->dirtyStencilRegion = QRect(0, 0, d->width, d->height);
2266 d->stencilClean =
true;
2268 d->shaderManager =
new QOpenGLEngineShaderManager(d->ctx);
2270 d->funcs.glDisable(GL_STENCIL_TEST);
2271 d->funcs.glDisable(GL_DEPTH_TEST);
2272 d->funcs.glDisable(GL_SCISSOR_TEST);
2274 d->glyphCacheFormat = QFontEngine::Format_A8;
2276#if !QT_CONFIG(opengles2)
2277 if (!QOpenGLContext::currentContext()->isOpenGLES()) {
2278 d->funcs.glDisable(GL_MULTISAMPLE);
2279 d->glyphCacheFormat = QFontEngine::Format_A32;
2280 d->multisamplingAlwaysEnabled =
false;
2286 d->multisamplingAlwaysEnabled = d->device->context()->format().samples() > 1;
2444 const bool singlePass = !path.hasWindingFill()
2445 && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
2446 || q->state()->needsClipBufferClear);
2447 const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
2449 if (q->state()->needsClipBufferClear)
2452 if (path.isEmpty()) {
2453 funcs.glEnable(GL_STENCIL_TEST);
2458 if (q->state()->clipTestEnabled)
2461 funcs.glStencilFunc(GL_ALWAYS, 0, 0xff);
2463 vertexCoordinateArray.clear();
2464 vertexCoordinateArray.addPath(path, inverseScale,
false);
2467 fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
2469 funcs.glColorMask(
false,
false,
false,
false);
2470 funcs.glEnable(GL_STENCIL_TEST);
2478 funcs.glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
2479 funcs.glStencilMask(value ^ referenceClipValue);
2481 drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
2483 funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
2484 funcs.glStencilMask(0xff);
2486 if (!q->state()->clipTestEnabled && path.hasWindingFill()) {
2489 composite(vertexCoordinateArray.boundingRect());
2495 composite(vertexCoordinateArray.boundingRect());
2499 funcs.glStencilMask(0);
2501 funcs.glColorMask(
true,
true,
true,
true);