File indexing completed on 2024-05-12 05:31:43

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
0006     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
0007     SPDX-FileCopyrightText: 2012 Philipp Knechtges <philipp-dev@knechtges.com>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "gltexture_p.h"
0013 #include "opengl/glframebuffer.h"
0014 #include "opengl/glplatform.h"
0015 #include "opengl/glutils.h"
0016 #include "opengl/glutils_funcs.h"
0017 #include "utils/common.h"
0018 
0019 #include <QImage>
0020 #include <QPixmap>
0021 #include <QVector2D>
0022 #include <QVector3D>
0023 #include <QVector4D>
0024 
0025 namespace KWin
0026 {
0027 
0028 //****************************************
0029 // GLTexture
0030 //****************************************
0031 
0032 bool GLTexturePrivate::s_supportsFramebufferObjects = false;
0033 bool GLTexturePrivate::s_supportsARGB32 = false;
0034 bool GLTexturePrivate::s_supportsUnpack = false;
0035 bool GLTexturePrivate::s_supportsTextureStorage = false;
0036 bool GLTexturePrivate::s_supportsTextureSwizzle = false;
0037 bool GLTexturePrivate::s_supportsTextureFormatRG = false;
0038 bool GLTexturePrivate::s_supportsTexture16Bit = false;
0039 uint GLTexturePrivate::s_fbo = 0;
0040 
0041 // Table of GL formats/types associated with different values of QImage::Format.
0042 // Zero values indicate a direct upload is not feasible.
0043 //
0044 // Note: Blending is set up to expect premultiplied data, so the non-premultiplied
0045 // Format_ARGB32 must be converted to Format_ARGB32_Premultiplied ahead of time.
0046 struct
0047 {
0048     GLenum internalFormat;
0049     GLenum format;
0050     GLenum type;
0051 } static const formatTable[] = {
0052     {0, 0, 0}, // QImage::Format_Invalid
0053     {0, 0, 0}, // QImage::Format_Mono
0054     {0, 0, 0}, // QImage::Format_MonoLSB
0055     {0, 0, 0}, // QImage::Format_Indexed8
0056     {GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // QImage::Format_RGB32
0057     {0, 0, 0}, // QImage::Format_ARGB32
0058     {GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // QImage::Format_ARGB32_Premultiplied
0059     {GL_RGB8, GL_BGR, GL_UNSIGNED_SHORT_5_6_5_REV}, // QImage::Format_RGB16
0060     {0, 0, 0}, // QImage::Format_ARGB8565_Premultiplied
0061     {0, 0, 0}, // QImage::Format_RGB666
0062     {0, 0, 0}, // QImage::Format_ARGB6666_Premultiplied
0063     {GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // QImage::Format_RGB555
0064     {0, 0, 0}, // QImage::Format_ARGB8555_Premultiplied
0065     {GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // QImage::Format_RGB888
0066     {GL_RGB4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // QImage::Format_RGB444
0067     {GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // QImage::Format_ARGB4444_Premultiplied
0068     {GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE}, // QImage::Format_RGBX8888
0069     {0, 0, 0}, // QImage::Format_RGBA8888
0070     {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // QImage::Format_RGBA8888_Premultiplied
0071     {GL_RGB10, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_BGR30
0072     {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_A2BGR30_Premultiplied
0073     {GL_RGB10, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_RGB30
0074     {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_A2RGB30_Premultiplied
0075     {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // QImage::Format_Alpha8
0076     {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // QImage::Format_Grayscale8
0077     {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // QImage::Format_RGBX64
0078     {0, 0, 0}, // QImage::Format_RGBA64
0079     {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // QImage::Format_RGBA64_Premultiplied
0080     {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // QImage::Format_Grayscale16
0081     {0, 0, 0}, // QImage::Format_BGR888
0082 };
0083 
0084 GLTexture::GLTexture(GLenum target)
0085     : d(std::make_unique<GLTexturePrivate>())
0086 {
0087     d->m_target = target;
0088 }
0089 
0090 GLTexture::GLTexture(GLenum target, GLuint textureId, GLenum internalFormat, const QSize &size, int levels, bool owning, OutputTransform transform)
0091     : GLTexture(target)
0092 {
0093     d->m_owning = owning;
0094     d->m_texture = textureId;
0095     d->m_scale.setWidth(1.0 / size.width());
0096     d->m_scale.setHeight(1.0 / size.height());
0097     d->m_size = size;
0098     d->m_canUseMipmaps = levels > 1;
0099     d->m_mipLevels = levels;
0100     d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
0101     d->m_internalFormat = internalFormat;
0102     d->m_textureToBufferTransform = transform;
0103 
0104     d->updateMatrix();
0105 }
0106 
0107 GLTexture::~GLTexture()
0108 {
0109 }
0110 
0111 bool GLTexture::create()
0112 {
0113     if (!isNull()) {
0114         return true;
0115     }
0116     glGenTextures(1, &d->m_texture);
0117     return d->m_texture != GL_NONE;
0118 }
0119 
0120 GLTexturePrivate::GLTexturePrivate()
0121     : m_texture(0)
0122     , m_target(0)
0123     , m_internalFormat(0)
0124     , m_filter(GL_NEAREST)
0125     , m_wrapMode(GL_REPEAT)
0126     , m_canUseMipmaps(false)
0127     , m_markedDirty(false)
0128     , m_filterChanged(true)
0129     , m_wrapModeChanged(false)
0130     , m_owning(true)
0131     , m_mipLevels(1)
0132     , m_unnormalizeActive(0)
0133     , m_normalizeActive(0)
0134 {
0135 }
0136 
0137 GLTexturePrivate::~GLTexturePrivate()
0138 {
0139     if (m_texture != 0 && m_owning) {
0140         glDeleteTextures(1, &m_texture);
0141     }
0142 }
0143 
0144 void GLTexturePrivate::initStatic()
0145 {
0146     if (!GLPlatform::instance()->isGLES()) {
0147         s_supportsFramebufferObjects = hasGLVersion(3, 0) || hasGLExtension("GL_ARB_framebuffer_object") || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object"));
0148         s_supportsTextureStorage = hasGLVersion(4, 2) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_storage"));
0149         s_supportsTextureSwizzle = hasGLVersion(3, 3) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_swizzle"));
0150         // see https://www.opengl.org/registry/specs/ARB/texture_rg.txt
0151         s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_rg"));
0152         s_supportsTexture16Bit = true;
0153         s_supportsARGB32 = true;
0154         s_supportsUnpack = true;
0155     } else {
0156         s_supportsFramebufferObjects = true;
0157         s_supportsTextureStorage = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_storage"));
0158         s_supportsTextureSwizzle = hasGLVersion(3, 0);
0159         // see https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
0160         s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_rg"));
0161         s_supportsTexture16Bit = hasGLExtension(QByteArrayLiteral("GL_EXT_texture_norm16"));
0162 
0163         // QImage::Format_ARGB32_Premultiplied is a packed-pixel format, so it's only
0164         // equivalent to GL_BGRA/GL_UNSIGNED_BYTE on little-endian systems.
0165         s_supportsARGB32 = QSysInfo::ByteOrder == QSysInfo::LittleEndian && hasGLExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"));
0166 
0167         s_supportsUnpack = hasGLExtension(QByteArrayLiteral("GL_EXT_unpack_subimage"));
0168     }
0169 }
0170 
0171 void GLTexturePrivate::cleanup()
0172 {
0173     s_supportsFramebufferObjects = false;
0174     s_supportsARGB32 = false;
0175     if (s_fbo) {
0176         glDeleteFramebuffers(1, &s_fbo);
0177         s_fbo = 0;
0178     }
0179 }
0180 
0181 bool GLTexture::isNull() const
0182 {
0183     return GL_NONE == d->m_texture;
0184 }
0185 
0186 QSize GLTexture::size() const
0187 {
0188     return d->m_size;
0189 }
0190 
0191 void GLTexture::setSize(const QSize &size)
0192 {
0193     if (!isNull()) {
0194         return;
0195     }
0196     d->m_size = size;
0197     d->updateMatrix();
0198 }
0199 
0200 void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src)
0201 {
0202     if (image.isNull() || isNull()) {
0203         return;
0204     }
0205 
0206     Q_ASSERT(d->m_owning);
0207 
0208     GLenum glFormat;
0209     GLenum type;
0210     QImage::Format uploadFormat;
0211     if (!GLPlatform::instance()->isGLES()) {
0212         const QImage::Format index = image.format();
0213 
0214         if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat
0215             && !(formatTable[index].type == GL_UNSIGNED_SHORT && !d->s_supportsTexture16Bit)) {
0216             glFormat = formatTable[index].format;
0217             type = formatTable[index].type;
0218             uploadFormat = index;
0219         } else {
0220             glFormat = GL_BGRA;
0221             type = GL_UNSIGNED_INT_8_8_8_8_REV;
0222             uploadFormat = QImage::Format_ARGB32_Premultiplied;
0223         }
0224     } else {
0225         if (d->s_supportsARGB32) {
0226             glFormat = GL_BGRA_EXT;
0227             type = GL_UNSIGNED_BYTE;
0228             uploadFormat = QImage::Format_ARGB32_Premultiplied;
0229         } else {
0230             glFormat = GL_RGBA;
0231             type = GL_UNSIGNED_BYTE;
0232             uploadFormat = QImage::Format_RGBA8888_Premultiplied;
0233         }
0234     }
0235     bool useUnpack = d->s_supportsUnpack && image.format() == uploadFormat && !src.isNull();
0236 
0237     QImage im;
0238     if (useUnpack) {
0239         im = image;
0240         Q_ASSERT(im.depth() % 8 == 0);
0241         glPixelStorei(GL_UNPACK_ROW_LENGTH, im.bytesPerLine() / (im.depth() / 8));
0242         glPixelStorei(GL_UNPACK_SKIP_PIXELS, src.x());
0243         glPixelStorei(GL_UNPACK_SKIP_ROWS, src.y());
0244     } else {
0245         if (src.isNull()) {
0246             im = image;
0247         } else {
0248             im = image.copy(src);
0249         }
0250         if (im.format() != uploadFormat) {
0251             im.convertTo(uploadFormat);
0252         }
0253     }
0254 
0255     int width = image.width();
0256     int height = image.height();
0257     if (!src.isNull()) {
0258         width = src.width();
0259         height = src.height();
0260     }
0261 
0262     bind();
0263 
0264     glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, glFormat, type, im.constBits());
0265 
0266     unbind();
0267 
0268     if (useUnpack) {
0269         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
0270         glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
0271         glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
0272     }
0273 }
0274 
0275 void GLTexture::bind()
0276 {
0277     Q_ASSERT(d->m_texture);
0278 
0279     glBindTexture(d->m_target, d->m_texture);
0280 
0281     if (d->m_markedDirty) {
0282         onDamage();
0283     }
0284     if (d->m_filterChanged) {
0285         GLenum minFilter = GL_NEAREST;
0286         GLenum magFilter = GL_NEAREST;
0287 
0288         switch (d->m_filter) {
0289         case GL_NEAREST:
0290             minFilter = magFilter = GL_NEAREST;
0291             break;
0292 
0293         case GL_LINEAR:
0294             minFilter = magFilter = GL_LINEAR;
0295             break;
0296 
0297         case GL_NEAREST_MIPMAP_NEAREST:
0298         case GL_NEAREST_MIPMAP_LINEAR:
0299             magFilter = GL_NEAREST;
0300             minFilter = d->m_canUseMipmaps ? d->m_filter : GL_NEAREST;
0301             break;
0302 
0303         case GL_LINEAR_MIPMAP_NEAREST:
0304         case GL_LINEAR_MIPMAP_LINEAR:
0305             magFilter = GL_LINEAR;
0306             minFilter = d->m_canUseMipmaps ? d->m_filter : GL_LINEAR;
0307             break;
0308         }
0309 
0310         glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, minFilter);
0311         glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, magFilter);
0312 
0313         d->m_filterChanged = false;
0314     }
0315     if (d->m_wrapModeChanged) {
0316         glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, d->m_wrapMode);
0317         glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, d->m_wrapMode);
0318         d->m_wrapModeChanged = false;
0319     }
0320 }
0321 
0322 void GLTexture::generateMipmaps()
0323 {
0324     if (d->m_canUseMipmaps && d->s_supportsFramebufferObjects) {
0325         glGenerateMipmap(d->m_target);
0326     }
0327 }
0328 
0329 void GLTexture::unbind()
0330 {
0331     glBindTexture(d->m_target, 0);
0332 }
0333 
0334 void GLTexture::render(const QSizeF &size)
0335 {
0336     render(infiniteRegion(), size, false);
0337 }
0338 
0339 void GLTexture::render(const QRegion &region, const QSizeF &targetSize, bool hardwareClipping)
0340 {
0341     const auto rotatedSize = d->m_textureToBufferTransform.map(size());
0342     render(QRectF(QPoint(), rotatedSize), region, targetSize, hardwareClipping);
0343 }
0344 
0345 void GLTexture::render(const QRectF &source, const QRegion &region, const QSizeF &targetSize, bool hardwareClipping)
0346 {
0347     if (targetSize.isEmpty()) {
0348         return; // nothing to paint and m_vbo is likely nullptr and d->m_cachedSize empty as well, #337090
0349     }
0350 
0351     const QSize destinationSize = targetSize.toSize(); // TODO: toSize is not enough to snap to the pixel grid, fix render() users and drop this toSize
0352     if (targetSize != d->m_cachedSize || d->m_cachedSource != source) {
0353         d->m_cachedSize = destinationSize;
0354         d->m_cachedSource = source;
0355 
0356         const float texWidth = (target() == GL_TEXTURE_RECTANGLE_ARB) ? width() : 1.0f;
0357         const float texHeight = (target() == GL_TEXTURE_RECTANGLE_ARB) ? height() : 1.0f;
0358 
0359         const QSize rotatedSize = d->m_textureToBufferTransform.map(size());
0360 
0361         QMatrix4x4 textureMat;
0362         textureMat.translate(texWidth / 2, texHeight / 2);
0363         // our Y axis is flipped vs OpenGL
0364         textureMat.scale(1, -1);
0365         textureMat *= d->m_textureToBufferTransform.toMatrix();
0366         textureMat.translate(-texWidth / 2, -texHeight / 2);
0367         textureMat.scale(texWidth / rotatedSize.width(), texHeight / rotatedSize.height());
0368 
0369         const QPointF p1 = textureMat.map(QPointF(source.x(), source.y()));
0370         const QPointF p2 = textureMat.map(QPointF(source.x(), source.y() + source.height()));
0371         const QPointF p3 = textureMat.map(QPointF(source.x() + source.width(), source.y()));
0372         const QPointF p4 = textureMat.map(QPointF(source.x() + source.width(), source.y() + source.height()));
0373 
0374         if (!d->m_vbo) {
0375             d->m_vbo = std::make_unique<GLVertexBuffer>(KWin::GLVertexBuffer::Static);
0376         }
0377         const std::array<GLVertex2D, 4> data{
0378             GLVertex2D{
0379                 .position = QVector2D(0, 0),
0380                 .texcoord = QVector2D(p1),
0381             },
0382             GLVertex2D{
0383                 .position = QVector2D(0, destinationSize.height()),
0384                 .texcoord = QVector2D(p2),
0385             },
0386             GLVertex2D{
0387                 .position = QVector2D(destinationSize.width(), 0),
0388                 .texcoord = QVector2D(p3),
0389             },
0390             GLVertex2D{
0391                 .position = QVector2D(destinationSize.width(), destinationSize.height()),
0392                 .texcoord = QVector2D(p4),
0393             },
0394         };
0395         d->m_vbo->setVertices(data);
0396     }
0397     bind();
0398     d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping);
0399     unbind();
0400 }
0401 
0402 GLuint GLTexture::texture() const
0403 {
0404     return d->m_texture;
0405 }
0406 
0407 GLenum GLTexture::target() const
0408 {
0409     return d->m_target;
0410 }
0411 
0412 GLenum GLTexture::filter() const
0413 {
0414     return d->m_filter;
0415 }
0416 
0417 GLenum GLTexture::internalFormat() const
0418 {
0419     return d->m_internalFormat;
0420 }
0421 
0422 void GLTexture::clear()
0423 {
0424     Q_ASSERT(d->m_owning);
0425     if (!GLTexturePrivate::s_fbo && GLFramebuffer::supported() && GLPlatform::instance()->driver() != Driver_Catalyst) { // fail. -> bug #323065
0426         glGenFramebuffers(1, &GLTexturePrivate::s_fbo);
0427     }
0428 
0429     if (GLTexturePrivate::s_fbo) {
0430         // Clear the texture
0431         GLuint previousFramebuffer = 0;
0432         glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&previousFramebuffer));
0433         if (GLTexturePrivate::s_fbo != previousFramebuffer) {
0434             glBindFramebuffer(GL_FRAMEBUFFER, GLTexturePrivate::s_fbo);
0435         }
0436         glClearColor(0, 0, 0, 0);
0437         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, d->m_texture, 0);
0438         glClear(GL_COLOR_BUFFER_BIT);
0439         if (GLTexturePrivate::s_fbo != previousFramebuffer) {
0440             glBindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
0441         }
0442     } else {
0443         if (const int size = width() * height()) {
0444             std::vector<uint32_t> buffer(size, 0);
0445             bind();
0446             if (!GLPlatform::instance()->isGLES()) {
0447                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(),
0448                                 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer.data());
0449             } else {
0450                 const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA;
0451                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(),
0452                                 format, GL_UNSIGNED_BYTE, buffer.data());
0453             }
0454             unbind();
0455         }
0456     }
0457 }
0458 
0459 bool GLTexture::isDirty() const
0460 {
0461     return d->m_markedDirty;
0462 }
0463 
0464 void GLTexture::setFilter(GLenum filter)
0465 {
0466     if (filter != d->m_filter) {
0467         d->m_filter = filter;
0468         d->m_filterChanged = true;
0469     }
0470 }
0471 
0472 void GLTexture::setWrapMode(GLenum mode)
0473 {
0474     if (mode != d->m_wrapMode) {
0475         d->m_wrapMode = mode;
0476         d->m_wrapModeChanged = true;
0477     }
0478 }
0479 
0480 void GLTexture::setDirty()
0481 {
0482     d->m_markedDirty = true;
0483 }
0484 
0485 void GLTexture::onDamage()
0486 {
0487 }
0488 
0489 void GLTexturePrivate::updateMatrix()
0490 {
0491     const QMatrix4x4 textureToBufferMatrix = m_textureToBufferTransform.toMatrix();
0492 
0493     m_matrix[NormalizedCoordinates].setToIdentity();
0494     m_matrix[UnnormalizedCoordinates].setToIdentity();
0495 
0496     if (m_target == GL_TEXTURE_RECTANGLE_ARB) {
0497         m_matrix[NormalizedCoordinates].scale(m_size.width(), m_size.height());
0498     } else {
0499         m_matrix[UnnormalizedCoordinates].scale(1.0 / m_size.width(), 1.0 / m_size.height());
0500     }
0501 
0502     m_matrix[NormalizedCoordinates].translate(0.5, 0.5);
0503     // our Y axis is flipped vs OpenGL
0504     m_matrix[NormalizedCoordinates].scale(1, -1);
0505     m_matrix[NormalizedCoordinates] *= textureToBufferMatrix;
0506     m_matrix[NormalizedCoordinates].translate(-0.5, -0.5);
0507 
0508     m_matrix[UnnormalizedCoordinates].translate(m_size.width() / 2, m_size.height() / 2);
0509     m_matrix[UnnormalizedCoordinates].scale(1, -1);
0510     m_matrix[UnnormalizedCoordinates] *= textureToBufferMatrix;
0511     m_matrix[UnnormalizedCoordinates].translate(-m_size.width() / 2, -m_size.height() / 2);
0512 }
0513 
0514 void GLTexture::setContentTransform(OutputTransform transform)
0515 {
0516     if (d->m_textureToBufferTransform != transform) {
0517         d->m_textureToBufferTransform = transform;
0518         d->updateMatrix();
0519     }
0520 }
0521 
0522 OutputTransform GLTexture::contentTransform() const
0523 {
0524     return d->m_textureToBufferTransform;
0525 }
0526 
0527 void GLTexture::setSwizzle(GLenum red, GLenum green, GLenum blue, GLenum alpha)
0528 {
0529     if (!GLPlatform::instance()->isGLES()) {
0530         const GLuint swizzle[] = {red, green, blue, alpha};
0531         glTexParameteriv(d->m_target, GL_TEXTURE_SWIZZLE_RGBA, (const GLint *)swizzle);
0532     } else {
0533         glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_R, red);
0534         glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_G, green);
0535         glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_B, blue);
0536         glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_A, alpha);
0537     }
0538 }
0539 
0540 int GLTexture::width() const
0541 {
0542     return d->m_size.width();
0543 }
0544 
0545 int GLTexture::height() const
0546 {
0547     return d->m_size.height();
0548 }
0549 
0550 QMatrix4x4 GLTexture::matrix(TextureCoordinateType type) const
0551 {
0552     return d->m_matrix[type];
0553 }
0554 
0555 bool GLTexture::framebufferObjectSupported()
0556 {
0557     return GLTexturePrivate::s_supportsFramebufferObjects;
0558 }
0559 
0560 bool GLTexture::supportsSwizzle()
0561 {
0562     return GLTexturePrivate::s_supportsTextureSwizzle;
0563 }
0564 
0565 bool GLTexture::supportsFormatRG()
0566 {
0567     return GLTexturePrivate::s_supportsTextureFormatRG;
0568 }
0569 
0570 QImage GLTexture::toImage()
0571 {
0572     if (target() != GL_TEXTURE_2D) {
0573         return QImage();
0574     }
0575     QImage ret(size(), QImage::Format_RGBA8888_Premultiplied);
0576 
0577     if (GLPlatform::instance()->isGLES()) {
0578         GLFramebuffer fbo(this);
0579         GLFramebuffer::pushFramebuffer(&fbo);
0580         glReadPixels(0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ret.bits());
0581         GLFramebuffer::popFramebuffer();
0582     } else {
0583         GLint currentTextureBinding;
0584         glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureBinding);
0585         if (GLuint(currentTextureBinding) != texture()) {
0586             glBindTexture(GL_TEXTURE_2D, texture());
0587         }
0588         glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ret.bits());
0589         if (GLuint(currentTextureBinding) != texture()) {
0590             glBindTexture(GL_TEXTURE_2D, currentTextureBinding);
0591         }
0592     }
0593     return ret;
0594 }
0595 
0596 std::unique_ptr<GLTexture> GLTexture::createNonOwningWrapper(GLuint textureId, GLenum internalFormat, const QSize &size)
0597 {
0598     return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, textureId, internalFormat, size, 1, false, OutputTransform{}));
0599 }
0600 
0601 std::unique_ptr<GLTexture> GLTexture::allocate(GLenum internalFormat, const QSize &size, int levels)
0602 {
0603     GLuint texture = 0;
0604     glGenTextures(1, &texture);
0605     if (texture == 0) {
0606         qCWarning(KWIN_OPENGL, "generating OpenGL texture handle failed");
0607         return nullptr;
0608     }
0609     glBindTexture(GL_TEXTURE_2D, texture);
0610 
0611     if (!GLPlatform::instance()->isGLES()) {
0612         if (GLTexturePrivate::s_supportsTextureStorage) {
0613             glTexStorage2D(GL_TEXTURE_2D, levels, internalFormat, size.width(), size.height());
0614         } else {
0615             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels - 1);
0616             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size.width(), size.height(), 0,
0617                          GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
0618         }
0619     } else {
0620         // The format parameter in glTexSubImage() must match the internal format
0621         // of the texture, so it's important that we allocate the texture with
0622         // the format that will be used in update() and clear().
0623         const GLenum format = GLTexturePrivate::s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA;
0624         glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0,
0625                      format, GL_UNSIGNED_BYTE, nullptr);
0626 
0627         // The internalFormat is technically not correct, but it means that code that calls
0628         // internalFormat() won't need to be specialized for GLES2.
0629     }
0630     glBindTexture(GL_TEXTURE_2D, 0);
0631     return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, texture, internalFormat, size, levels, true, OutputTransform{}));
0632 }
0633 
0634 std::unique_ptr<GLTexture> GLTexture::upload(const QImage &image)
0635 {
0636     if (image.isNull()) {
0637         return nullptr;
0638     }
0639     GLuint texture = 0;
0640     glGenTextures(1, &texture);
0641     if (texture == 0) {
0642         qCWarning(KWIN_OPENGL, "generating OpenGL texture handle failed");
0643         return nullptr;
0644     }
0645     glBindTexture(GL_TEXTURE_2D, texture);
0646 
0647     GLenum internalFormat;
0648     if (!GLPlatform::instance()->isGLES()) {
0649         QImage im;
0650         GLenum format;
0651         GLenum type;
0652 
0653         const QImage::Format index = image.format();
0654 
0655         if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat
0656             && !(formatTable[index].type == GL_UNSIGNED_SHORT && !GLTexturePrivate::s_supportsTexture16Bit)) {
0657             internalFormat = formatTable[index].internalFormat;
0658             format = formatTable[index].format;
0659             type = formatTable[index].type;
0660             im = image;
0661         } else {
0662             im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
0663             internalFormat = GL_RGBA8;
0664             format = GL_BGRA;
0665             type = GL_UNSIGNED_INT_8_8_8_8_REV;
0666         }
0667 
0668         if (GLTexturePrivate::s_supportsTextureStorage) {
0669             glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, im.width(), im.height());
0670             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im.width(), im.height(),
0671                             format, type, im.constBits());
0672         } else {
0673             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
0674             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, im.width(), im.height(), 0,
0675                          format, type, im.constBits());
0676         }
0677     } else {
0678         internalFormat = GL_RGBA8;
0679 
0680         if (GLTexturePrivate::s_supportsARGB32) {
0681             const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
0682             glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, im.width(), im.height(),
0683                          0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.constBits());
0684         } else {
0685             const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
0686             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.width(), im.height(),
0687                          0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits());
0688         }
0689     }
0690     glBindTexture(GL_TEXTURE_2D, 0);
0691     return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, texture, internalFormat, image.size(), 1, true, OutputTransform::FlipY));
0692 }
0693 
0694 std::unique_ptr<GLTexture> GLTexture::upload(const QPixmap &pixmap)
0695 {
0696     return upload(pixmap.toImage());
0697 }
0698 
0699 } // namespace KWin