KWin
Loading...
Searching...
No Matches
gltexture.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
6 SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
7 SPDX-FileCopyrightText: 2012 Philipp Knechtges <philipp-dev@knechtges.com>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12#include "gltexture_p.h"
14#include "opengl/glplatform.h"
15#include "opengl/glutils.h"
17#include "utils/common.h"
18
19#include <QImage>
20#include <QPixmap>
21#include <QVector2D>
22#include <QVector3D>
23#include <QVector4D>
24
25namespace KWin
26{
27
28//****************************************
29// GLTexture
30//****************************************
31
40
41// Table of GL formats/types associated with different values of QImage::Format.
42// Zero values indicate a direct upload is not feasible.
43//
44// Note: Blending is set up to expect premultiplied data, so the non-premultiplied
45// Format_ARGB32 must be converted to Format_ARGB32_Premultiplied ahead of time.
46struct
47{
49 GLenum format;
50 GLenum type;
51} static const formatTable[] = {
52 {0, 0, 0}, // QImage::Format_Invalid
53 {0, 0, 0}, // QImage::Format_Mono
54 {0, 0, 0}, // QImage::Format_MonoLSB
55 {0, 0, 0}, // QImage::Format_Indexed8
56 {GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // QImage::Format_RGB32
57 {0, 0, 0}, // QImage::Format_ARGB32
58 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // QImage::Format_ARGB32_Premultiplied
59 {GL_RGB8, GL_BGR, GL_UNSIGNED_SHORT_5_6_5_REV}, // QImage::Format_RGB16
60 {0, 0, 0}, // QImage::Format_ARGB8565_Premultiplied
61 {0, 0, 0}, // QImage::Format_RGB666
62 {0, 0, 0}, // QImage::Format_ARGB6666_Premultiplied
63 {GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // QImage::Format_RGB555
64 {0, 0, 0}, // QImage::Format_ARGB8555_Premultiplied
65 {GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // QImage::Format_RGB888
66 {GL_RGB4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // QImage::Format_RGB444
67 {GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // QImage::Format_ARGB4444_Premultiplied
68 {GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE}, // QImage::Format_RGBX8888
69 {0, 0, 0}, // QImage::Format_RGBA8888
70 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // QImage::Format_RGBA8888_Premultiplied
71 {GL_RGB10, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_BGR30
72 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_A2BGR30_Premultiplied
73 {GL_RGB10, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_RGB30
74 {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // QImage::Format_A2RGB30_Premultiplied
75 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // QImage::Format_Alpha8
76 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // QImage::Format_Grayscale8
77 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // QImage::Format_RGBX64
78 {0, 0, 0}, // QImage::Format_RGBA64
79 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // QImage::Format_RGBA64_Premultiplied
80 {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // QImage::Format_Grayscale16
81 {0, 0, 0}, // QImage::Format_BGR888
82};
83
85 : d(std::make_unique<GLTexturePrivate>())
86{
87 d->m_target = target;
88}
89
90GLTexture::GLTexture(GLenum target, GLuint textureId, GLenum internalFormat, const QSize &size, int levels, bool owning, OutputTransform transform)
91 : GLTexture(target)
92{
93 d->m_owning = owning;
94 d->m_texture = textureId;
95 d->m_scale.setWidth(1.0 / size.width());
96 d->m_scale.setHeight(1.0 / size.height());
97 d->m_size = size;
98 d->m_canUseMipmaps = levels > 1;
99 d->m_mipLevels = levels;
100 d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
101 d->m_internalFormat = internalFormat;
102 d->m_textureToBufferTransform = transform;
103
104 d->updateMatrix();
105}
106
110
112{
113 if (!isNull()) {
114 return true;
115 }
116 glGenTextures(1, &d->m_texture);
117 return d->m_texture != GL_NONE;
118}
119
121 : m_texture(0)
122 , m_target(0)
123 , m_internalFormat(0)
124 , m_filter(GL_NEAREST)
125 , m_wrapMode(GL_REPEAT)
126 , m_canUseMipmaps(false)
127 , m_markedDirty(false)
128 , m_filterChanged(true)
129 , m_wrapModeChanged(false)
130 , m_owning(true)
131 , m_mipLevels(1)
132 , m_unnormalizeActive(0)
133 , m_normalizeActive(0)
134{
135}
136
138{
139 if (m_texture != 0 && m_owning) {
140 glDeleteTextures(1, &m_texture);
141 }
142}
143
145{
146 if (!GLPlatform::instance()->isGLES()) {
147 s_supportsFramebufferObjects = hasGLVersion(3, 0) || hasGLExtension("GL_ARB_framebuffer_object") || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object"));
148 s_supportsTextureStorage = hasGLVersion(4, 2) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_storage"));
149 s_supportsTextureSwizzle = hasGLVersion(3, 3) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_swizzle"));
150 // see https://www.opengl.org/registry/specs/ARB/texture_rg.txt
151 s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_rg"));
153 s_supportsARGB32 = true;
154 s_supportsUnpack = true;
155 } else {
157 s_supportsTextureStorage = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_storage"));
159 // see https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
160 s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_rg"));
161 s_supportsTexture16Bit = hasGLExtension(QByteArrayLiteral("GL_EXT_texture_norm16"));
162
163 // QImage::Format_ARGB32_Premultiplied is a packed-pixel format, so it's only
164 // equivalent to GL_BGRA/GL_UNSIGNED_BYTE on little-endian systems.
165 s_supportsARGB32 = QSysInfo::ByteOrder == QSysInfo::LittleEndian && hasGLExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"));
166
167 s_supportsUnpack = hasGLExtension(QByteArrayLiteral("GL_EXT_unpack_subimage"));
168 }
169}
170
171void GLTexturePrivate::cleanup()
172{
174 s_supportsARGB32 = false;
175 if (s_fbo) {
176 glDeleteFramebuffers(1, &s_fbo);
177 s_fbo = 0;
178 }
179}
180
182{
183 return GL_NONE == d->m_texture;
184}
185
186QSize GLTexture::size() const
187{
188 return d->m_size;
189}
190
191void GLTexture::setSize(const QSize &size)
192{
193 if (!isNull()) {
194 return;
195 }
196 d->m_size = size;
197 d->updateMatrix();
198}
199
200void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src)
201{
202 if (image.isNull() || isNull()) {
203 return;
204 }
205
206 Q_ASSERT(d->m_owning);
207
208 GLenum glFormat;
209 GLenum type;
210 QImage::Format uploadFormat;
211 if (!GLPlatform::instance()->isGLES()) {
212 const QImage::Format index = image.format();
213
214 if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat
215 && !(formatTable[index].type == GL_UNSIGNED_SHORT && !d->s_supportsTexture16Bit)) {
216 glFormat = formatTable[index].format;
217 type = formatTable[index].type;
218 uploadFormat = index;
219 } else {
220 glFormat = GL_BGRA;
221 type = GL_UNSIGNED_INT_8_8_8_8_REV;
222 uploadFormat = QImage::Format_ARGB32_Premultiplied;
223 }
224 } else {
225 if (d->s_supportsARGB32) {
226 glFormat = GL_BGRA_EXT;
227 type = GL_UNSIGNED_BYTE;
228 uploadFormat = QImage::Format_ARGB32_Premultiplied;
229 } else {
230 glFormat = GL_RGBA;
231 type = GL_UNSIGNED_BYTE;
232 uploadFormat = QImage::Format_RGBA8888_Premultiplied;
233 }
234 }
235 bool useUnpack = d->s_supportsUnpack && image.format() == uploadFormat && !src.isNull();
236
237 QImage im;
238 if (useUnpack) {
239 im = image;
240 Q_ASSERT(im.depth() % 8 == 0);
241 glPixelStorei(GL_UNPACK_ROW_LENGTH, im.bytesPerLine() / (im.depth() / 8));
242 glPixelStorei(GL_UNPACK_SKIP_PIXELS, src.x());
243 glPixelStorei(GL_UNPACK_SKIP_ROWS, src.y());
244 } else {
245 if (src.isNull()) {
246 im = image;
247 } else {
248 im = image.copy(src);
249 }
250 if (im.format() != uploadFormat) {
251 im.convertTo(uploadFormat);
252 }
253 }
254
255 int width = image.width();
256 int height = image.height();
257 if (!src.isNull()) {
258 width = src.width();
259 height = src.height();
260 }
261
262 bind();
263
264 glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, glFormat, type, im.constBits());
265
266 unbind();
267
268 if (useUnpack) {
269 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
270 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
271 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
272 }
273}
274
276{
277 Q_ASSERT(d->m_texture);
278
279 glBindTexture(d->m_target, d->m_texture);
280
281 if (d->m_markedDirty) {
282 onDamage();
283 }
284 if (d->m_filterChanged) {
285 GLenum minFilter = GL_NEAREST;
286 GLenum magFilter = GL_NEAREST;
287
288 switch (d->m_filter) {
289 case GL_NEAREST:
290 minFilter = magFilter = GL_NEAREST;
291 break;
292
293 case GL_LINEAR:
294 minFilter = magFilter = GL_LINEAR;
295 break;
296
297 case GL_NEAREST_MIPMAP_NEAREST:
298 case GL_NEAREST_MIPMAP_LINEAR:
299 magFilter = GL_NEAREST;
300 minFilter = d->m_canUseMipmaps ? d->m_filter : GL_NEAREST;
301 break;
302
303 case GL_LINEAR_MIPMAP_NEAREST:
304 case GL_LINEAR_MIPMAP_LINEAR:
305 magFilter = GL_LINEAR;
306 minFilter = d->m_canUseMipmaps ? d->m_filter : GL_LINEAR;
307 break;
308 }
309
310 glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, minFilter);
311 glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, magFilter);
312
313 d->m_filterChanged = false;
314 }
315 if (d->m_wrapModeChanged) {
316 glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, d->m_wrapMode);
317 glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, d->m_wrapMode);
318 d->m_wrapModeChanged = false;
319 }
320}
321
323{
324 if (d->m_canUseMipmaps && d->s_supportsFramebufferObjects) {
325 glGenerateMipmap(d->m_target);
326 }
327}
328
330{
331 glBindTexture(d->m_target, 0);
332}
333
334void GLTexture::render(const QSizeF &size)
335{
336 render(infiniteRegion(), size, false);
337}
338
339void GLTexture::render(const QRegion &region, const QSizeF &targetSize, bool hardwareClipping)
340{
341 const auto rotatedSize = d->m_textureToBufferTransform.map(size());
342 render(QRectF(QPoint(), rotatedSize), region, targetSize, hardwareClipping);
343}
344
345void GLTexture::render(const QRectF &source, const QRegion &region, const QSizeF &targetSize, bool hardwareClipping)
346{
347 if (targetSize.isEmpty()) {
348 return; // nothing to paint and m_vbo is likely nullptr and d->m_cachedSize empty as well, #337090
349 }
350
351 const QSize destinationSize = targetSize.toSize(); // TODO: toSize is not enough to snap to the pixel grid, fix render() users and drop this toSize
352 if (targetSize != d->m_cachedSize || d->m_cachedSource != source || d->m_cachedContentTransform != d->m_textureToBufferTransform) {
353 d->m_cachedSize = destinationSize;
354 d->m_cachedSource = source;
355 d->m_cachedContentTransform = d->m_textureToBufferTransform;
356
357 const float texWidth = (target() == GL_TEXTURE_RECTANGLE_ARB) ? width() : 1.0f;
358 const float texHeight = (target() == GL_TEXTURE_RECTANGLE_ARB) ? height() : 1.0f;
359
360 const QSize rotatedSize = d->m_textureToBufferTransform.map(size());
361
362 QMatrix4x4 textureMat;
363 textureMat.translate(texWidth / 2, texHeight / 2);
364 // our Y axis is flipped vs OpenGL
365 textureMat.scale(1, -1);
366 textureMat *= d->m_textureToBufferTransform.toMatrix();
367 textureMat.translate(-texWidth / 2, -texHeight / 2);
368 textureMat.scale(texWidth / rotatedSize.width(), texHeight / rotatedSize.height());
369
370 const QPointF p1 = textureMat.map(QPointF(source.x(), source.y()));
371 const QPointF p2 = textureMat.map(QPointF(source.x(), source.y() + source.height()));
372 const QPointF p3 = textureMat.map(QPointF(source.x() + source.width(), source.y()));
373 const QPointF p4 = textureMat.map(QPointF(source.x() + source.width(), source.y() + source.height()));
374
375 if (!d->m_vbo) {
376 d->m_vbo = std::make_unique<GLVertexBuffer>(KWin::GLVertexBuffer::Static);
377 }
378 const std::array<GLVertex2D, 4> data{
380 .position = QVector2D(0, 0),
381 .texcoord = QVector2D(p1),
382 },
384 .position = QVector2D(0, destinationSize.height()),
385 .texcoord = QVector2D(p2),
386 },
388 .position = QVector2D(destinationSize.width(), 0),
389 .texcoord = QVector2D(p3),
390 },
392 .position = QVector2D(destinationSize.width(), destinationSize.height()),
393 .texcoord = QVector2D(p4),
394 },
395 };
396 d->m_vbo->setVertices(data);
397 }
398 bind();
399 d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping);
400 unbind();
401}
402
403GLuint GLTexture::texture() const
404{
405 return d->m_texture;
406}
407
408GLenum GLTexture::target() const
409{
410 return d->m_target;
411}
412
413GLenum GLTexture::filter() const
414{
415 return d->m_filter;
416}
417
419{
420 return d->m_internalFormat;
421}
422
424{
425 Q_ASSERT(d->m_owning);
426 if (!GLTexturePrivate::s_fbo && GLFramebuffer::supported() && GLPlatform::instance()->driver() != Driver_Catalyst) { // fail. -> bug #323065
427 glGenFramebuffers(1, &GLTexturePrivate::s_fbo);
428 }
429
431 // Clear the texture
432 GLuint previousFramebuffer = 0;
433 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&previousFramebuffer));
434 if (GLTexturePrivate::s_fbo != previousFramebuffer) {
435 glBindFramebuffer(GL_FRAMEBUFFER, GLTexturePrivate::s_fbo);
436 }
437 glClearColor(0, 0, 0, 0);
438 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, d->m_texture, 0);
439 glClear(GL_COLOR_BUFFER_BIT);
440 if (GLTexturePrivate::s_fbo != previousFramebuffer) {
441 glBindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
442 }
443 } else {
444 if (const int size = width() * height()) {
445 std::vector<uint32_t> buffer(size, 0);
446 bind();
447 if (!GLPlatform::instance()->isGLES()) {
448 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(),
449 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer.data());
450 } else {
451 const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA;
452 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(),
453 format, GL_UNSIGNED_BYTE, buffer.data());
454 }
455 unbind();
456 }
457 }
458}
459
461{
462 return d->m_markedDirty;
463}
464
465void GLTexture::setFilter(GLenum filter)
466{
467 if (filter != d->m_filter) {
468 d->m_filter = filter;
469 d->m_filterChanged = true;
470 }
471}
472
473void GLTexture::setWrapMode(GLenum mode)
474{
475 if (mode != d->m_wrapMode) {
476 d->m_wrapMode = mode;
477 d->m_wrapModeChanged = true;
478 }
479}
480
482{
483 d->m_markedDirty = true;
484}
485
487{
488}
489
491{
492 const QMatrix4x4 textureToBufferMatrix = m_textureToBufferTransform.toMatrix();
493
494 m_matrix[NormalizedCoordinates].setToIdentity();
495 m_matrix[UnnormalizedCoordinates].setToIdentity();
496
497 if (m_target == GL_TEXTURE_RECTANGLE_ARB) {
498 m_matrix[NormalizedCoordinates].scale(m_size.width(), m_size.height());
499 } else {
500 m_matrix[UnnormalizedCoordinates].scale(1.0 / m_size.width(), 1.0 / m_size.height());
501 }
502
503 m_matrix[NormalizedCoordinates].translate(0.5, 0.5);
504 // our Y axis is flipped vs OpenGL
505 m_matrix[NormalizedCoordinates].scale(1, -1);
506 m_matrix[NormalizedCoordinates] *= textureToBufferMatrix;
507 m_matrix[NormalizedCoordinates].translate(-0.5, -0.5);
508
509 m_matrix[UnnormalizedCoordinates].translate(m_size.width() / 2, m_size.height() / 2);
510 m_matrix[UnnormalizedCoordinates].scale(1, -1);
511 m_matrix[UnnormalizedCoordinates] *= textureToBufferMatrix;
512 m_matrix[UnnormalizedCoordinates].translate(-m_size.width() / 2, -m_size.height() / 2);
513}
514
516{
517 if (d->m_textureToBufferTransform != transform) {
518 d->m_textureToBufferTransform = transform;
519 d->updateMatrix();
520 }
521}
522
524{
525 return d->m_textureToBufferTransform;
526}
527
528void GLTexture::setSwizzle(GLenum red, GLenum green, GLenum blue, GLenum alpha)
529{
530 if (!GLPlatform::instance()->isGLES()) {
531 const GLuint swizzle[] = {red, green, blue, alpha};
532 glTexParameteriv(d->m_target, GL_TEXTURE_SWIZZLE_RGBA, (const GLint *)swizzle);
533 } else {
534 glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_R, red);
535 glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_G, green);
536 glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_B, blue);
537 glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_A, alpha);
538 }
539}
540
542{
543 return d->m_size.width();
544}
545
547{
548 return d->m_size.height();
549}
550
552{
553 return d->m_matrix[type];
554}
555
560
565
570
572{
573 if (target() != GL_TEXTURE_2D) {
574 return QImage();
575 }
576 QImage ret(size(), QImage::Format_RGBA8888_Premultiplied);
577
578 if (GLPlatform::instance()->isGLES()) {
579 GLFramebuffer fbo(this);
581 glReadPixels(0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ret.bits());
583 } else {
584 GLint currentTextureBinding;
585 glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureBinding);
586 if (GLuint(currentTextureBinding) != texture()) {
587 glBindTexture(GL_TEXTURE_2D, texture());
588 }
589 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ret.bits());
590 if (GLuint(currentTextureBinding) != texture()) {
591 glBindTexture(GL_TEXTURE_2D, currentTextureBinding);
592 }
593 }
594 return ret;
595}
596
597std::unique_ptr<GLTexture> GLTexture::createNonOwningWrapper(GLuint textureId, GLenum internalFormat, const QSize &size)
598{
599 return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, textureId, internalFormat, size, 1, false, OutputTransform{}));
600}
601
602std::unique_ptr<GLTexture> GLTexture::allocate(GLenum internalFormat, const QSize &size, int levels)
603{
604 GLuint texture = 0;
605 glGenTextures(1, &texture);
606 if (texture == 0) {
607 qCWarning(KWIN_OPENGL, "generating OpenGL texture handle failed");
608 return nullptr;
609 }
610 glBindTexture(GL_TEXTURE_2D, texture);
611
612 if (!GLPlatform::instance()->isGLES()) {
614 glTexStorage2D(GL_TEXTURE_2D, levels, internalFormat, size.width(), size.height());
615 } else {
616 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels - 1);
617 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size.width(), size.height(), 0,
618 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
619 }
620 } else {
621 // The format parameter in glTexSubImage() must match the internal format
622 // of the texture, so it's important that we allocate the texture with
623 // the format that will be used in update() and clear().
624 const GLenum format = GLTexturePrivate::s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA;
625 glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0,
626 format, GL_UNSIGNED_BYTE, nullptr);
627
628 // The internalFormat is technically not correct, but it means that code that calls
629 // internalFormat() won't need to be specialized for GLES2.
630 }
631 glBindTexture(GL_TEXTURE_2D, 0);
632 return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, texture, internalFormat, size, levels, true, OutputTransform{}));
633}
634
635std::unique_ptr<GLTexture> GLTexture::upload(const QImage &image)
636{
637 if (image.isNull()) {
638 return nullptr;
639 }
640 GLuint texture = 0;
641 glGenTextures(1, &texture);
642 if (texture == 0) {
643 qCWarning(KWIN_OPENGL, "generating OpenGL texture handle failed");
644 return nullptr;
645 }
646 glBindTexture(GL_TEXTURE_2D, texture);
647
648 GLenum internalFormat;
649 if (!GLPlatform::instance()->isGLES()) {
650 QImage im;
651 GLenum format;
652 GLenum type;
653
654 const QImage::Format index = image.format();
655
656 if (index < sizeof(formatTable) / sizeof(formatTable[0]) && formatTable[index].internalFormat
657 && !(formatTable[index].type == GL_UNSIGNED_SHORT && !GLTexturePrivate::s_supportsTexture16Bit)) {
658 internalFormat = formatTable[index].internalFormat;
659 format = formatTable[index].format;
660 type = formatTable[index].type;
661 im = image;
662 } else {
663 im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
664 internalFormat = GL_RGBA8;
665 format = GL_BGRA;
666 type = GL_UNSIGNED_INT_8_8_8_8_REV;
667 }
668
670 glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, im.width(), im.height());
671 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im.width(), im.height(),
672 format, type, im.constBits());
673 } else {
674 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
675 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, im.width(), im.height(), 0,
676 format, type, im.constBits());
677 }
678 } else {
679 internalFormat = GL_RGBA8;
680
682 const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
683 glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, im.width(), im.height(),
684 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.constBits());
685 } else {
686 const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
687 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.width(), im.height(),
688 0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits());
689 }
690 }
691 glBindTexture(GL_TEXTURE_2D, 0);
692 return std::unique_ptr<GLTexture>(new GLTexture(GL_TEXTURE_2D, texture, internalFormat, image.size(), 1, true, OutputTransform::FlipY));
693}
694
695std::unique_ptr<GLTexture> GLTexture::upload(const QPixmap &pixmap)
696{
697 return upload(pixmap.toImage());
698}
699
700} // namespace KWin
OpenGL framebuffer object.
static bool supported()
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
static GLPlatform * instance()
Definition glplatform.h:394
virtual void onDamage()
const std::unique_ptr< GLTexturePrivate > d
Definition gltexture.h:143
int width() const
QSize size() const
GLenum filter() const
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
OutputTransform contentTransform() const
void setContentTransform(OutputTransform transform)
GLuint texture() const
static bool supportsFormatRG()
void generateMipmaps()
virtual ~GLTexture()
bool isNull() const
void setFilter(GLenum filter)
bool isDirty() const
static std::unique_ptr< GLTexture > createNonOwningWrapper(GLuint textureId, GLenum internalFormat, const QSize &size)
GLenum internalFormat() const
int height() const
void setSize(const QSize &size)
void update(const QImage &image, const QPoint &offset=QPoint(0, 0), const QRect &src=QRect())
void setSwizzle(GLenum red, GLenum green, GLenum blue, GLenum alpha)
static std::unique_ptr< GLTexture > upload(const QImage &image)
GLTexture(GLenum target)
Definition gltexture.cpp:84
void clear()
Make the texture fully transparent.
QMatrix4x4 matrix(TextureCoordinateType type) const
GLenum target() const
void render(const QSizeF &size)
static bool framebufferObjectSupported()
void setWrapMode(GLenum mode)
static bool supportsSwizzle()
static bool s_supportsTexture16Bit
Definition gltexture_p.h:67
QMatrix4x4 m_matrix[2]
Definition gltexture_p.h:43
static bool s_supportsTextureFormatRG
Definition gltexture_p.h:66
static bool s_supportsARGB32
Definition gltexture_p.h:62
static bool s_supportsTextureSwizzle
Definition gltexture_p.h:65
static void initStatic()
static bool s_supportsTextureStorage
Definition gltexture_p.h:64
static bool s_supportsFramebufferObjects
Definition gltexture_p.h:61
static bool s_supportsUnpack
Definition gltexture_p.h:63
OutputTransform m_textureToBufferTransform
Definition gltexture_p.h:44
static GLuint s_fbo
Definition gltexture_p.h:68
@ Static
No changes to data.
QMatrix4x4 toMatrix() const
Definition output.cpp:300
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
@ Driver_Catalyst
Definition glplatform.h:55
Session::Type type
Definition session.cpp:17
bool hasGLVersion(int major, int minor, int release)
Definition glutils.cpp:133
GLenum internalFormat
Definition gltexture.cpp:48
GLenum format
Definition gltexture.cpp:49
struct KWin::@10 formatTable[]
bool hasGLExtension(const QByteArray &extension)
Definition glutils.cpp:138
TextureCoordinateType
Definition gltexture.h:34
@ NormalizedCoordinates
Definition gltexture.h:35
@ UnnormalizedCoordinates
Definition gltexture.h:36