Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include <QtGui/qpa/qplatformgraphicsbuffer.h>
++
++ #include "qplatformgraphicsbufferhelper.h"
++ #include <QtCore/QDebug>
++ #include <QtGui/qopengl.h>
++ #include <QtGui/QImage>
++ #include <QtGui/QOpenGLContext>
++ #include <QtGui/QOpenGLFunctions>
++
++ #ifndef GL_UNPACK_ROW_LENGTH
++ #define GL_UNPACK_ROW_LENGTH 0x0CF2
++ #endif
++
++ #ifndef GL_RGB10_A2
++ #define GL_RGB10_A2 0x8059
++ #endif
++
++ #ifndef GL_UNSIGNED_INT_2_10_10_10_REV
++ #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \namespace QPlatformGraphicsBufferHelper
++ \inmodule QtGui
++ \internal
++ */
++
++ /*!
++ Convenience function to both lock and bind the \a graphicsBuffer to a texture.
++ This function will first try to lock with texture read and texture write
++ access. If this succeeds it will use the bindToTexture function to bind the
++ content to the currently bound texture, and if \a premultiplied is provided,
++ it is set to false.
++
++ If it fails, it will try to lock with SWReadAccess and then use the
++ bindSWToTexture convenience function. If \a premultiplied is provided, it is
++ passed to the bindSWToTexture() function.
++
++ \a swizzle is meant to be used by the caller to figure out if the Red and
++ Blue color channels need to be swizzled when rendering.
++
++ \a rect is the subrect which is desired to be bounded to the texture. This
++ argument has a not less than semantic, meaning more (if not all) of the buffer
++ can be bounded to the texture. An empty QRect is interpreted as entire buffer
++ should be bound.
++
++ The user should use the AccessTypes returned by isLocked to figure out what
++ lock has been obtained.
++
++ Returns true if the buffer has successfully been bound to the currently
++ bound texture, otherwise returns false.
++ */
++ bool QPlatformGraphicsBufferHelper::lockAndBindToTexture(QPlatformGraphicsBuffer *graphicsBuffer,
++ bool *swizzle, bool *premultiplied,
++ const QRect &rect)
++ {
++ if (graphicsBuffer->lock(QPlatformGraphicsBuffer::TextureAccess)) {
++ if (!graphicsBuffer->bindToTexture(rect)) {
++ qWarning("Failed to bind %sgraphicsbuffer to texture", "");
++ return false;
++ }
++ if (swizzle)
++ *swizzle = false;
++ if (premultiplied)
++ *premultiplied = false;
++ } else if (graphicsBuffer->lock(QPlatformGraphicsBuffer::SWReadAccess)) {
++ if (!bindSWToTexture(graphicsBuffer, swizzle, premultiplied, rect)) {
++ qWarning("Failed to bind %sgraphicsbuffer to texture", "SW ");
++ return false;
++ }
++ } else {
++ qWarning("Failed to lock");
++ return false;
++ }
++ return true;
++ }
++
++ /*!
++ Convenience function that uploads the current raster content to the currently
++ bound texture.
++
++ \a swizzleRandB is meant to be used by the caller to decide if the Red and
++ Blue color channels need to be swizzled when rendering. This is an
++ optimization. Qt often renders to software buffers interpreting pixels as
++ unsigned ints. When these buffers are uploaded to textures and each color
++ channel per pixel is interpreted as a byte (read sequentially), then the
++ Red and Blue channels are swapped. Conveniently, the Alpha buffer will be
++ correct, since Qt historically has had the alpha channel as the first
++ channel, while OpenGL typically expects the alpha channel to be the last
++ channel.
++
++ \a subRect is the region to be bound to the texture. This argument has a
++ not less than semantic, meaning more (if not all) of the buffer can be
++ bound to the texture. An empty QRect is interpreted as meaning the entire
++ buffer should be bound.
++
++ This function fails if the \a graphicsBuffer is not locked to SWAccess.
++
++ Returns true on success, otherwise false. If \a premultipliedB is
++ provided, it is set according to what happens, if the function returns
++ true.
++ */
++ bool QPlatformGraphicsBufferHelper::bindSWToTexture(const QPlatformGraphicsBuffer *graphicsBuffer,
++ bool *swizzleRandB, bool *premultipliedB,
++ const QRect &subRect)
++ {
++ #ifndef QT_NO_OPENGL
++ QOpenGLContext *ctx = QOpenGLContext::currentContext();
++ if (!ctx)
++ return false;
++
++ if (!(graphicsBuffer->isLocked() & QPlatformGraphicsBuffer::SWReadAccess))
++ return false;
++
++ QSize size = graphicsBuffer->size();
++
++ Q_ASSERT(subRect.isEmpty() || QRect(QPoint(0,0), size).contains(subRect));
++
++ GLenum internalFormat = GL_RGBA;
++ GLuint pixelType = GL_UNSIGNED_BYTE;
++
++ bool needsConversion = false;
++ bool swizzle = false;
++ bool premultiplied = false;
++ QImage::Format imageformat = QImage::toImageFormat(graphicsBuffer->format());
++ QImage image(graphicsBuffer->data(), size.width(), size.height(), graphicsBuffer->bytesPerLine(), imageformat);
++ switch (imageformat) {
++ case QImage::Format_ARGB32_Premultiplied:
++ premultiplied = true;
++ Q_FALLTHROUGH();
++ case QImage::Format_RGB32:
++ case QImage::Format_ARGB32:
++ swizzle = true;
++ break;
++ case QImage::Format_RGBA8888_Premultiplied:
++ premultiplied = true;
++ Q_FALLTHROUGH();
++ case QImage::Format_RGBX8888:
++ case QImage::Format_RGBA8888:
++ break;
++ case QImage::Format_BGR30:
++ case QImage::Format_A2BGR30_Premultiplied:
++ if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
++ pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
++ internalFormat = GL_RGB10_A2;
++ premultiplied = true;
++ } else {
++ needsConversion = true;
++ }
++ break;
++ case QImage::Format_RGB30:
++ case QImage::Format_A2RGB30_Premultiplied:
++ if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
++ pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
++ internalFormat = GL_RGB10_A2;
++ premultiplied = true;
++ swizzle = true;
++ } else {
++ needsConversion = true;
++ }
++ break;
++ default:
++ needsConversion = true;
++ break;
++ }
++ if (!needsConversion && image.bytesPerLine() != (size.width() * 4) && ctx->isOpenGLES() && ctx->format().majorVersion() < 3)
++ needsConversion = true;
++ if (needsConversion)
++ image.convertTo(QImage::Format_RGBA8888);
++
++ bool needsRowLength = (image.bytesPerLine() != image.width() * 4);
++ QOpenGLFunctions *funcs = ctx->functions();
++
++ QRect rect = subRect;
++ if (rect.isNull() || rect == QRect(QPoint(0,0),size)) {
++ if (needsRowLength)
++ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, GLint(image.bytesPerLine() / 4));
++ funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size.width(), size.height(), 0, GL_RGBA, pixelType, image.constBits());
++ if (needsRowLength)
++ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
++ } else {
++ if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
++ // OpenGL 2.1+ or OpenGL ES/3+
++ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, GLint(image.bytesPerLine() / 4));
++ funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
++ image.constScanLine(rect.y()) + rect.x() * 4);
++ funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
++ } else
++ {
++ // if the rect is wide enough it's cheaper to just
++ // extend it instead of doing an image copy
++ if (rect.width() >= size.width() / 2) {
++ rect.setX(0);
++ rect.setWidth(size.width());
++ }
++
++ // if the sub-rect is full-width we can pass the image data directly to
++ // OpenGL instead of copying, since there's no gap between scanlines
++
++ if (rect.width() == image.bytesPerLine() / 4) {
++ funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
++ image.constScanLine(rect.y()));
++ } else {
++ funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
++ image.copy(rect).constBits());
++ }
++ }
++ }
++ if (swizzleRandB)
++ *swizzleRandB = swizzle;
++ if (premultipliedB)
++ *premultipliedB = premultiplied;
++
++ return true;
++
++ #else
++ Q_UNUSED(graphicsBuffer);
++ Q_UNUSED(swizzleRandB);
++ Q_UNUSED(premultipliedB);
++ Q_UNUSED(subRect);
++ return false;
++ #endif // QT_NO_OPENGL
++ }
++
++ QT_END_NAMESPACE