1fb9f6f13a
The main advantage of SPDX license identifiers over the traditional license headers is that it's more difficult to overlook inappropriate licenses for kwin, for example GPL 3. We also don't have to copy a lot of boilerplate text. In order to create this change, I ran licensedigger -r -c from the toplevel source directory.
682 lines
22 KiB
C++
682 lines
22 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
|
|
SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
|
|
SPDX-FileCopyrightText: 2012 Philipp Knechtges <philipp-dev@knechtges.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*********************************************************************/
|
|
|
|
#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
|
|
{ 0, 0, 0 }, // QImage::Format_ARGB6666_Premultiplied
|
|
{ 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
|
|
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // QImage::Format_Alpha8
|
|
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // QImage::Format_Grayscale8
|
|
};
|
|
|
|
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, bool needsMutability)
|
|
: 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_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 && !needsMutability) {
|
|
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, bool needsMutability)
|
|
: GLTexture(internalFormat, size.width(), size.height(), levels, needsMutability)
|
|
{
|
|
}
|
|
|
|
GLTexture::GLTexture(GLuint textureId, GLenum internalFormat, const QSize &size, int levels)
|
|
: d_ptr(new GLTexturePrivate())
|
|
{
|
|
Q_D(GLTexture);
|
|
d->m_foreign = true;
|
|
d->m_texture = textureId;
|
|
d->m_target = GL_TEXTURE_2D;
|
|
d->m_scale.setWidth(1.0 / size.width());
|
|
d->m_scale.setHeight(1.0 / size.height());
|
|
d->m_size = size;
|
|
d->m_canUseMipmaps = levels > 1;
|
|
d->m_mipLevels = levels;
|
|
d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
|
|
d->m_internalFormat = internalFormat;
|
|
|
|
d->updateMatrix();
|
|
}
|
|
|
|
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_markedDirty(false)
|
|
, m_filterChanged(true)
|
|
, m_wrapModeChanged(false)
|
|
, m_immutable(false)
|
|
, m_foreign(false)
|
|
, m_mipLevels(1)
|
|
, m_unnormalizeActive(0)
|
|
, m_normalizeActive(0)
|
|
, m_vbo(nullptr)
|
|
{
|
|
++s_textureObjectCounter;
|
|
}
|
|
|
|
GLTexturePrivate::~GLTexturePrivate()
|
|
{
|
|
delete m_vbo;
|
|
if (m_texture != 0 && !m_foreign) {
|
|
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);
|
|
Q_ASSERT(!d->m_foreign);
|
|
|
|
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(const QRegion ®ion, 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);
|
|
}
|
|
d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping);
|
|
}
|
|
|
|
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);
|
|
Q_ASSERT(!d->m_foreign);
|
|
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
|
|
GLuint previousFramebuffer = 0;
|
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&previousFramebuffer));
|
|
if (GLTexturePrivate::s_fbo != previousFramebuffer)
|
|
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);
|
|
if (GLTexturePrivate::s_fbo != previousFramebuffer)
|
|
glBindFramebuffer(GL_FRAMEBUFFER, previousFramebuffer);
|
|
} 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);
|
|
if (d->m_yInverted == inverted)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
QImage GLTexture::toImage() const
|
|
{
|
|
QImage ret(size(), QImage::Format_RGBA8888_Premultiplied);
|
|
glGetTextureImage(texture(), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ret.sizeInBytes(), ret.bits());
|
|
return ret;
|
|
}
|
|
|
|
} // namespace KWin
|