File indexing completed on 2024-11-10 04:56:55
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 ®ion, 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 ®ion, 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, ¤tTextureBinding); 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