kwingltexture.cpp 21 KB
Newer Older
1
2
3
4
5
6
/********************************************************************
 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>
7
Copyright (C) 2012 Philipp Knechtges <philipp-dev@knechtges.com>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

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"

29
30
#include "kwingltexture_p.h"

31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <QPixmap>
#include <QImage>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include <QMatrix4x4>

namespace KWin
{

//****************************************
// GLTexture
//****************************************

45
bool GLTexturePrivate::s_supportsFramebufferObjects = false;
46
bool GLTexturePrivate::s_supportsARGB32 = false;
47
bool GLTexturePrivate::s_supportsUnpack = false;
48
bool GLTexturePrivate::s_supportsTextureStorage = false;
Fredrik Höglund's avatar
Fredrik Höglund committed
49
bool GLTexturePrivate::s_supportsTextureSwizzle = false;
50
bool GLTexturePrivate::s_supportsTextureFormatRG = false;
51
52
53
uint GLTexturePrivate::s_textureObjectCounter = 0;
uint GLTexturePrivate::s_fbo = 0;

54
55

GLTexture::GLTexture()
56
57
58
59
60
61
62
63
64
65
66
    : d_ptr(new GLTexturePrivate())
{
}

GLTexture::GLTexture(GLTexturePrivate& dd)
    : d_ptr(&dd)
{
}

GLTexture::GLTexture(const GLTexture& tex)
    : d_ptr(tex.d_ptr)
67
68
69
70
{
}

GLTexture::GLTexture(const QImage& image, GLenum target)
71
    : d_ptr(new GLTexturePrivate())
72
{
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
    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;
90
91
    d->m_canUseMipmaps = false;
    d->m_mipLevels = 1;
92
93
94
95
96
97
98

    d->updateMatrix();

    glGenTextures(1, &d->m_texture);
    bind();

    if (!GLPlatform::instance()->isGLES()) {
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
        // 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
Alex Nemeth's avatar
Alex Nemeth committed
116
            { 0,           0,       0                              }, // QImage::Format_ARGB6666_Premultiplied
117
118
119
120
121
122
123
124
125
126
127
128
            { 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
Alex Nemeth's avatar
Alex Nemeth committed
129
130
            { GL_R8,       GL_RED,  GL_UNSIGNED_BYTE               }, // QImage::Format_Alpha8
            { GL_R8,       GL_RED,  GL_UNSIGNED_BYTE               }, // QImage::Format_Grayscale8
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
        };

        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;
154
155

        if (d->s_supportsTextureStorage) {
156
            glTexStorage2D(d->m_target, 1, internalFormat, im.width(), im.height());
157
            glTexSubImage2D(d->m_target, 0, 0, 0, im.width(), im.height(),
158
                            format, type, im.bits());
159
160
161
            d->m_immutable = true;
        } else {
            glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, d->m_mipLevels - 1);
162
163
            glTexImage2D(d->m_target, 0, internalFormat, im.width(), im.height(), 0,
                         format, type, im.bits());
164
        }
165
    } else {
166
167
        d->m_internalFormat = GL_RGBA8;

168
169
170
171
172
173
174
175
176
177
178
179
180
        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);
181
182
183
}

GLTexture::GLTexture(const QPixmap& pixmap, GLenum target)
184
    : GLTexture(pixmap.toImage(), target)
185
186
187
188
{
}

GLTexture::GLTexture(const QString& fileName)
189
     : GLTexture(QImage(fileName))
190
191
192
{
}

193
GLTexture::GLTexture(GLenum internalFormat, int width, int height, int levels)
194
     : d_ptr(new GLTexturePrivate())
195
{
196
    Q_D(GLTexture);
197

198
199
200
201
    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);
202
203
204
    d->m_canUseMipmaps = levels > 1;
    d->m_mipLevels = levels;
    d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
205

206
    d->updateMatrix();
207

208
209
    glGenTextures(1, &d->m_texture);
    bind();
210

211
    if (!GLPlatform::instance()->isGLES()) {
212
        if (d->s_supportsTextureStorage) {
213
            glTexStorage2D(d->m_target, levels, internalFormat, width, height);
214
215
216
            d->m_immutable = true;
        } else {
            glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, levels - 1);
217
            glTexImage2D(d->m_target, 0, internalFormat, width, height, 0,
218
219
                         GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
        }
220
        d->m_internalFormat = internalFormat;
221
    } else {
222
223
224
225
226
227
        // 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);
228
229
230
231

        // 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;
232
    }
233
234

    unbind();
235
236
}

237
238
GLTexture::GLTexture(GLenum internalFormat, const QSize &size, int levels)
    : GLTexture(internalFormat, size.width(), size.height(), levels)
239
240
241
{
}

242
243
244
245
GLTexture::~GLTexture()
{
}

246
GLTexture& GLTexture::operator = (const GLTexture& tex)
247
{
248
249
250
251
252
    d_ptr = tex.d_ptr;
    return *this;
}

GLTexturePrivate::GLTexturePrivate()
Nick Shaforostoff's avatar
Nick Shaforostoff committed
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
 : 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_mipLevels(1)
 , m_unnormalizeActive(0)
 , m_normalizeActive(0)
 , m_vbo(nullptr)
268
{
269
    ++s_textureObjectCounter;
270
271
}

272
273
GLTexturePrivate::~GLTexturePrivate()
{
Nick Shaforostoff's avatar
Nick Shaforostoff committed
274
    delete m_vbo;
275
276
277
    if (m_texture != 0) {
        glDeleteTextures(1, &m_texture);
    }
278
279
280
281
282
    // Delete the FBO if this is the last Texture
    if (--s_textureObjectCounter == 0 && s_fbo) {
        glDeleteFramebuffers(1, &s_fbo);
        s_fbo = 0;
    }
283
284
285
}

void GLTexturePrivate::initStatic()
286
{
287
    if (!GLPlatform::instance()->isGLES()) {
288
289
        s_supportsFramebufferObjects = hasGLVersion(3, 0) ||
            hasGLExtension("GL_ARB_framebuffer_object") || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object"));
290
        s_supportsTextureStorage = hasGLVersion(4, 2) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_storage"));
Fredrik Höglund's avatar
Fredrik Höglund committed
291
        s_supportsTextureSwizzle = hasGLVersion(3, 3) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_swizzle"));
292
293
        // see https://www.opengl.org/registry/specs/ARB/texture_rg.txt
        s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_texture_rg"));
294
        s_supportsARGB32 = true;
295
        s_supportsUnpack = true;
296
    } else {
297
        s_supportsFramebufferObjects = true;
298
        s_supportsTextureStorage = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_storage"));
Fredrik Höglund's avatar
Fredrik Höglund committed
299
        s_supportsTextureSwizzle = hasGLVersion(3, 0);
300
301
        // see https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
        s_supportsTextureFormatRG = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_texture_rg"));
302
303
304
305
306
307

        // 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"));

308
        s_supportsUnpack = hasGLExtension(QByteArrayLiteral("GL_EXT_unpack_subimage"));
309
    }
310
311
}

312
313
void GLTexturePrivate::cleanup()
{
314
    s_supportsFramebufferObjects = false;
315
    s_supportsARGB32 = false;
316
317
}

318
319
bool GLTexture::isNull() const
{
320
    Q_D(const GLTexture);
321
    return GL_NONE == d->m_texture;
322
323
324
325
}

QSize GLTexture::size() const
{
326
327
    Q_D(const GLTexture);
    return d->m_size;
328
329
}

330
331
332
333
334
335
void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src)
{
    if (image.isNull() || isNull())
        return;

    Q_D(GLTexture);
336

337
338
    bool useUnpack = !src.isNull() && d->s_supportsUnpack && d->s_supportsARGB32 && image.format() == QImage::Format_ARGB32_Premultiplied;

339
340
341
    int width = image.width();
    int height = image.height();
    QImage tmpImage;
342

343
    if (!src.isNull()) {
344
        if (useUnpack) {
345
346
347
348
349
350
351
352
353
            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();
    }
354
355

    const QImage &img = tmpImage.isNull() ? image : tmpImage;
356
357

    bind();
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

    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());
        }
    }
374

375
    unbind();
376
377

    if (useUnpack) {
378
379
380
381
382
383
        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
        glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
    }
}

384
385
void GLTexture::discard()
{
386
    d_ptr = new GLTexturePrivate();
387
388
}

389
390
391
void GLTexture::bind()
{
    Q_D(GLTexture);
392
393
394

    glBindTexture(d->m_target, d->m_texture);

395
396
397
398
    if (d->m_markedDirty) {
        d->onDamage();
    }
    if (d->m_filterChanged) {
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
        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;
422
        }
423
424
425
426

        glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, magFilter);

427
        d->m_filterChanged = false;
428
429
430
431
    }
    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);
432
        d->m_wrapModeChanged = false;
433
    }
434
435
}

436
437
438
439
440
441
442
443
void GLTexture::generateMipmaps()
{
    Q_D(GLTexture);

    if (d->m_canUseMipmaps && d->s_supportsFramebufferObjects)
        glGenerateMipmap(d->m_target);
}

444
445
446
void GLTexture::unbind()
{
    Q_D(GLTexture);
447
    glBindTexture(d->m_target, 0);
448
449
}

450
void GLTexture::render(QRegion region, const QRect& rect, bool hardwareClipping)
451
{
452
    Q_D(GLTexture);
453
454
    if (rect.isEmpty())
        return; // nothing to paint and m_vbo is likely nullptr and d->m_cachedSize empty as well, #337090
455
456
    if (rect.size() != d->m_cachedSize) {
        d->m_cachedSize = rect.size();
457
458
        QRect r(rect);
        r.moveTo(0, 0);
459
460
        if (!d->m_vbo) {
            d->m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static);
461
        }
462

463
464
        const float verts[ 4 * 2 ] = {
            // NOTICE: r.x/y could be replaced by "0", but that would make it unreadable...
465
466
467
468
            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())
469
        };
470

471
472
        const float texWidth = (target() == GL_TEXTURE_RECTANGLE_ARB) ? width() : 1.0f;
        const float texHeight = (target() == GL_TEXTURE_RECTANGLE_ARB) ? height() : 1.0f;
473

474
        const float texcoords[ 4 * 2 ] = {
475
476
477
478
            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
479
        };
480

481
        d->m_vbo->setData(4, 2, verts, texcoords);
482
    }
483
    d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping);
484
485
486
487
}

GLuint GLTexture::texture() const
{
488
489
    Q_D(const GLTexture);
    return d->m_texture;
490
491
492
493
}

GLenum GLTexture::target() const
{
494
495
    Q_D(const GLTexture);
    return d->m_target;
496
497
498
499
}

GLenum GLTexture::filter() const
{
500
501
    Q_D(const GLTexture);
    return d->m_filter;
502
503
}

504
505
506
507
508
509
GLenum GLTexture::internalFormat() const
{
    Q_D(const GLTexture);
    return d->m_internalFormat;
}

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
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();
529
530
531
532
533
534
535
536
            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);
            }
537
538
539
540
541
542
            unbind();
            delete[] buffer;
        }
    }
}

543
544
bool GLTexture::isDirty() const
{
545
    Q_D(const GLTexture);
546
    return d->m_markedDirty;
547
548
549
550
}

void GLTexture::setFilter(GLenum filter)
{
551
    Q_D(GLTexture);
552
553
554
555
    if (filter != d->m_filter) {
        d->m_filter = filter;
        d->m_filterChanged = true;
    }
556
557
558
559
}

void GLTexture::setWrapMode(GLenum mode)
{
560
    Q_D(GLTexture);
561
562
563
564
    if (mode != d->m_wrapMode) {
        d->m_wrapMode = mode;
        d->m_wrapModeChanged=true;
    }
565
566
}

567
void GLTexturePrivate::onDamage()
568
{
569
    // No-op
570
571
}

572
void GLTexture::setDirty()
573
{
574
575
    Q_D(GLTexture);
    d->m_markedDirty = true;
576
577
}

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
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);
    }
}

Martin Flöser's avatar
Martin Flöser committed
597
598
bool GLTexture::isYInverted() const
{
599
600
601
602
603
604
605
606
    Q_D(const GLTexture);
    return d->m_yInverted;
}

void GLTexture::setYInverted(bool inverted)
{
    Q_D(GLTexture);
    d->m_yInverted = inverted;
607
    d->updateMatrix();
608
609
}

Fredrik Höglund's avatar
Fredrik Höglund committed
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
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);
    }
}

625
626
627
628
629
630
631
632
633
634
635
636
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();
}

637
638
639
640
641
642
QMatrix4x4 GLTexture::matrix(TextureCoordinateType type) const
{
    Q_D(const GLTexture);
    return d->m_matrix[type];
}

643
644
bool GLTexture::framebufferObjectSupported()
{
645
    return GLTexturePrivate::s_supportsFramebufferObjects;
646
647
}

Fredrik Höglund's avatar
Fredrik Höglund committed
648
649
650
651
652
bool GLTexture::supportsSwizzle()
{
    return GLTexturePrivate::s_supportsTextureSwizzle;
}

653
654
655
656
657
bool GLTexture::supportsFormatRG()
{
    return GLTexturePrivate::s_supportsTextureFormatRG;
}

658
} // namespace KWin