~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/lanczosfilter.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 2010 by Fredrik Höglund <fredrik@kde.org>
 
6
Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
 
7
 
 
8
This program is free software; you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation; either version 2 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
This program is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*********************************************************************/
 
21
 
 
22
#include "lanczosfilter.h"
 
23
#include "effects.h"
 
24
 
 
25
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
26
#include <kwinglutils.h>
 
27
#include <kwinglplatform.h>
 
28
#endif
 
29
 
 
30
#include <kwineffects.h>
 
31
#include <KDE/KGlobalSettings>
 
32
 
 
33
#include <qmath.h>
 
34
#include <cmath>
 
35
 
 
36
namespace KWin
 
37
{
 
38
 
 
39
LanczosFilter::LanczosFilter(QObject* parent)
 
40
    : QObject(parent)
 
41
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
42
    , m_offscreenTex(0)
 
43
    , m_offscreenTarget(0)
 
44
    , m_shader(0)
 
45
#endif
 
46
    , m_inited(false)
 
47
{
 
48
}
 
49
 
 
50
LanczosFilter::~LanczosFilter()
 
51
{
 
52
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
53
    delete m_offscreenTarget;
 
54
    delete m_offscreenTex;
 
55
#endif
 
56
}
 
57
 
 
58
void LanczosFilter::init()
 
59
{
 
60
    if (m_inited)
 
61
        return;
 
62
    m_inited = true;
 
63
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
64
 
 
65
    KSharedConfigPtr config = KSharedConfig::openConfig("kwinrc");
 
66
 
 
67
    if (config->group("Compositing").readEntry("GLTextureFilter", 2) != 2)
 
68
        return; // disabled by config
 
69
 
 
70
    // The lanczos filter is reported to be broken with the Intel driver and Mesa 7.10
 
71
    GLPlatform *gl = GLPlatform::instance();
 
72
    if (gl->driver() == Driver_Intel && gl->mesaVersion() >= kVersionNumber(7, 10))
 
73
        return;
 
74
 
 
75
    m_shader = new LanczosShader(this);
 
76
    if (!m_shader->init()) {
 
77
        delete m_shader;
 
78
        m_shader = 0;
 
79
    }
 
80
#endif
 
81
}
 
82
 
 
83
 
 
84
void LanczosFilter::updateOffscreenSurfaces()
 
85
{
 
86
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
87
    int w = displayWidth();
 
88
    int h = displayHeight();
 
89
    if (!GLTexture::NPOTTextureSupported()) {
 
90
        w = nearestPowerOfTwo(w);
 
91
        h = nearestPowerOfTwo(h);
 
92
    }
 
93
    if (!m_offscreenTex || m_offscreenTex->width() != w || m_offscreenTex->height() != h) {
 
94
        if (m_offscreenTex) {
 
95
            delete m_offscreenTex;
 
96
            delete m_offscreenTarget;
 
97
        }
 
98
        m_offscreenTex = new GLTexture(w, h);
 
99
        m_offscreenTex->setFilter(GL_LINEAR);
 
100
        m_offscreenTex->setWrapMode(GL_CLAMP_TO_EDGE);
 
101
        m_offscreenTarget = new GLRenderTarget(m_offscreenTex);
 
102
    }
 
103
#endif
 
104
}
 
105
 
 
106
static float sinc(float x)
 
107
{
 
108
    return std::sin(x * M_PI) / (x * M_PI);
 
109
}
 
110
 
 
111
static float lanczos(float x, float a)
 
112
{
 
113
    if (qFuzzyCompare(x + 1.0, 1.0))
 
114
        return 1.0;
 
115
 
 
116
    if (qAbs(x) >= a)
 
117
        return 0.0;
 
118
 
 
119
    return sinc(x) * sinc(x / a);
 
120
}
 
121
 
 
122
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
123
void LanczosShader::createKernel(float delta, int *size)
 
124
{
 
125
    const float a = 2.0;
 
126
 
 
127
    // The two outermost samples always fall at points where the lanczos
 
128
    // function returns 0, so we'll skip them.
 
129
    const int sampleCount = qBound(3, qCeil(delta * a) * 2 + 1 - 2, 29);
 
130
    const int center = sampleCount / 2;
 
131
    const int kernelSize = center + 1;
 
132
    const float factor = 1.0 / delta;
 
133
 
 
134
    QVector<float> values(kernelSize);
 
135
    float sum = 0;
 
136
 
 
137
    for (int i = 0; i < kernelSize; i++) {
 
138
        const float val = lanczos(i * factor, a);
 
139
        sum += i > 0 ? val * 2 : val;
 
140
        values[i] = val;
 
141
    }
 
142
 
 
143
    memset(m_kernel, 0, 16 * sizeof(QVector4D));
 
144
 
 
145
    // Normalize the kernel
 
146
    for (int i = 0; i < kernelSize; i++) {
 
147
        const float val = values[i] / sum;
 
148
        m_kernel[i] = QVector4D(val, val, val, val);
 
149
    }
 
150
 
 
151
    *size = kernelSize;
 
152
}
 
153
 
 
154
void LanczosShader::createOffsets(int count, float width, Qt::Orientation direction)
 
155
{
 
156
    memset(m_offsets, 0, 16 * sizeof(QVector2D));
 
157
    for (int i = 0; i < count; i++) {
 
158
        m_offsets[i] = (direction == Qt::Horizontal) ?
 
159
                       QVector2D(i / width, 0) : QVector2D(0, i / width);
 
160
    }
 
161
}
 
162
#endif
 
163
 
 
164
void LanczosFilter::performPaint(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
 
165
{
 
166
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
167
    if (effects->compositingType() == KWin::OpenGLCompositing && (data.xScale < 0.9 || data.yScale < 0.9) &&
 
168
            KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
 
169
        if (!m_inited)
 
170
            init();
 
171
        const QRect screenRect = Workspace::self()->clientArea(ScreenArea, w->screen(), w->desktop());
 
172
        // window geometry may not be bigger than screen geometry to fit into the FBO
 
173
        if (m_shader && w->width() <= screenRect.width() && w->height() <= screenRect.height()) {
 
174
            double left = 0;
 
175
            double top = 0;
 
176
            double right = w->width();
 
177
            double bottom = w->height();
 
178
            foreach (const WindowQuad & quad, data.quads) {
 
179
                // we need this loop to include the decoration padding
 
180
                left   = qMin(left, quad.left());
 
181
                top    = qMin(top, quad.top());
 
182
                right  = qMax(right, quad.right());
 
183
                bottom = qMax(bottom, quad.bottom());
 
184
            }
 
185
            double width = right - left;
 
186
            double height = bottom - top;
 
187
            if (width > screenRect.width() || height > screenRect.height()) {
 
188
                // window with padding does not fit into the framebuffer
 
189
                // so cut of the shadow
 
190
                left = 0;
 
191
                top = 0;
 
192
                width = w->width();
 
193
                height = w->height();
 
194
            }
 
195
            int tx = data.xTranslate + w->x() + left * data.xScale;
 
196
            int ty = data.yTranslate + w->y() + top * data.yScale;
 
197
            int tw = width * data.xScale;
 
198
            int th = height * data.yScale;
 
199
            const QRect textureRect(tx, ty, tw, th);
 
200
 
 
201
            int sw = width;
 
202
            int sh = height;
 
203
 
 
204
            GLTexture *cachedTexture = static_cast< GLTexture*>(w->data(LanczosCacheRole).value<void*>());
 
205
            if (cachedTexture) {
 
206
                if (cachedTexture->width() == tw && cachedTexture->height() == th) {
 
207
                    cachedTexture->bind();
 
208
                    if (ShaderManager::instance()->isValid()) {
 
209
                        glEnable(GL_BLEND);
 
210
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
211
 
 
212
                        const float rgb = data.brightness * data.opacity;
 
213
                        const float a = data.opacity;
 
214
 
 
215
                        GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
 
216
                        shader->setUniform(GLShader::Offset, QVector2D(0, 0));
 
217
                        shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a));
 
218
                        shader->setUniform(GLShader::Saturation, data.saturation);
 
219
                        shader->setUniform(GLShader::AlphaToOne, 0);
 
220
 
 
221
                        cachedTexture->render(textureRect, textureRect);
 
222
 
 
223
                        ShaderManager::instance()->popShader();
 
224
                        glDisable(GL_BLEND);
 
225
                    } else {
 
226
                        prepareRenderStates(cachedTexture, data.opacity, data.brightness, data.saturation);
 
227
                        cachedTexture->render(textureRect, textureRect);
 
228
                        restoreRenderStates(cachedTexture, data.opacity, data.brightness, data.saturation);
 
229
                    }
 
230
                    cachedTexture->unbind();
 
231
                    m_timer.start(5000, this);
 
232
                    return;
 
233
                } else {
 
234
                    // offscreen texture not matching - delete
 
235
                    delete cachedTexture;
 
236
                    cachedTexture = 0;
 
237
                    w->setData(LanczosCacheRole, QVariant());
 
238
                }
 
239
            }
 
240
 
 
241
            WindowPaintData thumbData = data;
 
242
            thumbData.xScale = 1.0;
 
243
            thumbData.yScale = 1.0;
 
244
            thumbData.xTranslate = -w->x() - left;
 
245
            thumbData.yTranslate = -w->y() - top;
 
246
            thumbData.brightness = 1.0;
 
247
            thumbData.opacity = 1.0;
 
248
            thumbData.saturation = 1.0;
 
249
 
 
250
            // Bind the offscreen FBO and draw the window on it unscaled
 
251
            updateOffscreenSurfaces();
 
252
            GLRenderTarget::pushRenderTarget(m_offscreenTarget);
 
253
 
 
254
            glClearColor(0.0, 0.0, 0.0, 0.0);
 
255
            glClear(GL_COLOR_BUFFER_BIT);
 
256
            w->sceneWindow()->performPaint(mask, infiniteRegion(), thumbData);
 
257
 
 
258
            // Create a scratch texture and copy the rendered window into it
 
259
            GLTexture tex(sw, sh);
 
260
            tex.setFilter(GL_LINEAR);
 
261
            tex.setWrapMode(GL_CLAMP_TO_EDGE);
 
262
            tex.bind();
 
263
 
 
264
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, sw, sh);
 
265
 
 
266
            // Set up the shader for horizontal scaling
 
267
            float dx = sw / float(tw);
 
268
            int kernelSize;
 
269
            m_shader->createKernel(dx, &kernelSize);
 
270
            m_shader->createOffsets(kernelSize, sw, Qt::Horizontal);
 
271
 
 
272
            m_shader->bind();
 
273
            m_shader->setUniforms();
 
274
 
 
275
            // Draw the window back into the FBO, this time scaled horizontally
 
276
            glClear(GL_COLOR_BUFFER_BIT);
 
277
            QVector<float> verts;
 
278
            QVector<float> texCoords;
 
279
            verts.reserve(12);
 
280
            texCoords.reserve(12);
 
281
 
 
282
            texCoords << 1.0 << 0.0; verts << tw  << 0.0; // Top right
 
283
            texCoords << 0.0 << 0.0; verts << 0.0 << 0.0; // Top left
 
284
            texCoords << 0.0 << 1.0; verts << 0.0 << sh;  // Bottom left
 
285
            texCoords << 0.0 << 1.0; verts << 0.0 << sh;  // Bottom left
 
286
            texCoords << 1.0 << 1.0; verts << tw  << sh;  // Bottom right
 
287
            texCoords << 1.0 << 0.0; verts << tw  << 0.0; // Top right
 
288
            GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
 
289
            vbo->reset();
 
290
            vbo->setData(6, 2, verts.constData(), texCoords.constData());
 
291
            vbo->render(GL_TRIANGLES);
 
292
 
 
293
            // At this point we don't need the scratch texture anymore
 
294
            tex.unbind();
 
295
            tex.discard();
 
296
 
 
297
            // create scratch texture for second rendering pass
 
298
            GLTexture tex2(tw, sh);
 
299
            tex2.setFilter(GL_LINEAR);
 
300
            tex2.setWrapMode(GL_CLAMP_TO_EDGE);
 
301
            tex2.bind();
 
302
 
 
303
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - sh, tw, sh);
 
304
 
 
305
            // Set up the shader for vertical scaling
 
306
            float dy = sh / float(th);
 
307
            m_shader->createKernel(dy, &kernelSize);
 
308
            m_shader->createOffsets(kernelSize, m_offscreenTex->height(), Qt::Vertical);
 
309
            m_shader->setUniforms();
 
310
 
 
311
            // Now draw the horizontally scaled window in the FBO at the right
 
312
            // coordinates on the screen, while scaling it vertically and blending it.
 
313
            glClear(GL_COLOR_BUFFER_BIT);
 
314
 
 
315
            verts.clear();
 
316
 
 
317
            verts << tw  << 0.0; // Top right
 
318
            verts << 0.0 << 0.0; // Top left
 
319
            verts << 0.0 << th;  // Bottom left
 
320
            verts << 0.0 << th;  // Bottom left
 
321
            verts << tw  << th;  // Bottom right
 
322
            verts << tw  << 0.0; // Top right
 
323
            vbo->setData(6, 2, verts.constData(), texCoords.constData());
 
324
            vbo->render(GL_TRIANGLES);
 
325
 
 
326
            tex2.unbind();
 
327
            tex2.discard();
 
328
            m_shader->unbind();
 
329
 
 
330
            // create cache texture
 
331
            GLTexture *cache = new GLTexture(tw, th);
 
332
 
 
333
            cache->setFilter(GL_LINEAR);
 
334
            cache->setWrapMode(GL_CLAMP_TO_EDGE);
 
335
            cache->bind();
 
336
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, m_offscreenTex->height() - th, tw, th);
 
337
            GLRenderTarget::popRenderTarget();
 
338
 
 
339
            if (ShaderManager::instance()->isValid()) {
 
340
                glEnable(GL_BLEND);
 
341
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
342
 
 
343
                const float rgb = data.brightness * data.opacity;
 
344
                const float a = data.opacity;
 
345
 
 
346
                GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
 
347
                shader->setUniform(GLShader::Offset, QVector2D(0, 0));
 
348
                shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a));
 
349
                shader->setUniform(GLShader::Saturation, data.saturation);
 
350
                shader->setUniform(GLShader::AlphaToOne, 0);
 
351
 
 
352
                cache->render(textureRect, textureRect);
 
353
 
 
354
                ShaderManager::instance()->popShader();
 
355
                glDisable(GL_BLEND);
 
356
            } else {
 
357
                prepareRenderStates(cache, data.opacity, data.brightness, data.saturation);
 
358
                cache->render(textureRect, textureRect);
 
359
                restoreRenderStates(cache, data.opacity, data.brightness, data.saturation);
 
360
            }
 
361
 
 
362
            cache->unbind();
 
363
            w->setData(LanczosCacheRole, QVariant::fromValue(static_cast<void*>(cache)));
 
364
 
 
365
            // Delete the offscreen surface after 5 seconds
 
366
            m_timer.start(5000, this);
 
367
            return;
 
368
        }
 
369
    } // if ( effects->compositingType() == KWin::OpenGLCompositing )
 
370
#endif
 
371
    w->sceneWindow()->performPaint(mask, region, data);
 
372
} // End of function
 
373
 
 
374
void LanczosFilter::timerEvent(QTimerEvent *event)
 
375
{
 
376
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
377
    if (event->timerId() == m_timer.timerId()) {
 
378
        m_timer.stop();
 
379
 
 
380
        delete m_offscreenTarget;
 
381
        delete m_offscreenTex;
 
382
        m_offscreenTarget = 0;
 
383
        m_offscreenTex = 0;
 
384
        foreach (EffectWindow * w, effects->stackingOrder()) {
 
385
            QVariant cachedTextureVariant = w->data(LanczosCacheRole);
 
386
            if (cachedTextureVariant.isValid()) {
 
387
                GLTexture *cachedTexture = static_cast< GLTexture*>(cachedTextureVariant.value<void*>());
 
388
                delete cachedTexture;
 
389
                cachedTexture = 0;
 
390
                w->setData(LanczosCacheRole, QVariant());
 
391
            }
 
392
        }
 
393
    }
 
394
#endif
 
395
}
 
396
 
 
397
void LanczosFilter::prepareRenderStates(GLTexture* tex, double opacity, double brightness, double saturation)
 
398
{
 
399
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
400
#ifndef KWIN_HAVE_OPENGLES
 
401
    const bool alpha = true;
 
402
    // setup blending of transparent windows
 
403
    glPushAttrib(GL_ENABLE_BIT);
 
404
    glEnable(GL_BLEND);
 
405
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
406
    if (saturation != 1.0 && tex->saturationSupported()) {
 
407
        // First we need to get the color from [0; 1] range to [0.5; 1] range
 
408
        glActiveTexture(GL_TEXTURE0);
 
409
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
410
        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
 
411
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
 
412
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 
413
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
 
414
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
415
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
 
416
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
 
417
        const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5};
 
418
        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant);
 
419
        tex->bind();
 
420
 
 
421
        // Then we take dot product of the result of previous pass and
 
422
        //  saturation_constant. This gives us completely unsaturated
 
423
        //  (greyscale) image
 
424
        // Note that both operands have to be in range [0.5; 1] since opengl
 
425
        //  automatically substracts 0.5 from them
 
426
        glActiveTexture(GL_TEXTURE1);
 
427
        float saturation_constant[] = { 0.5 + 0.5 * 0.30, 0.5 + 0.5 * 0.59, 0.5 + 0.5 * 0.11, saturation };
 
428
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
429
        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
 
430
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
 
431
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 
432
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
 
433
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
434
        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant);
 
435
        tex->bind();
 
436
 
 
437
        // Finally we need to interpolate between the original image and the
 
438
        //  greyscale image to get wanted level of saturation
 
439
        glActiveTexture(GL_TEXTURE2);
 
440
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
441
        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
 
442
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
 
443
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 
444
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
 
445
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
446
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
 
447
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
 
448
        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant);
 
449
        // Also replace alpha by primary color's alpha here
 
450
        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
451
        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
 
452
        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 
453
        // And make primary color contain the wanted opacity
 
454
        glColor4f(opacity, opacity, opacity, opacity);
 
455
        tex->bind();
 
456
 
 
457
        if (alpha || brightness != 1.0f) {
 
458
            glActiveTexture(GL_TEXTURE3);
 
459
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
460
            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
 
461
            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
 
462
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 
463
            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
 
464
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
465
            // The color has to be multiplied by both opacity and brightness
 
466
            float opacityByBrightness = opacity * brightness;
 
467
            glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity);
 
468
            if (alpha) {
 
469
                // Multiply original texture's alpha by our opacity
 
470
                glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
 
471
                glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
 
472
                glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 
473
                glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
 
474
                glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
 
475
            } else {
 
476
                // Alpha will be taken from previous stage
 
477
                glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
478
                glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
 
479
                glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 
480
            }
 
481
            tex->bind();
 
482
        }
 
483
 
 
484
        glActiveTexture(GL_TEXTURE0);
 
485
    } else if (opacity != 1.0 || brightness != 1.0) {
 
486
        // the window is additionally configured to have its opacity adjusted,
 
487
        // do it
 
488
        float opacityByBrightness = opacity * brightness;
 
489
        if (alpha) {
 
490
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
491
            glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness,
 
492
                      opacity);
 
493
        } else {
 
494
            // Multiply color by brightness and replace alpha by opacity
 
495
            float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity };
 
496
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
 
497
            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
 
498
            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
 
499
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 
500
            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
 
501
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
502
            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
503
            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
 
504
            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
 
505
        }
 
506
    }
 
507
#endif
 
508
#endif
 
509
}
 
510
 
 
511
void LanczosFilter::restoreRenderStates(GLTexture* tex, double opacity, double brightness, double saturation)
 
512
{
 
513
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
514
#ifndef KWIN_HAVE_OPENGLES
 
515
    if (opacity != 1.0 || saturation != 1.0 || brightness != 1.0f) {
 
516
        if (saturation != 1.0 && tex->saturationSupported()) {
 
517
            glActiveTexture(GL_TEXTURE3);
 
518
            glDisable(tex->target());
 
519
            glActiveTexture(GL_TEXTURE2);
 
520
            glDisable(tex->target());
 
521
            glActiveTexture(GL_TEXTURE1);
 
522
            glDisable(tex->target());
 
523
            glActiveTexture(GL_TEXTURE0);
 
524
        }
 
525
    }
 
526
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
527
    glColor4f(0, 0, 0, 0);
 
528
 
 
529
    glPopAttrib();  // ENABLE_BIT
 
530
#endif
 
531
#endif
 
532
}
 
533
 
 
534
/************************************************
 
535
* LanczosShader
 
536
************************************************/
 
537
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
 
538
LanczosShader::LanczosShader(QObject* parent)
 
539
    : QObject(parent)
 
540
    , m_shader(0)
 
541
    , m_arbProgram(0)
 
542
{
 
543
}
 
544
 
 
545
LanczosShader::~LanczosShader()
 
546
{
 
547
    delete m_shader;
 
548
#ifndef KWIN_HAVE_OPENGLES
 
549
    if (m_arbProgram) {
 
550
        glDeleteProgramsARB(1, &m_arbProgram);
 
551
        m_arbProgram = 0;
 
552
    }
 
553
#endif
 
554
}
 
555
 
 
556
void LanczosShader::bind()
 
557
{
 
558
    if (m_shader)
 
559
        ShaderManager::instance()->pushShader(m_shader);
 
560
#ifndef KWIN_HAVE_OPENGLES
 
561
    else {
 
562
        glEnable(GL_FRAGMENT_PROGRAM_ARB);
 
563
        glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram);
 
564
    }
 
565
#endif
 
566
}
 
567
 
 
568
void LanczosShader::unbind()
 
569
{
 
570
    if (m_shader)
 
571
        ShaderManager::instance()->popShader();
 
572
#ifndef KWIN_HAVE_OPENGLES
 
573
    else {
 
574
        int boundObject;
 
575
        glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &boundObject);
 
576
        if (boundObject == m_arbProgram) {
 
577
            glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
 
578
            glDisable(GL_FRAGMENT_PROGRAM_ARB);
 
579
        }
 
580
    }
 
581
#endif
 
582
}
 
583
 
 
584
void LanczosShader::setUniforms()
 
585
{
 
586
    if (m_shader) {
 
587
        glUniform1i(m_uTexUnit, 0);
 
588
        glUniform2fv(m_uOffsets, 16, (const GLfloat*)m_offsets);
 
589
        glUniform4fv(m_uKernel, 16, (const GLfloat*)m_kernel);
 
590
    }
 
591
#ifndef KWIN_HAVE_OPENGLES
 
592
    else {
 
593
        for (int i = 0; i < 16; ++i) {
 
594
            glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, i, m_offsets[i].x(), m_offsets[i].y(), 0, 0);
 
595
        }
 
596
        for (int i = 0; i < 16; ++i) {
 
597
            glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, i + 16, m_kernel[i].x(), m_kernel[i].y(), m_kernel[i].z(), m_kernel[i].w());
 
598
        }
 
599
    }
 
600
#endif
 
601
}
 
602
 
 
603
bool LanczosShader::init()
 
604
{
 
605
    GLPlatform *gl = GLPlatform::instance();
 
606
    if (gl->supports(GLSL) &&
 
607
            ShaderManager::instance()->isValid() &&
 
608
            GLRenderTarget::supported() &&
 
609
            !(gl->isRadeon() && gl->chipClass() < R600)) {
 
610
        m_shader = ShaderManager::instance()->loadFragmentShader(ShaderManager::SimpleShader, ":/resources/lanczos-fragment.glsl");
 
611
        if (m_shader->isValid()) {
 
612
            ShaderManager::instance()->pushShader(m_shader);
 
613
            m_uTexUnit    = m_shader->uniformLocation("texUnit");
 
614
            m_uKernel     = m_shader->uniformLocation("kernel");
 
615
            m_uOffsets    = m_shader->uniformLocation("offsets");
 
616
            ShaderManager::instance()->popShader();
 
617
            return true;
 
618
        } else {
 
619
            kDebug(1212) << "Shader is not valid";
 
620
            m_shader = 0;
 
621
            // try ARB shader
 
622
        }
 
623
    }
 
624
 
 
625
#ifdef KWIN_HAVE_OPENGLES
 
626
    // no ARB shader in GLES
 
627
    return false;
 
628
#else
 
629
    // try to create an ARB Shader
 
630
    if (!hasGLExtension("GL_ARB_fragment_program"))
 
631
        return false;
 
632
 
 
633
    QByteArray text;
 
634
    QTextStream stream(&text);
 
635
 
 
636
    // Note: This program uses 31 temporaries, 61 ALU instructions, 31 texture
 
637
    //       fetches, 3 texture indirections and 93 instructions.
 
638
    //       The R300 limitations are 32, 64, 32, 4 and 96 respectively.
 
639
    stream << "!!ARBfp1.0\n";
 
640
    stream << "TEMP sum;\n";
 
641
 
 
642
    // Declare 30 temporaries for holding texcoords and TEX results
 
643
    for (int i = 0; i < 30; i++)
 
644
        stream << "TEMP temp" << i << ";\n";
 
645
 
 
646
    // Compute the texture coordinates
 
647
    for (int i = 0, j = 0; i < 30 / 2; i++) {
 
648
        stream << "ADD temp" << j++ << ", fragment.texcoord, program.local[" << i + 1 << "];\n";
 
649
        stream << "SUB temp" << j++ << ", fragment.texcoord, program.local[" << i + 1 << "];\n";
 
650
    }
 
651
 
 
652
    // Sample the texture coordinates
 
653
    stream << "TEX sum, fragment.texcoord, texture[0], 2D;\n";
 
654
    for (int i = 0; i < 30; i++)
 
655
        stream << "TEX temp" << i << ", temp" << i << ", texture[0], 2D;\n";
 
656
 
 
657
    // Process the results
 
658
    stream << "MUL sum, sum, program.local[16];\n"; // sum = sum * kernel[0]
 
659
    for (int i = 0, j = 0; i < 30 / 2; i++) {
 
660
        stream << "MAD sum, temp" << j++ << ", program.local[" << (17 + i) << "], sum;\n";
 
661
        stream << "MAD sum, temp" << j++ << ", program.local[" << (17 + i) << "], sum;\n";
 
662
    }
 
663
 
 
664
    stream << "MOV result.color, sum;\n";
 
665
    stream << "END\n";
 
666
    stream.flush();
 
667
 
 
668
    glEnable(GL_FRAGMENT_PROGRAM_ARB);
 
669
    glGenProgramsARB(1, &m_arbProgram);
 
670
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_arbProgram);
 
671
    glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, text.length(), text.constData());
 
672
 
 
673
    if (glGetError()) {
 
674
        const char *error = (const char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
 
675
        kError() << "Failed to compile fragment program:" << error;
 
676
        glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
 
677
        glDeleteProgramsARB(1, &m_arbProgram);
 
678
        glDisable(GL_FRAGMENT_PROGRAM_ARB);
 
679
        m_arbProgram = 0;
 
680
        return false;
 
681
    }
 
682
 
 
683
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
 
684
    glDisable(GL_FRAGMENT_PROGRAM_ARB);
 
685
    kDebug(1212) << "ARB Shader compiled, id: " << m_arbProgram;
 
686
    return true;
 
687
#endif
 
688
}
 
689
#endif
 
690
 
 
691
} // namespace
 
692