Add blur effect.

Blur effects blurs out background of translucent windows, thus improving e.g. readability of text in
  such windows.
I'll send a mail with couple of screenshots and more details to kwin list soon.

svn path=/branches/work/kwin_composite/; revision=658817
This commit is contained in:
Rivo Laks 2007-04-28 15:13:28 +00:00
parent f00d1745ed
commit 72f2d412c1
8 changed files with 453 additions and 0 deletions

View file

@ -39,6 +39,7 @@ SET(kwin4_effect_tests_sources
if(OPENGL_FOUND)
SET(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
blur.cpp
explosioneffect.cpp
magnifier.cpp
mousemark.cpp
@ -52,6 +53,7 @@ if(OPENGL_FOUND)
)
install( FILES
blur.desktop
explosion.desktop
magnifier.desktop
mousemark.desktop
@ -70,6 +72,10 @@ if(OPENGL_FOUND)
data/explosion-end.png
data/liquid.frag
data/liquid.vert
data/blur.frag
data/blur.vert
data/blur-render.frag
data/blur-render.vert
DESTINATION ${DATA_INSTALL_DIR}/kwin )
endif(OPENGL_FOUND)

285
effects/blur.cpp Normal file
View file

@ -0,0 +1,285 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#include "blur.h"
#include <kwinglutils.h>
#include <QString>
#include <KStandardDirs>
#include <kdebug.h>
namespace KWin
{
KWIN_EFFECT( Blur, BlurEffect );
KWIN_EFFECT_SUPPORTED( Blur, BlurEffect::supported() );
BlurEffect::BlurEffect() : Effect()
{
mSceneTexture = 0;
mTmpTexture = 0;
mBlurTexture = 0;
mSceneTarget = 0;
mTmpTarget = 0;
mBlurTarget = 0;
mBlurShader = 0;
mWindowShader = 0;
mBlurRadius = 4;
mTime = 0;
mValid = loadData();
}
BlurEffect::~BlurEffect()
{
delete mSceneTexture;
delete mTmpTexture;
delete mBlurTexture;
delete mSceneTarget;
delete mTmpTarget;
delete mBlurTarget;
delete mBlurShader;
delete mWindowShader;
}
bool BlurEffect::loadData()
{
// Create texture and render target
mSceneTexture = new GLTexture(displayWidth(), displayHeight());
mSceneTexture->setFilter(GL_LINEAR);
mTmpTexture = new GLTexture(displayWidth(), displayHeight());
mTmpTexture->setFilter(GL_LINEAR);
mBlurTexture = new GLTexture(displayWidth(), displayHeight());
mSceneTarget = new GLRenderTarget(mSceneTexture);
if( !mSceneTarget->valid() )
return false;
mTmpTarget = new GLRenderTarget(mTmpTexture);
if( !mTmpTarget->valid() )
return false;
mBlurTarget = new GLRenderTarget(mBlurTexture);
if( !mBlurTarget->valid() )
return false;
mBlurShader = loadShader("blur");
if( !mBlurShader )
return false;
mWindowShader = loadShader("blur-render");
if( !mWindowShader )
return false;
mBlurShader->bind();
mBlurShader->setUniform("inputTex", 0);
mBlurShader->setUniform("displayWidth", (float)displayWidth());
mBlurShader->setUniform("displayHeight", (float)displayHeight());
mBlurShader->unbind();
mWindowShader->bind();
mWindowShader->setUniform("windowTex", 0);
mWindowShader->setUniform("backgroundTex", 4);
mWindowShader->setUniform("displayWidth", (float)displayWidth());
mWindowShader->setUniform("displayHeight", (float)displayHeight());
mWindowShader->unbind();
return true;
}
GLShader* BlurEffect::loadShader(const QString& name)
{
QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/" + name + ".frag");
QString vertexshader = KGlobal::dirs()->findResource("data", "kwin/" + name + ".vert");
if(fragmentshader.isEmpty() || vertexshader.isEmpty())
{
kError() << k_funcinfo << "Couldn't locate shader files for '" << name << "'" << endl;
return false;
}
GLShader* shader = new GLShader(vertexshader, fragmentshader);
if(!shader->isValid())
{
kError() << k_funcinfo << "Shader '" << name << "' failed to load!" << endl;
delete shader;
return 0;
}
return shader;
}
bool BlurEffect::supported()
{
return GLRenderTarget::supported() &&
GLShader::fragmentShaderSupported() &&
(effects->compositingType() == OpenGLCompositing);
}
void BlurEffect::prePaintScreen( int* mask, QRegion* region, int time )
{
mTransparentWindows = 0;
mTime += time;
effects->prePaintScreen(mask, region, time);
}
void BlurEffect::prePaintWindow( EffectWindow* w, int* mask, QRegion* paint, QRegion* clip, int time )
{
effects->prePaintWindow( w, mask, paint, clip, time );
if( w->isPaintingEnabled() && ( *mask & PAINT_WINDOW_TRANSLUCENT ))
mTransparentWindows++;
}
void BlurEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if( mValid && mTransparentWindows )
{
if( mask & PAINT_WINDOW_TRANSLUCENT )
{
// Make sure the blur texture is up to date
if( mask & PAINT_SCREEN_TRANSFORMED )
{
// We don't want any transformations when working with our own
// textures, so load an identity matrix
glPushMatrix();
glLoadIdentity();
}
// If we're having transformations, we don't know the window's
// transformed position on the screen and thus have to update the
// entire screen
if( mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS ) )
updateBlurTexture( QRegion(0, 0, displayWidth(), displayHeight()) );
else
updateBlurTexture(region);
if( mask & PAINT_SCREEN_TRANSFORMED )
// Restore the original matrix
glPopMatrix();
// Set custom shader to render the window with
mWindowShader->bind();
w->setShader(mWindowShader);
// Put the blur texture to tex unit 4
glActiveTexture(GL_TEXTURE4);
mBlurTexture->bind();
mBlurTexture->enableUnnormalizedTexCoords();
glActiveTexture(GL_TEXTURE0);
// Paint
effects->paintWindow( w, mask, region, data );
if(mTransparentWindows > 1)
{
// If we have multiple translucent windows on top of each
// other, we need to paint those onto the scene rendertarget
// as well
effects->pushRenderTarget(mSceneTarget);
effects->paintWindow( w, mask, region, data );
effects->popRenderTarget();
}
// Disable blur texture and shader
glActiveTexture(GL_TEXTURE4);
mBlurTexture->disableUnnormalizedTexCoords();
mBlurTexture->unbind();
glActiveTexture(GL_TEXTURE0);
mWindowShader->unbind();
}
else
{
// Opaque window
// Paint to the screen...
effects->paintWindow( w, mask, region, data );
// ...and to the rendertarget as well
effects->pushRenderTarget(mSceneTarget);
effects->paintWindow( w, mask, region, data );
effects->popRenderTarget();
}
}
else
// If there are no translucent windows then paint as usual
effects->paintWindow( w, mask, region, data );
}
void BlurEffect::updateBlurTexture(const QRegion& region)
{
QRect bounding = region.boundingRect();
QVector<QRect> rects = region.rects();
int totalarea = 0;
foreach( QRect r, rects )
totalarea += r.width() * r.height();
if( (int)(totalarea * 1.33 + 100 ) < bounding.width() * bounding.height() )
{
// Use small rects
updateBlurTexture(rects);
}
else
{
// Bounding rect is probably cheaper
QVector<QRect> tmp( 1, bounding );
updateBlurTexture( tmp );
}
}
void BlurEffect::updateBlurTexture(const QVector<QRect>& rects)
{
// Blur
// First pass (vertical)
effects->pushRenderTarget(mTmpTarget);
mBlurShader->bind();
mSceneTexture->bind();
mBlurShader->setAttribute("xBlur", 0);
mBlurShader->setAttribute("yBlur", 1);
foreach( QRect r, rects )
{
r.adjust(-mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius);
glBegin(GL_QUADS);
glVertex2f( r.x() , r.y() + r.height() );
glVertex2f( r.x() + r.width(), r.y() + r.height() );
glVertex2f( r.x() + r.width(), r.y() );
glVertex2f( r.x() , r.y() );
glEnd();
}
mSceneTexture->unbind();
mBlurShader->unbind();
effects->popRenderTarget();
// Second pass (horizontal)
effects->pushRenderTarget(mBlurTarget);
mBlurShader->bind();
mTmpTexture->bind();
mBlurShader->setAttribute("xBlur", 1);
mBlurShader->setAttribute("yBlur", 0);
foreach( QRect r, rects )
{
r.adjust(-mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius);
glBegin(GL_QUADS);
glVertex2f( r.x() , r.y() + r.height() );
glVertex2f( r.x() + r.width(), r.y() + r.height() );
glVertex2f( r.x() + r.width(), r.y() );
glVertex2f( r.x() , r.y() );
glEnd();
}
mTmpTexture->unbind();
mBlurShader->unbind();
effects->popRenderTarget();
}
} // namespace

4
effects/blur.desktop Normal file
View file

@ -0,0 +1,4 @@
[Desktop Entry]
Encoding=UTF-8
Name=Blur
X-KDE-Library=kwin4_effect_builtins

67
effects/blur.h Normal file
View file

@ -0,0 +1,67 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#ifndef KWIN_BLUR_H
#define KWIN_BLUR_H
// Include with base class for effects.
#include <kwineffects.h>
template< class T > class QVector;
namespace KWin
{
class GLRenderTarget;
class GLTexture;
class GLShader;
/**
* Blurs the background of translucent windows
**/
class BlurEffect : public Effect
{
public:
BlurEffect();
~BlurEffect();
virtual void prePaintScreen( int* mask, QRegion* region, int time );
virtual void prePaintWindow( EffectWindow* w, int* mask, QRegion* paint, QRegion* clip, int time );
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
static bool supported();
protected:
bool loadData();
GLShader* loadShader(const QString& name);
void updateBlurTexture(const QVector<QRect>& rects);
void updateBlurTexture(const QRegion& region);
private:
GLTexture* mSceneTexture;
GLTexture* mTmpTexture;
GLTexture* mBlurTexture;
GLRenderTarget* mSceneTarget;
GLRenderTarget* mTmpTarget;
GLRenderTarget* mBlurTarget;
GLShader* mBlurShader;
GLShader* mWindowShader;
bool mValid;
int mBlurRadius;
int mTransparentWindows;
int mTime;
};
} // namespace
#endif

View file

@ -0,0 +1,39 @@
uniform sampler2D windowTex;
uniform sampler2D backgroundTex;
uniform float displayWidth;
uniform float displayHeight;
uniform float opacity;
uniform float saturation;
uniform float brightness;
// Converts pixel coordinates to texture coordinates
vec2 pix2tex(vec2 pix)
{
return vec2(pix.x / displayWidth, pix.y / displayHeight);
}
// Returns color of the window at given texture coordinate, taking into
// account opacity, brightness and saturation
vec4 windowColor(vec2 texcoord)
{
vec3 color = texture2D(windowTex, texcoord).rgb;
// Apply saturation
float grayscale = dot(vec3(0.30, 0.59, 0.11), color.rgb);
color = mix(vec3(grayscale), color, saturation);
// Apply brightness
color = color * brightness;
// Apply opacity and return
return vec4(color, opacity);
}
void main()
{
vec2 texcoord = (gl_TexCoord[0] * gl_TextureMatrix[0]).xy;
vec2 blurtexcoord = pix2tex(gl_FragCoord.xy); //(gl_FragCoord * gl_TextureMatrix[4]).xy;
vec3 tex = mix(texture2D(backgroundTex, blurtexcoord).rgb,
windowColor(texcoord).rgb, opacity);
gl_FragColor = vec4(tex, 1.0);
}

View file

@ -0,0 +1,5 @@
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}

34
effects/data/blur.frag Normal file
View file

@ -0,0 +1,34 @@
uniform sampler2D inputTex;
uniform float displayWidth;
uniform float displayHeight;
varying vec2 pos;
varying vec2 blurDirection;
// Converts pixel coordinates to texture coordinates
vec2 pix2tex(vec2 pix)
{
return vec2(pix.x / displayWidth, 1.0 - pix.y / displayHeight);
}
vec3 blurTex(float offset, float strength)
{
return texture2D(inputTex, pix2tex(pos + blurDirection * offset)).rgb * strength;
}
void main()
{
// Do the gaussian blur
// This blur actually has a radius of 4, but we take advantage of gpu's
// linear texture filtering, so e.g. 1.5 actually gives us both texels
// 1 and 2
vec3 tex = blurTex(0.0, 0.20);
tex += blurTex(-1.5, 0.30);
tex += blurTex( 1.5, 0.30);
tex += blurTex(-3.5, 0.10);
tex += blurTex( 3.5, 0.10);
gl_FragColor = vec4(tex, 1.0);
}

13
effects/data/blur.vert Normal file
View file

@ -0,0 +1,13 @@
varying vec2 pos;
varying vec2 blurDirection;
attribute float xBlur;
attribute float yBlur;
void main()
{
blurDirection = vec2(xBlur, yBlur);
pos = gl_Vertex.xy;
gl_Position = ftransform();
}