diff --git a/effects/taskbarthumbnail/CMakeLists.txt b/effects/taskbarthumbnail/CMakeLists.txt
index c502753907..36875f62ee 100644
--- a/effects/taskbarthumbnail/CMakeLists.txt
+++ b/effects/taskbarthumbnail/CMakeLists.txt
@@ -5,6 +5,9 @@
set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
taskbarthumbnail/taskbarthumbnail.cpp
)
+qt4_add_resources( kwin4_effect_builtins_sources
+ taskbarthumbnail/taskbarthumbnail.qrc
+ )
# .desktop files
install( FILES
diff --git a/effects/taskbarthumbnail/fragment.glsl b/effects/taskbarthumbnail/fragment.glsl
new file mode 100644
index 0000000000..639bf847b4
--- /dev/null
+++ b/effects/taskbarthumbnail/fragment.glsl
@@ -0,0 +1,15 @@
+uniform sampler2D texUnit;
+uniform vec2 offsets[25];
+uniform vec4 kernel[25];
+uniform int kernelSize;
+
+void main(void)
+{
+ vec4 sum = texture2D(texUnit, gl_TexCoord[0].st) * kernel[0];
+ for (int i = 1; i < kernelSize; i++) {
+ sum += texture2D(texUnit, gl_TexCoord[0].st - offsets[i]) * kernel[i];
+ sum += texture2D(texUnit, gl_TexCoord[0].st + offsets[i]) * kernel[i];
+ }
+ gl_FragColor = sum;
+}
+
diff --git a/effects/taskbarthumbnail/taskbarthumbnail.cpp b/effects/taskbarthumbnail/taskbarthumbnail.cpp
index 01f0f30f5d..0346dc61a4 100644
--- a/effects/taskbarthumbnail/taskbarthumbnail.cpp
+++ b/effects/taskbarthumbnail/taskbarthumbnail.cpp
@@ -27,6 +27,8 @@ along with this program. If not, see .
#include
#endif
+#include
+#include
// This effect shows a preview inside a window that has a special property set
// on it that says which window and where to render. It is used by the taskbar
@@ -44,12 +46,42 @@ TaskbarThumbnailEffect::TaskbarThumbnailEffect()
// TODO hackish way to announce support, make better after 4.0
unsigned char dummy = 0;
XChangeProperty( display(), rootWindow(), atom, atom, 8, PropModeReplace, &dummy, 1 );
+
+ // With the NVIDIA driver the alpha values are set to 0 when drawing a window texture
+ // without an alpha channel on an FBO that has an alpha channel.
+ // This creates problems when the FBO is drawn on the backbuffer with blending enabled,
+ // so for now we only do high quality scaling with windows with alpha channels with
+ // the NVIDIA driver.
+ const QByteArray vendor = (const char *) glGetString( GL_VENDOR );
+ alphaWindowsOnly = vendor.startsWith("NVIDIA");
+
+ if ( GLShader::fragmentShaderSupported() &&
+ GLShader::vertexShaderSupported() &&
+ GLRenderTarget::supported() )
+ {
+ shader = new GLShader(":/taskbarthumbnail/vertex.glsl", ":/taskbarthumbnail/fragment.glsl");
+ shader->bind();
+ uTexUnit = shader->uniformLocation("texUnit");
+ uKernelSize = shader->uniformLocation("kernelSize");
+ uKernel = shader->uniformLocation("kernel");
+ uOffsets = shader->uniformLocation("offsets");
+ shader->unbind();
+ }
+ else
+ {
+ shader = 0;
+ }
+ offscreenTex = 0;
+ offscreenTarget = 0;
}
TaskbarThumbnailEffect::~TaskbarThumbnailEffect()
{
XDeleteProperty( display(), rootWindow(), atom );
effects->registerPropertyType( atom, false );
+ delete offscreenTarget;
+ delete offscreenTex;
+ delete shader;
}
void TaskbarThumbnailEffect::prePaintScreen( ScreenPrePaintData& data, int time )
@@ -66,6 +98,85 @@ void TaskbarThumbnailEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData
effects->prePaintWindow( w, data, time );
}
+void TaskbarThumbnailEffect::updateOffscreenSurfaces()
+ {
+ int w = displayWidth();
+ int h = displayHeight();
+ if ( !GLTexture::NPOTTextureSupported() )
+ {
+ w = nearestPowerOfTwo( w );
+ h = nearestPowerOfTwo( h );
+ }
+ if ( !offscreenTex || offscreenTex->width() != w || offscreenTex->height() != h )
+ {
+ if ( offscreenTex )
+ {
+ delete offscreenTex;
+ delete offscreenTarget;
+ }
+ offscreenTex = new GLTexture( w, h );
+ offscreenTex->setFilter( GL_LINEAR );
+ offscreenTex->setWrapMode( GL_CLAMP_TO_EDGE );
+ offscreenTarget = new GLRenderTarget( offscreenTex );
+ }
+ }
+
+static float sinc( float x )
+ {
+ return std::sin( x * M_PI ) / ( x * M_PI );
+ }
+
+static float lanczos( float x, float a )
+ {
+ if ( qFuzzyCompare( x + 1.0, 1.0 ) )
+ return 1.0;
+
+ if ( qAbs( x ) >= a )
+ return 0.0;
+
+ return sinc( x ) * sinc( x / a );
+ }
+
+QVector TaskbarThumbnailEffect::createKernel( float delta )
+ {
+ const float a = 2.0;
+
+ // The two outermost samples always fall at points where the lanczos
+ // function returns 0, so we'll skip them.
+ const int sampleCount = qBound( 3, qCeil(delta * a) * 2 + 1 - 2, 49 );
+ const int center = sampleCount / 2;
+ const int kernelSize = center + 1;
+ const float factor = 1.0 / delta;
+
+ QVector values( kernelSize );
+ QVector kernel( kernelSize );
+ float sum = 0;
+
+ for ( int i = 0; i < kernelSize; i++ ) {
+ const float val = lanczos( i * factor, a );
+ sum += i > 0 ? val * 2 : val;
+ values[i] = val;
+ }
+
+ // Normalize the kernel
+ for ( int i = 0; i < kernelSize; i++ ) {
+ const float val = values[i] / sum;
+ kernel[i] = QVector4D( val, val, val, val );
+ }
+
+ return kernel;
+ }
+
+QVector TaskbarThumbnailEffect::createOffsets( int count, float width, Qt::Orientation direction )
+ {
+ QVector offsets( count );
+ for ( int i = 0; i < count; i++ ) {
+ offsets[i] = ( direction == Qt::Horizontal ) ?
+ QVector2D( i / width, 0 ) : QVector2D( 0, i / width );
+ }
+ return offsets;
+ }
+
void TaskbarThumbnailEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
effects->paintWindow( w, mask, region, data ); // paint window first
@@ -84,30 +195,124 @@ void TaskbarThumbnailEffect::paintWindow( EffectWindow* w, int mask, QRegion reg
WindowPaintData thumbData( thumbw );
thumbData.opacity = data.opacity;
QRect r;
- setPositionTransformations( thumbData, r,
- thumbw, thumb.rect.translated( w->pos()), Qt::KeepAspectRatio );
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
- if( effects->compositingType() == KWin::OpenGLCompositing && data.shader )
+ if( effects->compositingType() == KWin::OpenGLCompositing )
{
- // there is a shader - update texture width and height
- int texw = thumbw->width();
- int texh = thumbw->height();
- if( !GLTexture::NPOTTextureSupported() )
+ if ( shader && (!alphaWindowsOnly || thumbw->hasAlpha()) )
{
- kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ;
- texw = nearestPowerOfTwo( texw );
- texh = nearestPowerOfTwo( texh );
+ int tx = thumb.rect.x() + w->x();
+ int ty = thumb.rect.y() + w->y();
+ int tw = thumb.rect.width();
+ int th = thumb.rect.height();
+
+ int sw = thumbw->width();
+ int sh = thumbw->height();
+
+ setPositionTransformations( thumbData, r,
+ thumbw, QRect(0, 0, sw, sh), Qt::KeepAspectRatio );
+
+ // Bind the offscreen FBO and draw the window on it unscaled
+ updateOffscreenSurfaces();
+ effects->pushRenderTarget( offscreenTarget );
+
+ glClear( GL_COLOR_BUFFER_BIT );
+ effects->drawWindow( thumbw, mask, r, thumbData );
+
+ // Create a scratch texture and copy the rendered window into it
+ GLTexture tex( sw, sh );
+ tex.setFilter( GL_LINEAR );
+ tex.setWrapMode( GL_CLAMP_TO_EDGE );
+ tex.bind();
+
+ glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, offscreenTex->height() - sh, sw, sh );
+
+ // Set up the shader for horizontal scaling
+ float dx = sw / float(tw);
+ QVector kernel = createKernel( dx );
+ QVector offsets = createOffsets( kernel.size(), sw, Qt::Horizontal );
+
+ shader->bind();
+ glUniform1i( uTexUnit, 0 );
+ glUniform1i( uKernelSize, kernel.size() );
+ glUniform2fv( uOffsets, offsets.size(), (const GLfloat*)offsets.constData() );
+ glUniform4fv( uKernel, kernel.size(), (const GLfloat*)kernel.constData() );
+
+ // Draw the window back into the FBO, this time scaled horizontally
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ glBegin( GL_QUADS );
+ glTexCoord2f( 0, 0 ); glVertex2i( 0, 0 ); // Top left
+ glTexCoord2f( 1, 0 ); glVertex2i( tw, 0 ); // Top right
+ glTexCoord2f( 1, 1 ); glVertex2i( tw, sh ); // Bottom right
+ glTexCoord2f( 0, 1 ); glVertex2i( 0, sh ); // Bottom left
+ glEnd();
+
+ // At this point we don't need the scratch texture anymore
+ tex.unbind();
+ tex.discard();
+
+ // Unbind the FBO
+ effects->popRenderTarget();
+
+ // Set up the shader for vertical scaling
+ float dy = sh / float(th);
+ kernel = createKernel( dy );
+ offsets = createOffsets( kernel.size(), offscreenTex->height(), Qt::Vertical );
+
+ glUniform1i( uKernelSize, kernel.size() );
+ glUniform2fv( uOffsets, offsets.size(), (const GLfloat*)offsets.constData() );
+ glUniform4fv( uKernel, kernel.size(), (const GLfloat*)kernel.constData() );
+
+ float sx2 = tw / float(offscreenTex->width());
+ float sy2 = 1 - (sh / float(offscreenTex->height()));
+
+ // Now draw the horizontally scaled window in the FBO at the right
+ // coordinates on the screen, while scaling it vertically and blending it.
+ offscreenTex->bind();
+
+ glPushAttrib( GL_COLOR_BUFFER_BIT );
+ glEnable( GL_BLEND );
+ glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
+
+ glBegin( GL_QUADS );
+ glTexCoord2f( 0, sy2 ); glVertex2i( tx, ty ); // Top left
+ glTexCoord2f( sx2, sy2 ); glVertex2i( tx + tw, ty ); // Top right
+ glTexCoord2f( sx2, 1 ); glVertex2i( tx + tw, ty + th ); // Bottom right
+ glTexCoord2f( 0, 1 ); glVertex2i( tx, ty + th ); // Bottom left
+ glEnd();
+
+ glPopAttrib();
+ offscreenTex->unbind();
+ shader->unbind();
+
+ // Delete the offscreen surface after 5 seconds
+ timer.start( 5000, this );
+ return;
}
- thumbData.shader = data.shader;
- thumbData.shader->setTextureWidth( (float)texw );
- thumbData.shader->setTextureHeight( (float)texh );
- }
+ if ( data.shader )
+ {
+ // there is a shader - update texture width and height
+ int texw = thumbw->width();
+ int texh = thumbw->height();
+ if( !GLTexture::NPOTTextureSupported() )
+ {
+ kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ;
+ texw = nearestPowerOfTwo( texw );
+ texh = nearestPowerOfTwo( texh );
+ }
+ thumbData.shader = data.shader;
+ thumbData.shader->setTextureWidth( (float)texw );
+ thumbData.shader->setTextureHeight( (float)texh );
+ }
+ } // if ( effects->compositingType() == KWin::OpenGLCompositing )
#endif
+ setPositionTransformations( thumbData, r,
+ thumbw, thumb.rect.translated( w->pos()), Qt::KeepAspectRatio );
effects->drawWindow( thumbw, mask, r, thumbData );
}
}
- }
+ } // End of function
void TaskbarThumbnailEffect::windowDamaged( EffectWindow* w, const QRect& damage )
{
@@ -158,4 +363,18 @@ void TaskbarThumbnailEffect::propertyNotify( EffectWindow* w, long a )
}
}
+
+void TaskbarThumbnailEffect::timerEvent( QTimerEvent *event )
+ {
+ if (event->timerId() == timer.timerId())
+ {
+ timer.stop();
+
+ delete offscreenTarget;
+ delete offscreenTex;
+ offscreenTarget = 0;
+ offscreenTex = 0;
+ }
+ }
+
} // namespace
diff --git a/effects/taskbarthumbnail/taskbarthumbnail.h b/effects/taskbarthumbnail/taskbarthumbnail.h
index aa419792a3..507e35285e 100644
--- a/effects/taskbarthumbnail/taskbarthumbnail.h
+++ b/effects/taskbarthumbnail/taskbarthumbnail.h
@@ -23,12 +23,17 @@ along with this program. If not, see .
#define KWIN_TASKBARTHUMBNAIL_H
#include
+#include
+#include
+#include
+#include
+#include
namespace KWin
{
class TaskbarThumbnailEffect
- : public Effect
+ : public QObject, public Effect
{
public:
TaskbarThumbnailEffect();
@@ -41,15 +46,28 @@ class TaskbarThumbnailEffect
virtual void windowDeleted( EffectWindow* w );
virtual void propertyNotify( EffectWindow* w, long atom );
protected:
+ virtual void timerEvent(QTimerEvent*);
private:
+ void updateOffscreenSurfaces();
+ QVector createKernel(float delta);
+ QVector createOffsets(int count, float width, Qt::Orientation direction);
struct Data
{
Window window; // thumbnail of this window
QRect rect;
};
long atom;
+ GLTexture *offscreenTex;
+ GLRenderTarget *offscreenTarget;
+ GLShader *shader;
QMultiHash< EffectWindow*, Data > thumbnails;
EffectWindowList damagedWindows;
+ QBasicTimer timer;
+ int uTexUnit;
+ int uOffsets;
+ int uKernel;
+ int uKernelSize;
+ bool alphaWindowsOnly;
};
} // namespace
diff --git a/effects/taskbarthumbnail/taskbarthumbnail.qrc b/effects/taskbarthumbnail/taskbarthumbnail.qrc
new file mode 100644
index 0000000000..b439ad06c7
--- /dev/null
+++ b/effects/taskbarthumbnail/taskbarthumbnail.qrc
@@ -0,0 +1,6 @@
+
+
+ vertex.glsl
+ fragment.glsl
+
+
diff --git a/effects/taskbarthumbnail/vertex.glsl b/effects/taskbarthumbnail/vertex.glsl
new file mode 100644
index 0000000000..f04c034d3e
--- /dev/null
+++ b/effects/taskbarthumbnail/vertex.glsl
@@ -0,0 +1,6 @@
+void main(void)
+{
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+}
+