diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-07-18 14:49:15 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-07-27 12:12:34 +0000 |
commit | ede3791df8330ed8daae6667d025ad40219a9f5f (patch) | |
tree | 0f0c8d8897799b6a7f56c381fe36701795f28f6c /src/gui/opengl | |
parent | 318b15db15b515675b0b1e7b3319c793b9f1a757 (diff) |
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>
Diffstat (limited to 'src/gui/opengl')
-rw-r--r-- | src/gui/opengl/opengl.pri | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopenglextensions_p.h | 3 | ||||
-rw-r--r-- | src/gui/opengl/qopenglfunctions.cpp | 20 | ||||
-rw-r--r-- | src/gui/opengl/qopenglpaintengine.cpp | 8 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache.cpp | 199 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache_p.h | 24 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureuploader.cpp | 303 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureuploader_p.h | 84 |
8 files changed, 432 insertions, 211 deletions
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index 4c778b184e..24758afdeb 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -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 diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h index a5f1a2cc88..af8ee8201d 100644 --- a/src/gui/opengl/qopenglextensions_p.h +++ b/src/gui/opengl/qopenglextensions_p.h @@ -90,7 +90,8 @@ public: MapBufferRange = 0x00100000, Sized8Formats = 0x00200000, DiscardFramebuffer = 0x00400000, - Sized16Formats = 0x00800000 + Sized16Formats = 0x00800000, + TextureSwizzle = 0x01000000, }; Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension) diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index 913b289c52..4f48604a88 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -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; diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index e390183893..5902713faa 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -263,7 +263,7 @@ GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QGradient &gradient) struct ImageWithBindOptions { const QImage ℑ - 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; diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp index 27aa8db33a..8de0b25fee 100644 --- a/src/gui/opengl/qopengltexturecache.cpp +++ b/src/gui/opengl/qopengltexturecache.cpp @@ -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); - - 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 = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options); - 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); } diff --git a/src/gui/opengl/qopengltexturecache_p.h b/src/gui/opengl/qopengltexturecache_p.h index b9d7df91e3..88ef06e744 100644 --- a/src/gui/opengl/qopengltexturecache_p.h +++ b/src/gui/opengl/qopengltexturecache_p.h @@ -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 diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp new file mode 100644 index 0000000000..0204d4852f --- /dev/null +++ b/src/gui/opengl/qopengltextureuploader.cpp @@ -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 diff --git a/src/gui/opengl/qopengltextureuploader_p.h b/src/gui/opengl/qopengltextureuploader_p.h new file mode 100644 index 0000000000..d758b3787b --- /dev/null +++ b/src/gui/opengl/qopengltextureuploader_p.h @@ -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 + |