summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/opengl/qopenglpaintengine.cpp12
-rw-r--r--src/gui/opengl/qopenglpaintengine_p.h2
-rw-r--r--src/gui/opengl/qopengltexturecache.cpp130
-rw-r--r--src/gui/opengl/qopengltexturecache_p.h18
-rw-r--r--tests/auto/gui/qopengl/tst_qopengl.cpp20
5 files changed, 154 insertions, 28 deletions
diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp
index 81a0d82c99..de5b6be492 100644
--- a/src/gui/opengl/qopenglpaintengine.cpp
+++ b/src/gui/opengl/qopenglpaintengine.cpp
@@ -154,8 +154,8 @@ void QOpenGL2PaintEngineExPrivate::setBrush(const QBrush& brush)
Q_ASSERT(newStyle != Qt::NoBrush);
currentBrush = brush;
- if (!currentBrushPixmap.isNull())
- currentBrushPixmap = QPixmap();
+ if (!currentBrushImage.isNull())
+ currentBrushImage = QImage();
brushUniformsDirty = true; // All brushes have at least one uniform
if (newStyle > Qt::SolidPattern)
@@ -214,11 +214,11 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture()
updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform);
}
else if (style == Qt::TexturePattern) {
- currentBrushPixmap = currentBrush.texture();
+ currentBrushImage = currentBrush.textureImage();
int max_texture_size = ctx->d_func()->maxTextureSize();
- if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size)
- currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
+ if (currentBrushImage.width() > max_texture_size || currentBrushImage.height() > max_texture_size)
+ currentBrushImage = currentBrushImage.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio);
GLuint wrapMode = GL_REPEAT;
if (QOpenGLContext::currentContext()->isOpenGLES()) {
@@ -229,7 +229,7 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture()
}
funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
- QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, currentBrushPixmap);
+ QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, currentBrushImage);
updateTextureFilter(GL_TEXTURE_2D, wrapMode, q->state()->renderHints & QPainter::SmoothPixmapTransform);
textureInvertedY = false;
}
diff --git a/src/gui/opengl/qopenglpaintengine_p.h b/src/gui/opengl/qopenglpaintengine_p.h
index 4f0e2e52a4..89eb7e87b2 100644
--- a/src/gui/opengl/qopenglpaintengine_p.h
+++ b/src/gui/opengl/qopenglpaintengine_p.h
@@ -288,7 +288,7 @@ public:
QBrush currentBrush; // May not be the state's brush!
const QBrush noBrush;
- QPixmap currentBrushPixmap;
+ QImage currentBrushImage;
QOpenGL2PEXVertexArray vertexCoordinateArray;
QOpenGL2PEXVertexArray textureCoordinateArray;
diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp
index d8b44016b9..4a4a36d7e5 100644
--- a/src/gui/opengl/qopengltexturecache.cpp
+++ b/src/gui/opengl/qopengltexturecache.cpp
@@ -43,11 +43,24 @@
#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_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
+
class QOpenGLTextureCacheWrapper
{
public:
@@ -107,7 +120,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;
@@ -117,20 +130,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;
}
-GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image)
+GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options)
{
if (image.isNull())
return 0;
@@ -140,7 +153,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();
}
@@ -158,26 +171,119 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i
}
}
- 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_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;
}
@@ -203,7 +309,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);
}
diff --git a/src/gui/opengl/qopengltexturecache_p.h b/src/gui/opengl/qopengltexturecache_p.h
index 2e82d5f373..d81115fefc 100644
--- a/src/gui/opengl/qopengltexturecache_p.h
+++ b/src/gui/opengl/qopengltexturecache_p.h
@@ -64,13 +64,15 @@ QT_BEGIN_NAMESPACE
class QOpenGLCachedTexture
{
public:
- QOpenGLCachedTexture(GLuint id, QOpenGLContext *context);
+ QOpenGLCachedTexture(GLuint id, int options, QOpenGLContext *context);
~QOpenGLCachedTexture() { m_resource->free(); }
GLuint id() const { return m_resource->id(); }
+ int options() const { return m_options; }
private:
QOpenGLSharedResourceGuard *m_resource;
+ int m_options;
};
class Q_GUI_EXPORT QOpenGLTextureCache : public QOpenGLSharedResource
@@ -81,8 +83,14 @@ public:
QOpenGLTextureCache(QOpenGLContext *);
~QOpenGLTextureCache();
- GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap);
- GLuint bindTexture(QOpenGLContext *context, const QImage &image);
+ enum BindOption {
+ NoBindOption = 0x0000,
+ PremultipliedAlphaBindOption = 0x0001,
+ };
+ 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);
void invalidate(qint64 key);
@@ -90,12 +98,14 @@ public:
void freeResource(QOpenGLContext *ctx);
private:
- GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image);
+ GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureCache::BindOptions options);
QMutex m_mutex;
QCache<quint64, QOpenGLCachedTexture> m_cache;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureCache::BindOptions)
+
QT_END_NAMESPACE
#endif
diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp
index 7de1989ada..1e8727ca04 100644
--- a/tests/auto/gui/qopengl/tst_qopengl.cpp
+++ b/tests/auto/gui/qopengl/tst_qopengl.cpp
@@ -65,6 +65,8 @@
#include <QtPlatformHeaders/QGLXNativeContext>
#endif
+Q_DECLARE_METATYPE(QImage::Format)
+
class tst_QOpenGL : public QObject
{
Q_OBJECT
@@ -605,7 +607,14 @@ void tst_QOpenGL::fboHandleNulledAfterContextDestroyed()
void tst_QOpenGL::openGLPaintDevice_data()
{
- common_data();
+ QTest::addColumn<int>("surfaceClass");
+ QTest::addColumn<QImage::Format>("imageFormat");
+
+ QTest::newRow("Using QWindow - RGB32") << int(QSurface::Window) << QImage::Format_RGB32;
+ QTest::newRow("Using QOffscreenSurface - RGB32") << int(QSurface::Offscreen) << QImage::Format_RGB32;
+ QTest::newRow("Using QOffscreenSurface - RGBx8888") << int(QSurface::Offscreen) << QImage::Format_RGBX8888;
+ QTest::newRow("Using QOffscreenSurface - RGB888") << int(QSurface::Offscreen) << QImage::Format_RGB888;
+ QTest::newRow("Using QOffscreenSurface - RGB16") << int(QSurface::Offscreen) << QImage::Format_RGB16;
}
void tst_QOpenGL::openGLPaintDevice()
@@ -615,6 +624,7 @@ void tst_QOpenGL::openGLPaintDevice()
#endif
QFETCH(int, surfaceClass);
+ QFETCH(QImage::Format, imageFormat);
QScopedPointer<QSurface> surface(createSurface(surfaceClass));
QOpenGLContext ctx;
@@ -627,7 +637,7 @@ void tst_QOpenGL::openGLPaintDevice()
const QSize size(128, 128);
- QImage image(size, QImage::Format_RGB32);
+ QImage image(size, imageFormat);
QPainter p(&image);
p.fillRect(0, 0, image.width() / 2, image.height() / 2, Qt::red);
p.fillRect(image.width() / 2, 0, image.width() / 2, image.height() / 2, Qt::green);
@@ -646,7 +656,7 @@ void tst_QOpenGL::openGLPaintDevice()
p.fillRect(0, image.height() / 2, image.width() / 2, image.height() / 2, Qt::white);
p.end();
- QImage actual = fbo.toImage().convertToFormat(QImage::Format_RGB32);
+ QImage actual = fbo.toImage().convertToFormat(imageFormat);
QCOMPARE(image.size(), actual.size());
QCOMPARE(image, actual);
@@ -655,7 +665,7 @@ void tst_QOpenGL::openGLPaintDevice()
p.drawImage(0, 0, image);
p.end();
- actual = fbo.toImage().convertToFormat(QImage::Format_RGB32);
+ actual = fbo.toImage().convertToFormat(imageFormat);
QCOMPARE(image.size(), actual.size());
QCOMPARE(image, actual);
@@ -664,7 +674,7 @@ void tst_QOpenGL::openGLPaintDevice()
p.fillRect(0, 0, image.width(), image.height(), QBrush(image));
p.end();
- actual = fbo.toImage().convertToFormat(QImage::Format_RGB32);
+ actual = fbo.toImage().convertToFormat(imageFormat);
QCOMPARE(image.size(), actual.size());
QCOMPARE(image, actual);
}