You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
664 lines
21 KiB
664 lines
21 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee> |
|
Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> |
|
Copyright (C) 2012 Philipp Knechtges <philipp-dev@knechtges.com> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
#include "kwinconfig.h" // KWIN_HAVE_OPENGL |
|
|
|
#include "kwinglplatform.h" |
|
#include "kwinglutils_funcs.h" |
|
#include "kwinglutils.h" |
|
|
|
#include "kwingltexture_p.h" |
|
|
|
#include <QPixmap> |
|
#include <QImage> |
|
#include <QVector2D> |
|
#include <QVector3D> |
|
#include <QVector4D> |
|
#include <QMatrix4x4> |
|
|
|
namespace KWin |
|
{ |
|
|
|
//**************************************** |
|
// GLTexture |
|
//**************************************** |
|
|
|
bool GLTexturePrivate::s_supportsFramebufferObjects = false; |
|
bool GLTexturePrivate::s_supportsARGB32 = false; |
|
bool GLTexturePrivate::s_supportsUnpack = false; |
|
bool GLTexturePrivate::s_supportsTextureStorage = false; |
|
bool GLTexturePrivate::s_supportsTextureSwizzle = false; |
|
bool GLTexturePrivate::s_supportsTextureFormatRG = false; |
|
uint GLTexturePrivate::s_textureObjectCounter = 0; |
|
uint GLTexturePrivate::s_fbo = 0; |
|
|
|
|
|
GLTexture::GLTexture() |
|
: d_ptr(new GLTexturePrivate()) |
|
{ |
|
} |
|
|
|
GLTexture::GLTexture(GLTexturePrivate& dd) |
|
: d_ptr(&dd) |
|
{ |
|
} |
|
|
|
GLTexture::GLTexture(const GLTexture& tex) |
|
: d_ptr(tex.d_ptr) |
|
{ |
|
} |
|
|
|
GLTexture::GLTexture(const QImage& image, GLenum target) |
|
: d_ptr(new GLTexturePrivate()) |
|
{ |
|
Q_D(GLTexture); |
|
|
|
if (image.isNull()) |
|
return; |
|
|
|
d->m_target = target; |
|
|
|
if (d->m_target != GL_TEXTURE_RECTANGLE_ARB) { |
|
d->m_scale.setWidth(1.0 / image.width()); |
|
d->m_scale.setHeight(1.0 / image.height()); |
|
} else { |
|
d->m_scale.setWidth(1.0); |
|
d->m_scale.setHeight(1.0); |
|
} |
|
|
|
d->m_size = image.size(); |
|
d->m_yInverted = true; |
|
d->m_canUseMipmaps = false; |
|
d->m_mipLevels = 1; |
|
|
|
d->updateMatrix(); |
|
|
|
glGenTextures(1, &d->m_texture); |
|
bind(); |
|
|
|
if (!GLPlatform::instance()->isGLES()) { |
|
// Note: Blending is set up to expect premultiplied data, so non-premultiplied |
|
// formats must always be converted. |
|
struct { |
|
GLenum internalFormat; |
|
GLenum format; |
|
GLenum type; |
|
} static const table[] = { |
|
{ 0, 0, 0 }, // QImage::Format_Invalid |
|
{ 0, 0, 0 }, // QImage::Format_Mono |
|
{ 0, 0, 0 }, // QImage::Format_MonoLSB |
|
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // QImage::Format_Indexed8 |
|
{ GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, // QImage::Format_RGB32 |
|
{ 0, 0, 0 }, // QImage::Format_ARGB32 |
|
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, // QImage::Format_ARGB32_Premultiplied |
|
{ GL_RGB8, GL_BGR, GL_UNSIGNED_SHORT_5_6_5_REV }, // QImage::Format_RGB16 |
|
{ 0, 0, 0 }, // QImage::Format_ARGB8565_Premultiplied |
|
{ 0, 0, 0 }, // QImage::Format_RGB666 |
|
{ GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV }, // QImage::Format_RGB555 |
|
{ 0, 0, 0 }, // QImage::Format_ARGB8555_Premultiplied |
|
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE }, // QImage::Format_RGB888 |
|
{ GL_RGB4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV }, // QImage::Format_RGB444 |
|
{ GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV }, // QImage::Format_ARGB4444_Premultiplied |
|
{ GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE }, // QImage::Format_RGBX8888 |
|
{ 0, 0, 0 }, // QImage::Format_RGBA8888 |
|
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // QImage::Format_RGBA8888_Premultiplied |
|
{ GL_RGB10, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_BGR30 |
|
{ GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_A2BGR30_Premultiplied |
|
{ GL_RGB10, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_RGB30 |
|
{ GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV }, // QImage::Format_A2RGB30_Premultiplied |
|
}; |
|
|
|
QImage im; |
|
GLenum internalFormat; |
|
GLenum format; |
|
GLenum type; |
|
|
|
const QImage::Format index = image.format(); |
|
|
|
if (index < sizeof(table) / sizeof(table[0]) && table[index].internalFormat && |
|
!(index == QImage::Format_Indexed8 && image.colorCount() > 0)) { |
|
internalFormat = table[index].internalFormat; |
|
format = table[index].format; |
|
type = table[index].type; |
|
im = image; |
|
} else { |
|
im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); |
|
internalFormat = GL_RGBA8; |
|
format = GL_BGRA; |
|
type = GL_UNSIGNED_INT_8_8_8_8_REV; |
|
} |
|
|
|
d->m_internalFormat = internalFormat; |
|
|
|
if (d->s_supportsTextureStorage) { |
|
glTexStorage2D(d->m_target, 1, internalFormat, im.width(), im.height()); |
|
glTexSubImage2D(d->m_target, 0, 0, 0, im.width(), im.height(), |
|
format, type, im.bits()); |
|
d->m_immutable = true; |
|
} else { |
|
glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, d->m_mipLevels - 1); |
|
glTexImage2D(d->m_target, 0, internalFormat, im.width(), im.height(), 0, |
|
format, type, im.bits()); |
|
} |
|
} else { |
|
d->m_internalFormat = GL_RGBA8; |
|
|
|
if (d->s_supportsARGB32) { |
|
const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); |
|
glTexImage2D(d->m_target, 0, GL_BGRA_EXT, im.width(), im.height(), |
|
0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); |
|
} else { |
|
const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); |
|
glTexImage2D(d->m_target, 0, GL_RGBA, im.width(), im.height(), |
|
0, GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); |
|
} |
|
} |
|
|
|
unbind(); |
|
setFilter(GL_LINEAR); |
|
} |
|
|
|
GLTexture::GLTexture(const QPixmap& pixmap, GLenum target) |
|
: GLTexture(pixmap.toImage(), target) |
|
{ |
|
} |
|
|
|
GLTexture::GLTexture(const QString& fileName) |
|
: GLTexture(QImage(fileName)) |
|
{ |
|
} |
|
|
|
GLTexture::GLTexture(GLenum internalFormat, int width, int height, int levels) |
|
: d_ptr(new GLTexturePrivate()) |
|
{ |
|
Q_D(GLTexture); |
|
|
|
d->m_target = GL_TEXTURE_2D; |
|
d->m_scale.setWidth(1.0 / width); |
|
d->m_scale.setHeight(1.0 / height); |
|
d->m_size = QSize(width, height); |
|
d->m_canUseMipmaps = levels > 1; |
|
d->m_mipLevels = levels; |
|
d->m_canUseMipmaps = true; |
|
d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST; |
|
|
|
d->updateMatrix(); |
|
|
|
glGenTextures(1, &d->m_texture); |
|
bind(); |
|
|
|
if (!GLPlatform::instance()->isGLES()) { |
|
if (d->s_supportsTextureStorage) { |
|
glTexStorage2D(d->m_target, levels, internalFormat, width, height); |
|
d->m_immutable = true; |
|
} else { |
|
glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, levels - 1); |
|
glTexImage2D(d->m_target, 0, internalFormat, width, height, 0, |
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); |
|
} |
|
d->m_internalFormat = internalFormat; |
|
} else { |
|
// The format parameter in glTexSubImage() must match the internal format |
|
// of the texture, so it's important that we allocate the texture with |
|
// the format that will be used in update() and clear(). |
|
const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA; |
|
glTexImage2D(d->m_target, 0, format, width, height, 0, |
|
format, GL_UNSIGNED_BYTE, nullptr); |
|
|
|
// This is technically not true, but it means that code that calls |
|
// internalFormat() won't need to be specialized for GLES2. |
|
d->m_internalFormat = GL_RGBA8; |
|
} |
|
|
|
unbind(); |
|
} |
|
|
|
GLTexture::GLTexture(GLenum internalFormat, const QSize &size, int levels) |
|
: GLTexture(internalFormat, size.width(), size.height(), levels) |
|
{ |
|
} |
|
|
|
GLTexture::~GLTexture() |
|
{ |
|
} |
|
|
|
GLTexture& GLTexture::operator = (const GLTexture& tex) |
|
{ |
|
d_ptr = tex.d_ptr; |
|
return *this; |
|
} |
|
|
|
GLTexturePrivate::GLTexturePrivate() |
|
{ |
|
m_texture = 0; |
|
m_target = 0; |
|
m_internalFormat = 0; |
|
m_filter = GL_NEAREST; |
|
m_wrapMode = GL_REPEAT; |
|
m_yInverted = false; |
|
m_canUseMipmaps = false; |
|
m_mipLevels = 1; |
|
m_immutable = false; |
|
m_markedDirty = false; |
|
m_unnormalizeActive = 0; |
|
m_normalizeActive = 0; |
|
m_vbo = nullptr; |
|
m_filterChanged = true; |
|
m_wrapModeChanged = false; |
|
++s_textureObjectCounter; |
|
} |
|
|
|
GLTexturePrivate::~GLTexturePrivate() |
|
{ |
|
if (m_vbo != nullptr) { |
|
delete m_vbo; |
|
} |
|
if (m_texture != 0) { |
|
glDeleteTextures(1, &m_texture); |
|
} |
|
// Delete the FBO if this is the last Texture |
|
if (--s_textureObjectCounter == 0 && s_fbo) { |
|
glDeleteFramebuffers(1, &s_fbo); |
|
s_fbo = 0; |
|
} |
|
} |
|
|
|
void GLTexturePrivate::initStatic() |
|
{ |
|
if (!GLPlatform::instance()->isGLES()) { |
|
s_supportsFramebufferObjects = hasGLVersion(3, 0) || |
|
hasGLExtension("GL_ARB_framebuffer_object") || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object")); |
|
s_supportsTextureStorage = hasGLVersion(4, 2) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_storage")); |
|
s_supportsTextureSwizzle = hasGLVersion(3, 3) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_swizzle")); |
|
// see https://www.opengl.org/registry/specs/ARB/texture_rg.txt |
|
s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_rg")); |
|
s_supportsARGB32 = true; |
|
s_supportsUnpack = true; |
|
} else { |
|
s_supportsFramebufferObjects = true; |
|
s_supportsTextureStorage = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_storage")); |
|
s_supportsTextureSwizzle = hasGLVersion(3, 0); |
|
// see https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt |
|
s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_rg")); |
|
|
|
// QImage::Format_ARGB32_Premultiplied is a packed-pixel format, so it's only |
|
// equivalent to GL_BGRA/GL_UNSIGNED_BYTE on little-endian systems. |
|
s_supportsARGB32 = QSysInfo::ByteOrder == QSysInfo::LittleEndian && |
|
hasGLExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888")); |
|
|
|
s_supportsUnpack = hasGLExtension(QByteArrayLiteral("GL_EXT_unpack_subimage")); |
|
} |
|
} |
|
|
|
void GLTexturePrivate::cleanup() |
|
{ |
|
s_supportsFramebufferObjects = false; |
|
s_supportsARGB32 = false; |
|
} |
|
|
|
bool GLTexture::isNull() const |
|
{ |
|
Q_D(const GLTexture); |
|
return GL_NONE == d->m_texture; |
|
} |
|
|
|
QSize GLTexture::size() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_size; |
|
} |
|
|
|
void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src) |
|
{ |
|
if (image.isNull() || isNull()) |
|
return; |
|
|
|
Q_D(GLTexture); |
|
|
|
bool useUnpack = !src.isNull() && d->s_supportsUnpack && d->s_supportsARGB32 && image.format() == QImage::Format_ARGB32_Premultiplied; |
|
|
|
int width = image.width(); |
|
int height = image.height(); |
|
QImage tmpImage; |
|
|
|
if (!src.isNull()) { |
|
if (useUnpack) { |
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.width()); |
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, src.x()); |
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, src.y()); |
|
} else { |
|
tmpImage = image.copy(src); |
|
} |
|
width = src.width(); |
|
height = src.height(); |
|
} |
|
|
|
const QImage &img = tmpImage.isNull() ? image : tmpImage; |
|
|
|
bind(); |
|
|
|
if (!GLPlatform::instance()->isGLES()) { |
|
const QImage im = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); |
|
glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, |
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, im.bits()); |
|
} else { |
|
if (d->s_supportsARGB32) { |
|
const QImage im = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); |
|
glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, |
|
GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); |
|
} else { |
|
const QImage im = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied); |
|
glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, |
|
GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); |
|
} |
|
} |
|
|
|
unbind(); |
|
|
|
if (useUnpack) { |
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
|
} |
|
} |
|
|
|
void GLTexture::discard() |
|
{ |
|
d_ptr = new GLTexturePrivate(); |
|
} |
|
|
|
void GLTexture::bind() |
|
{ |
|
Q_D(GLTexture); |
|
|
|
glBindTexture(d->m_target, d->m_texture); |
|
|
|
if (d->m_markedDirty) { |
|
d->onDamage(); |
|
} |
|
if (d->m_filterChanged) { |
|
GLenum minFilter = GL_NEAREST; |
|
GLenum magFilter = GL_NEAREST; |
|
|
|
switch (d->m_filter) { |
|
case GL_NEAREST: |
|
minFilter = magFilter = GL_NEAREST; |
|
break; |
|
|
|
case GL_LINEAR: |
|
minFilter = magFilter = GL_LINEAR; |
|
break; |
|
|
|
case GL_NEAREST_MIPMAP_NEAREST: |
|
case GL_NEAREST_MIPMAP_LINEAR: |
|
magFilter = GL_NEAREST; |
|
minFilter = d->m_canUseMipmaps ? d->m_filter : GL_NEAREST; |
|
break; |
|
|
|
case GL_LINEAR_MIPMAP_NEAREST: |
|
case GL_LINEAR_MIPMAP_LINEAR: |
|
magFilter = GL_LINEAR; |
|
minFilter = d->m_canUseMipmaps ? d->m_filter : GL_LINEAR; |
|
break; |
|
} |
|
|
|
glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, minFilter); |
|
glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, magFilter); |
|
|
|
d->m_filterChanged = false; |
|
} |
|
if (d->m_wrapModeChanged) { |
|
glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, d->m_wrapMode); |
|
glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, d->m_wrapMode); |
|
d->m_wrapModeChanged = false; |
|
} |
|
} |
|
|
|
void GLTexture::generateMipmaps() |
|
{ |
|
Q_D(GLTexture); |
|
|
|
if (d->m_canUseMipmaps && d->s_supportsFramebufferObjects) |
|
glGenerateMipmap(d->m_target); |
|
} |
|
|
|
void GLTexture::unbind() |
|
{ |
|
Q_D(GLTexture); |
|
glBindTexture(d->m_target, 0); |
|
} |
|
|
|
void GLTexture::render(QRegion region, const QRect& rect, bool hardwareClipping) |
|
{ |
|
Q_D(GLTexture); |
|
if (rect.isEmpty()) |
|
return; // nothing to paint and m_vbo is likely nullptr and d->m_cachedSize empty as well, #337090 |
|
if (rect.size() != d->m_cachedSize) { |
|
d->m_cachedSize = rect.size(); |
|
QRect r(rect); |
|
r.moveTo(0, 0); |
|
if (!d->m_vbo) { |
|
d->m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static); |
|
} |
|
|
|
const float verts[ 4 * 2 ] = { |
|
// NOTICE: r.x/y could be replaced by "0", but that would make it unreadable... |
|
static_cast<float>(r.x()), static_cast<float>(r.y()), |
|
static_cast<float>(r.x()), static_cast<float>(r.y() + rect.height()), |
|
static_cast<float>(r.x() + rect.width()), static_cast<float>(r.y()), |
|
static_cast<float>(r.x() + rect.width()), static_cast<float>(r.y() + rect.height()) |
|
}; |
|
|
|
const float texWidth = (target() == GL_TEXTURE_RECTANGLE_ARB) ? width() : 1.0f; |
|
const float texHeight = (target() == GL_TEXTURE_RECTANGLE_ARB) ? height() : 1.0f; |
|
|
|
const float texcoords[ 4 * 2 ] = { |
|
0.0f, d->m_yInverted ? 0.0f : texHeight, // y needs to be swapped (normalized coords) |
|
0.0f, d->m_yInverted ? texHeight : 0.0f, |
|
texWidth, d->m_yInverted ? 0.0f : texHeight, |
|
texWidth, d->m_yInverted ? texHeight : 0.0f |
|
}; |
|
|
|
d->m_vbo->setData(4, 2, verts, texcoords); |
|
} |
|
QMatrix4x4 translation; |
|
translation.translate(rect.x(), rect.y()); |
|
GLShader *shader = ShaderManager::instance()->getBoundShader(); |
|
shader->setUniform(GLShader::Offset, QVector2D(rect.x(), rect.y())); |
|
shader->setUniform(GLShader::WindowTransformation, translation); |
|
d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping); |
|
shader->setUniform(GLShader::WindowTransformation, QMatrix4x4()); |
|
} |
|
|
|
GLuint GLTexture::texture() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_texture; |
|
} |
|
|
|
GLenum GLTexture::target() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_target; |
|
} |
|
|
|
GLenum GLTexture::filter() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_filter; |
|
} |
|
|
|
GLenum GLTexture::internalFormat() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_internalFormat; |
|
} |
|
|
|
void GLTexture::clear() |
|
{ |
|
Q_D(GLTexture); |
|
if (!GLTexturePrivate::s_fbo && GLRenderTarget::supported() && |
|
GLPlatform::instance()->driver() != Driver_Catalyst) // fail. -> bug #323065 |
|
glGenFramebuffers(1, &GLTexturePrivate::s_fbo); |
|
|
|
if (GLTexturePrivate::s_fbo) { |
|
// Clear the texture |
|
glBindFramebuffer(GL_FRAMEBUFFER, GLTexturePrivate::s_fbo); |
|
glClearColor(0, 0, 0, 0); |
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, d->m_texture, 0); |
|
glClear(GL_COLOR_BUFFER_BIT); |
|
glBindFramebuffer(GL_FRAMEBUFFER, 0); |
|
} else { |
|
if (const int size = width()*height()) { |
|
uint32_t *buffer = new uint32_t[size]; |
|
memset(buffer, 0, size*sizeof(uint32_t)); |
|
bind(); |
|
if (!GLPlatform::instance()->isGLES()) { |
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(), |
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); |
|
} else { |
|
const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA; |
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(), |
|
format, GL_UNSIGNED_BYTE, buffer); |
|
} |
|
unbind(); |
|
delete[] buffer; |
|
} |
|
} |
|
} |
|
|
|
bool GLTexture::isDirty() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_markedDirty; |
|
} |
|
|
|
void GLTexture::setFilter(GLenum filter) |
|
{ |
|
Q_D(GLTexture); |
|
if (filter != d->m_filter) { |
|
d->m_filter = filter; |
|
d->m_filterChanged = true; |
|
} |
|
} |
|
|
|
void GLTexture::setWrapMode(GLenum mode) |
|
{ |
|
Q_D(GLTexture); |
|
if (mode != d->m_wrapMode) { |
|
d->m_wrapMode = mode; |
|
d->m_wrapModeChanged=true; |
|
} |
|
} |
|
|
|
void GLTexturePrivate::onDamage() |
|
{ |
|
// No-op |
|
} |
|
|
|
void GLTexture::setDirty() |
|
{ |
|
Q_D(GLTexture); |
|
d->m_markedDirty = true; |
|
} |
|
|
|
void GLTexturePrivate::updateMatrix() |
|
{ |
|
m_matrix[NormalizedCoordinates].setToIdentity(); |
|
m_matrix[UnnormalizedCoordinates].setToIdentity(); |
|
|
|
if (m_target == GL_TEXTURE_RECTANGLE_ARB) |
|
m_matrix[NormalizedCoordinates].scale(m_size.width(), m_size.height()); |
|
else |
|
m_matrix[UnnormalizedCoordinates].scale(1.0 / m_size.width(), 1.0 / m_size.height()); |
|
|
|
if (!m_yInverted) { |
|
m_matrix[NormalizedCoordinates].translate(0.0, 1.0); |
|
m_matrix[NormalizedCoordinates].scale(1.0, -1.0); |
|
|
|
m_matrix[UnnormalizedCoordinates].translate(0.0, m_size.height()); |
|
m_matrix[UnnormalizedCoordinates].scale(1.0, -1.0); |
|
} |
|
} |
|
|
|
bool GLTexture::isYInverted() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_yInverted; |
|
} |
|
|
|
void GLTexture::setYInverted(bool inverted) |
|
{ |
|
Q_D(GLTexture); |
|
d->m_yInverted = inverted; |
|
d->updateMatrix(); |
|
} |
|
|
|
void GLTexture::setSwizzle(GLenum red, GLenum green, GLenum blue, GLenum alpha) |
|
{ |
|
Q_D(GLTexture); |
|
|
|
if (!GLPlatform::instance()->isGLES()) { |
|
const GLuint swizzle[] = { red, green, blue, alpha }; |
|
glTexParameteriv(d->m_target, GL_TEXTURE_SWIZZLE_RGBA, (const GLint *) swizzle); |
|
} else { |
|
glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_R, red); |
|
glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_G, green); |
|
glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_B, blue); |
|
glTexParameteri(d->m_target, GL_TEXTURE_SWIZZLE_A, alpha); |
|
} |
|
} |
|
|
|
int GLTexture::width() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_size.width(); |
|
} |
|
|
|
int GLTexture::height() const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_size.height(); |
|
} |
|
|
|
QMatrix4x4 GLTexture::matrix(TextureCoordinateType type) const |
|
{ |
|
Q_D(const GLTexture); |
|
return d->m_matrix[type]; |
|
} |
|
|
|
bool GLTexture::framebufferObjectSupported() |
|
{ |
|
return GLTexturePrivate::s_supportsFramebufferObjects; |
|
} |
|
|
|
bool GLTexture::supportsSwizzle() |
|
{ |
|
return GLTexturePrivate::s_supportsTextureSwizzle; |
|
} |
|
|
|
bool GLTexture::supportsFormatRG() |
|
{ |
|
return GLTexturePrivate::s_supportsTextureFormatRG; |
|
} |
|
|
|
} // namespace KWin
|
|
|