Move texture uploading out of QOpenGLTextureCache

This way the logic can be reused elsewhere.

At the same time a standard OpenGL ES/3 way of handling BGRA is added,
so we don't depend on extensions, and handling of NPOT and max size
which QSGTexture will need.

Change-Id: I475bc7127f44be3964fdb482c9e86a20db1fbca5
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Allan Sandfeld Jensen 2018-07-18 14:49:15 +02:00
parent 318b15db15
commit ede3791df8
8 changed files with 432 additions and 211 deletions

View File

@ -32,6 +32,7 @@ qtConfig(opengl) {
opengl/qopengltexture.h \
opengl/qopengltexture_p.h \
opengl/qopengltexturehelper_p.h \
opengl/qopengltextureuploader_p.h \
opengl/qopenglpixeltransferoptions.h \
opengl/qopenglextrafunctions.h \
opengl/qopenglprogrambinarycache_p.h
@ -56,6 +57,7 @@ qtConfig(opengl) {
opengl/qopengltextureblitter.cpp \
opengl/qopengltexture.cpp \
opengl/qopengltexturehelper.cpp \
opengl/qopengltextureuploader.cpp \
opengl/qopenglpixeltransferoptions.cpp \
opengl/qopenglprogrambinarycache.cpp

View File

@ -90,7 +90,8 @@ public:
MapBufferRange = 0x00100000,
Sized8Formats = 0x00200000,
DiscardFramebuffer = 0x00400000,
Sized16Formats = 0x00800000
Sized16Formats = 0x00800000,
TextureSwizzle = 0x01000000,
};
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)

View File

@ -45,6 +45,7 @@
#include <QtGui/private/qopengl_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
#ifdef Q_OS_INTEGRITY
#include <EGL/egl.h>
@ -424,6 +425,8 @@ static int qt_gl_resolve_extensions()
extensions |= QOpenGLExtensions::NVFloatBuffer;
if (extensionMatcher.match("GL_ARB_pixel_buffer_object"))
extensions |= QOpenGLExtensions::PixelBufferObject;
if (extensionMatcher.match("GL_ARB_texture_swizzle") || extensionMatcher.match("GL_EXT_texture_swizzle"))
extensions |= QOpenGLExtensions::TextureSwizzle;
if (ctx->isOpenGLES()) {
if (format.majorVersion() >= 2)
@ -436,7 +439,8 @@ static int qt_gl_resolve_extensions()
| QOpenGLExtensions::MapBufferRange
| QOpenGLExtensions::FramebufferBlit
| QOpenGLExtensions::FramebufferMultisample
| QOpenGLExtensions::Sized8Formats;
| QOpenGLExtensions::Sized8Formats
| QOpenGLExtensions::TextureSwizzle;
} else {
// Recognize features by extension name.
if (extensionMatcher.match("GL_OES_packed_depth_stencil"))
@ -462,6 +466,17 @@ static int qt_gl_resolve_extensions()
// We don't match GL_APPLE_texture_format_BGRA8888 here because it has different semantics.
if (extensionMatcher.match("GL_IMG_texture_format_BGRA8888") || extensionMatcher.match("GL_EXT_texture_format_BGRA8888"))
extensions |= QOpenGLExtensions::BGRATextureFormat;
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
QString *deviceName =
static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName"));
static bool wrongfullyReportsBgra8888Support = deviceName != 0
&& (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0
|| deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0
|| deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0);
if (wrongfullyReportsBgra8888Support)
extensions &= ~QOpenGLExtensions::BGRATextureFormat;
#endif
if (extensionMatcher.match("GL_EXT_discard_framebuffer"))
extensions |= QOpenGLExtensions::DiscardFramebuffer;
if (extensionMatcher.match("GL_EXT_texture_norm16"))
@ -495,6 +510,9 @@ static int qt_gl_resolve_extensions()
if (format.version() >= qMakePair(3, 2) || extensionMatcher.match("GL_ARB_geometry_shader4"))
extensions |= QOpenGLExtensions::GeometryShaders;
if (format.version() >= qMakePair(3, 3))
extensions |= QOpenGLExtensions::TextureSwizzle;
if (extensionMatcher.match("GL_ARB_map_buffer_range"))
extensions |= QOpenGLExtensions::MapBufferRange;

View File

@ -263,7 +263,7 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QGradient &gradient)
struct ImageWithBindOptions
{
const QImage &image;
QOpenGLTextureCache::BindOptions options;
QOpenGLTextureUploader::BindOptions options;
};
template<>
@ -1554,7 +1554,7 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c
ensureActive();
d->transferMode(ImageDrawingMode);
QOpenGLTextureCache::BindOptions bindOption = QOpenGLTextureCache::PremultipliedAlphaBindOption;
QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
// Use specialized bind for formats we have specialized shaders for.
switch (image.format()) {
case QImage::Format_RGBA8888:
@ -1565,14 +1565,14 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c
case QImage::Format_Alpha8:
if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc);
bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption;
bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption;
} else
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
break;
case QImage::Format_Grayscale8:
if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) {
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc);
bindOption = QOpenGLTextureCache::UseRedFor8BitBindOption;
bindOption = QOpenGLTextureUploader::UseRedFor8BitBindOption;
} else
d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc);
break;

View File

@ -38,39 +38,14 @@
****************************************************************************/
#include "qopengltexturecache_p.h"
#include "qopengltextureuploader_p.h"
#include <qmath.h>
#include <qopenglfunctions.h>
#include <private/qopenglcontext_p.h>
#include <private/qopenglextensions_p.h>
#include <private/qimagepixmapcleanuphooks_p.h>
#include <qpa/qplatformpixmap.h>
QT_BEGIN_NAMESPACE
#ifndef GL_RED
#define GL_RED 0x1903
#endif
#ifndef GL_RGB10_A2
#define GL_RGB10_A2 0x8059
#endif
#ifndef GL_BGR
#define GL_BGR 0x80E0
#endif
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#endif
#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
#endif
class QOpenGLTextureCacheWrapper
{
public:
@ -130,7 +105,7 @@ QOpenGLTextureCache::~QOpenGLTextureCache()
{
}
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, BindOptions options)
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureUploader::BindOptions options)
{
if (pixmap.isNull())
return 0;
@ -153,7 +128,7 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &
return id;
}
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options)
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureUploader::BindOptions options)
{
if (image.isNull())
return 0;
@ -170,16 +145,8 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i
}
QImage img = image;
if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
// Scale the pixmap if needed. GL textures needs to have the
// dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
// 2.0 or use the GL_TEXTURE_RECTANGLE texture target
int tx_w = qNextPowerOfTwo(image.width() - 1);
int tx_h = qNextPowerOfTwo(image.height() - 1);
if (tx_w != image.width() || tx_h != image.height()) {
img = img.scaled(tx_w, tx_h);
}
}
if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures))
options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
GLuint id = bindTexture(context, key, img, options);
if (id > 0)
@ -188,164 +155,16 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i
return id;
}
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, BindOptions options)
GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options)
{
GLuint id;
QOpenGLFunctions *funcs = context->functions();
funcs->glGenTextures(1, &id);
funcs->glBindTexture(GL_TEXTURE_2D, id);
QImage tx;
GLenum externalFormat;
GLenum internalFormat;
GLuint pixelType;
QImage::Format targetFormat = QImage::Format_Invalid;
const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
int cost = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options);
switch (image.format()) {
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_Premultiplied:
if (isOpenGL12orBetter) {
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_INT_8_8_8_8_REV;
} else {
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
// Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian.
break;
#endif
if (static_cast<QOpenGLExtensions*>(context->functions())->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat)) {
// GL_EXT_bgra or GL_EXT_texture_format_BGRA8888 extensions.
if (context->isOpenGLES()) {
// The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
externalFormat = internalFormat = GL_BGRA;
} else {
// OpenGL BGRA/BGR format is not allowed as an internal format
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
}
pixelType = GL_UNSIGNED_BYTE;
} else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
// Is only allowed as an external format like OpenGL.
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
} else {
// No support for direct ARGB32 upload.
break;
}
}
targetFormat = image.format();
break;
case QImage::Format_BGR30:
case QImage::Format_A2BGR30_Premultiplied:
if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_RGBA;
internalFormat = GL_RGB10_A2;
targetFormat = image.format();
}
break;
case QImage::Format_RGB30:
case QImage::Format_A2RGB30_Premultiplied:
if (isOpenGL12orBetter) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_BGRA;
internalFormat = GL_RGB10_A2;
targetFormat = image.format();
} else if (context->isOpenGLES() && context->format().majorVersion() >= 3) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_RGBA;
internalFormat = GL_RGB10_A2;
targetFormat = QImage::Format_A2BGR30_Premultiplied;
}
break;
case QImage::Format_RGB444:
case QImage::Format_RGB555:
case QImage::Format_RGB16:
if (isOpenGL12orBetter || context->isOpenGLES()) {
externalFormat = internalFormat = GL_RGB;
pixelType = GL_UNSIGNED_SHORT_5_6_5;
targetFormat = QImage::Format_RGB16;
}
break;
case QImage::Format_RGB666:
case QImage::Format_RGB888:
externalFormat = internalFormat = GL_RGB;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = QImage::Format_RGB888;
break;
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
case QImage::Format_RGBA8888_Premultiplied:
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
break;
case QImage::Format_Indexed8:
if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
case QImage::Format_Alpha8:
if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
externalFormat = internalFormat = GL_ALPHA;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
case QImage::Format_Grayscale8:
if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
externalFormat = internalFormat = GL_LUMINANCE;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
default:
break;
}
if (targetFormat == QImage::Format_Invalid) {
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
if (!image.hasAlphaChannel())
targetFormat = QImage::Format_RGBX8888;
else
targetFormat = QImage::Format_RGBA8888;
}
if (options & PremultipliedAlphaBindOption) {
if (targetFormat == QImage::Format_ARGB32)
targetFormat = QImage::Format_ARGB32_Premultiplied;
else if (targetFormat == QImage::Format_RGBA8888)
targetFormat = QImage::Format_RGBA8888_Premultiplied;
} else {
if (targetFormat == QImage::Format_ARGB32_Premultiplied)
targetFormat = QImage::Format_ARGB32;
else if (targetFormat == QImage::Format_RGBA8888_Premultiplied)
targetFormat = QImage::Format_RGBA8888;
}
if (image.format() != targetFormat)
tx = image.convertToFormat(targetFormat);
else
tx = image;
funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, const_cast<const QImage &>(tx).bits());
int cost = tx.width() * tx.height() * tx.depth() / (1024 * 8);
m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost);
m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost / 1024);
return id;
}
@ -371,7 +190,7 @@ static void freeTexture(QOpenGLFunctions *funcs, GLuint id)
funcs->glDeleteTextures(1, &id);
}
QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context) : m_options(options)
QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context) : m_options(options)
{
m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture);
}

View File

@ -56,6 +56,7 @@
#include <QObject>
#include <QCache>
#include <private/qopenglcontext_p.h>
#include <private/qopengltextureuploader_p.h>
#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
@ -70,15 +71,10 @@ public:
QOpenGLTextureCache(QOpenGLContext *);
~QOpenGLTextureCache();
enum BindOption {
NoBindOption = 0x0000,
PremultipliedAlphaBindOption = 0x0001,
UseRedFor8BitBindOption = 0x0002,
};
Q_DECLARE_FLAGS(BindOptions, BindOption)
GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption);
GLuint bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption);
GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap,
QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption);
GLuint bindTexture(QOpenGLContext *context, const QImage &image,
QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption);
void invalidate(qint64 key);
@ -86,26 +82,24 @@ public:
void freeResource(QOpenGLContext *ctx) override;
private:
GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureCache::BindOptions options);
GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options);
QMutex m_mutex;
QCache<quint64, QOpenGLCachedTexture> m_cache;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureCache::BindOptions)
class QOpenGLCachedTexture
{
public:
QOpenGLCachedTexture(GLuint id, QOpenGLTextureCache::BindOptions options, QOpenGLContext *context);
QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context);
~QOpenGLCachedTexture() { m_resource->free(); }
GLuint id() const { return m_resource->id(); }
QOpenGLTextureCache::BindOptions options() const { return m_options; }
QOpenGLTextureUploader::BindOptions options() const { return m_options; }
private:
QOpenGLSharedResourceGuard *m_resource;
QOpenGLTextureCache::BindOptions m_options;
QOpenGLTextureUploader::BindOptions m_options;
};
QT_END_NAMESPACE

View File

@ -0,0 +1,303 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qopengltextureuploader_p.h"
#include <qimage.h>
#include <qmath.h>
#include <qopenglfunctions.h>
#include <private/qopenglcontext_p.h>
#include <private/qopenglextensions_p.h>
#ifndef GL_RED
#define GL_RED 0x1903
#endif
#ifndef GL_GREEN
#define GL_GREEN 0x1904
#endif
#ifndef GL_BLUE
#define GL_BLUE 0x1905
#endif
#ifndef GL_RGB10_A2
#define GL_RGB10_A2 0x8059
#endif
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#endif
#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
#endif
#ifndef GL_TEXTURE_SWIZZLE_RGBA
#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
#endif
#ifndef GL_SRGB
#define GL_SRGB 0x8C40
#endif
#ifndef GL_SRGB_ALPHA
#define GL_SRGB_ALPHA 0x8C42
#endif
QT_BEGIN_NAMESPACE
qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &image, QOpenGLTextureUploader::BindOptions options, QSize maxSize)
{
QOpenGLContext *context = QOpenGLContext::currentContext();
QOpenGLExtensions *funcs = static_cast<QOpenGLExtensions*>(context->functions());
QImage tx;
GLenum externalFormat;
GLenum internalFormat;
GLuint pixelType;
QImage::Format targetFormat = QImage::Format_Invalid;
const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2);
const bool isOpenGLES3orBetter = context->isOpenGLES() && context->format().majorVersion() >= 3;
const bool sRgbBinding = (options & SRgbBindOption);
Q_ASSERT(isOpenGL12orBetter || context->isOpenGLES());
Q_ASSERT((options & (SRgbBindOption | UseRedFor8BitBindOption)) != (SRgbBindOption | UseRedFor8BitBindOption));
switch (image.format()) {
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_Premultiplied:
if (isOpenGL12orBetter) {
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_INT_8_8_8_8_REV;
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle) && false) {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA };
funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
#else
GLint swizzle[4] = { GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED };
funcs->glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
#endif
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
} else {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
// Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian.
if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) {
// The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
externalFormat = internalFormat = GL_BGRA;
pixelType = GL_UNSIGNED_BYTE;
} else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
// Is only allowed as an external format like OpenGL.
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
} else {
// No support for direct ARGB32 upload.
break;
}
}
targetFormat = image.format();
#endif
break;
case QImage::Format_BGR30:
case QImage::Format_A2BGR30_Premultiplied:
if (sRgbBinding) {
// Always needs conversion
break;
} else if (isOpenGL12orBetter || isOpenGLES3orBetter) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_RGBA;
internalFormat = GL_RGB10_A2;
targetFormat = image.format();
}
break;
case QImage::Format_RGB30:
case QImage::Format_A2RGB30_Premultiplied:
if (sRgbBinding) {
// Always needs conversion
break;
} else if (isOpenGL12orBetter) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_BGRA;
internalFormat = GL_RGB10_A2;
targetFormat = image.format();
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle) && (isOpenGL12orBetter || isOpenGLES3orBetter)) {
pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
externalFormat = GL_RGBA;
internalFormat = GL_RGB10_A2;
GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA };
funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
targetFormat = image.format();
}
break;
case QImage::Format_RGB444:
case QImage::Format_RGB555:
case QImage::Format_RGB16:
if (isOpenGL12orBetter || context->isOpenGLES()) {
externalFormat = internalFormat = GL_RGB;
pixelType = GL_UNSIGNED_SHORT_5_6_5;
targetFormat = QImage::Format_RGB16;
}
break;
case QImage::Format_RGB666:
case QImage::Format_RGB888:
externalFormat = internalFormat = GL_RGB;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = QImage::Format_RGB888;
break;
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
case QImage::Format_RGBA8888_Premultiplied:
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
break;
case QImage::Format_Indexed8:
if (sRgbBinding) {
// Always needs conversion
break;
} else if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
case QImage::Format_Alpha8:
if (sRgbBinding) {
// Always needs conversion
break;
} else if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
externalFormat = internalFormat = GL_ALPHA;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
GLint swizzle[4] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED };
funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
case QImage::Format_Grayscale8:
if (sRgbBinding) {
// Always needs conversion
break;
} else if (options & UseRedFor8BitBindOption) {
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) {
externalFormat = internalFormat = GL_LUMINANCE;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
GLint swizzle[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
funcs->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
externalFormat = internalFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
targetFormat = image.format();
}
break;
default:
break;
}
// If no direct upload was detected above, convert to RGBA8888 and upload that
if (targetFormat == QImage::Format_Invalid) {
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
if (!image.hasAlphaChannel())
targetFormat = QImage::Format_RGBX8888;
else
targetFormat = QImage::Format_RGBA8888;
}
if (options & PremultipliedAlphaBindOption) {
if (targetFormat == QImage::Format_ARGB32)
targetFormat = QImage::Format_ARGB32_Premultiplied;
else if (targetFormat == QImage::Format_RGBA8888)
targetFormat = QImage::Format_RGBA8888_Premultiplied;
} else {
if (targetFormat == QImage::Format_ARGB32_Premultiplied)
targetFormat = QImage::Format_ARGB32;
else if (targetFormat == QImage::Format_RGBA8888_Premultiplied)
targetFormat = QImage::Format_RGBA8888;
}
if (sRgbBinding) {
Q_ASSERT(internalFormat == GL_RGBA || internalFormat == GL_RGB);
if (image.hasAlphaChannel())
internalFormat = GL_SRGB_ALPHA;
else
internalFormat = GL_SRGB;
}
if (image.format() != targetFormat)
tx = image.convertToFormat(targetFormat);
else
tx = image;
QSize newSize = tx.size();
if (!maxSize.isEmpty())
newSize = newSize.boundedTo(maxSize);
if (options & PowerOfTwoBindOption) {
newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1));
newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1));
}
if (newSize != tx.size())
tx = tx.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
funcs->glTexImage2D(target, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, tx.constBits());
qsizetype cost = qint64(tx.width()) * tx.height() * tx.depth() / 8;
return cost;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#ifndef QOPENGLTEXTUREUPLOADER_P_H
#define QOPENGLTEXTUREUPLOADER_P_H
#include <QtCore/qsize.h>
#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/private/qopenglcontext_p.h>
QT_BEGIN_NAMESPACE
class QImage;
class Q_GUI_EXPORT QOpenGLTextureUploader
{
public:
enum BindOption {
NoBindOption = 0x0000,
PremultipliedAlphaBindOption = 0x0001,
UseRedFor8BitBindOption = 0x0002,
SRgbBindOption = 0x0004,
PowerOfTwoBindOption = 0x0008
};
Q_DECLARE_FLAGS(BindOptions, BindOption)
Q_FLAGS(BindOptions)
static qsizetype textureImage(GLenum target, const QImage &image, BindOptions options, QSize maxSize = QSize());
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureUploader::BindOptions)
QT_END_NAMESPACE
#endif