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:
parent
f00d1745ed
commit
72f2d412c1
8 changed files with 453 additions and 0 deletions
|
@ -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
285
effects/blur.cpp
Normal 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
4
effects/blur.desktop
Normal file
|
@ -0,0 +1,4 @@
|
|||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=Blur
|
||||
X-KDE-Library=kwin4_effect_builtins
|
67
effects/blur.h
Normal file
67
effects/blur.h
Normal 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
|
39
effects/data/blur-render.frag
Normal file
39
effects/data/blur-render.frag
Normal 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);
|
||||
}
|
||||
|
5
effects/data/blur-render.vert
Normal file
5
effects/data/blur-render.vert
Normal file
|
@ -0,0 +1,5 @@
|
|||
void main()
|
||||
{
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = ftransform();
|
||||
}
|
34
effects/data/blur.frag
Normal file
34
effects/data/blur.frag
Normal 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
13
effects/data/blur.vert
Normal 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();
|
||||
}
|
Loading…
Reference in a new issue