diff options
Diffstat (limited to 'src/gui/opengl/qopengltexturecache.cpp')
-rw-r--r-- | src/gui/opengl/qopengltexturecache.cpp | 180 |
1 files changed, 152 insertions, 28 deletions
diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp index f4aa29ac0f..055974d5a4 100644 --- a/src/gui/opengl/qopengltexturecache.cpp +++ b/src/gui/opengl/qopengltexturecache.cpp @@ -40,13 +40,35 @@ ****************************************************************************/ #include "qopengltexturecache_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_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: @@ -106,7 +128,7 @@ QOpenGLTextureCache::~QOpenGLTextureCache() { } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, BindOptions options) { if (pixmap.isNull()) return 0; @@ -116,34 +138,20 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap & // A QPainter is active on the image - take the safe route and replace the texture. if (!pixmap.paintingActive()) { QOpenGLCachedTexture *entry = m_cache.object(key); - if (entry) { + if (entry && entry->options() == options) { context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); return entry->id(); } } - GLuint id = bindTexture(context, key, pixmap.toImage()); + GLuint id = bindTexture(context, key, pixmap.toImage(), options); if (id > 0) QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); return id; } -// returns the highest number closest to v, which is a power of 2 -// NB! assumes 32 bit ints -static int qt_next_power_of_two(int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - return v; -} - -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options) { if (image.isNull()) return 0; @@ -153,7 +161,7 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i // A QPainter is active on the image - take the safe route and replace the texture. if (!image.paintingActive()) { QOpenGLCachedTexture *entry = m_cache.object(key); - if (entry) { + if (entry && entry->options() == options) { context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); return entry->id(); } @@ -164,33 +172,149 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i // 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 = qt_next_power_of_two(image.width()); - int tx_h = qt_next_power_of_two(image.height()); + 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); } } - GLuint id = bindTexture(context, key, img); + GLuint id = bindTexture(context, key, img, options); if (id > 0) QImagePixmapCleanupHooks::enableCleanupHooks(image); return id; } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, BindOptions options) { GLuint id; QOpenGLFunctions *funcs = context->functions(); funcs->glGenTextures(1, &id); funcs->glBindTexture(GL_TEXTURE_2D, id); - QImage tx = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + 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; + 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, GL_RGBA, tx.width(), tx.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, const_cast<const QImage &>(tx).bits()); + 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() * 4 / 1024; - m_cache.insert(key, new QOpenGLCachedTexture(id, context), cost); + int cost = tx.width() * tx.height() * tx.depth() / (1024 * 8); + m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost); return id; } @@ -216,7 +340,7 @@ static void freeTexture(QOpenGLFunctions *funcs, GLuint id) funcs->glDeleteTextures(1, &id); } -QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLContext *context) +QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, int options, QOpenGLContext *context) : m_options(options) { m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture); } |