diff --git a/effects/blur.cpp b/effects/blur.cpp index 7456c05390..9db4d35298 100644 --- a/effects/blur.cpp +++ b/effects/blur.cpp @@ -47,12 +47,17 @@ BlurEffect::BlurEffect() : Effect() mWindowShader = 0; mBlurRadius = 4; - mTime = 0; mValid = loadData(); + if( !mValid ) + { + kWarning() << "Loading failed"; + } + effects->addRepaintFull(); } BlurEffect::~BlurEffect() -{ + { + effects->addRepaintFull(); delete mSceneTexture; delete mTmpTexture; delete mBlurTexture; @@ -61,7 +66,7 @@ BlurEffect::~BlurEffect() delete mBlurTarget; delete mBlurShader; delete mWindowShader; -} + } bool BlurEffect::loadData() @@ -140,44 +145,123 @@ bool BlurEffect::supported() (effects->compositingType() == OpenGLCompositing); } +QRegion BlurEffect::expandedRegion( const QRegion& region ) const + { + QRegion expandedregion; + foreach( QRect r, region.rects() ) + { + r.adjust( -mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius ); + expandedregion += r; + } + return expandedregion; + } void BlurEffect::prePaintScreen( ScreenPrePaintData& data, int time ) { mTransparentWindows = 0; - mTime += time; + mScreenDirty = QRegion(); + mBlurDirty = QRegion(); + mBlurMask = QRegion(); effects->prePaintScreen(data, time); } void BlurEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) -{ + { + // Expand the painted area + mBlurMask |= expandedRegion( data.paint ); + data.paint |= expandedRegion( mBlurMask ); effects->prePaintWindow( w, data, time ); if( w->isPaintingEnabled() && ( data.mask & PAINT_WINDOW_TRANSLUCENT )) mTransparentWindows++; -} + data.setTranslucent(); + } -void BlurEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) -{ - if( mValid && mTransparentWindows ) +void BlurEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) { - if( mask & PAINT_WINDOW_TRANSLUCENT ) + // TODO: prePaintWindow() gets called _after_ paintScreen(), so we have no + // way of knowing here whether there will be any translucent windows or + // not. If we'd know that there's no translucent windows then we could + // render straight onto screen, saving some time. + if( mValid /*&& mTransparentWindows*/ ) { + // rendering everything onto render target + effects->pushRenderTarget(mSceneTarget); + effects->paintScreen( mask, region, data ); + effects->popRenderTarget(); + + // Copy changed areas back onto screen + mScreenDirty &= mBlurMask; + if( !mScreenDirty.isEmpty() ) + { + if( mask & PAINT_SCREEN_TRANSFORMED ) + { + // We don't want any transformations when working with our own + // textures, so load an identity matrix + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + } + + GLTexture* tex = mSceneTexture; + int pixels = 0; + tex->bind(); + tex->enableUnnormalizedTexCoords(); + foreach( QRect r, mScreenDirty.rects() ) + { + r.adjust(0, -1, 0, -1); + int rx2 = r.x() + r.width(); + int ry2 = r.y() + r.height(); + glBegin(GL_QUADS); + glTexCoord2f( r.x(), ry2 ); glVertex2f( r.x(), ry2 ); + glTexCoord2f( rx2 , ry2 ); glVertex2f( rx2 , ry2 ); + glTexCoord2f( rx2 , r.y() ); glVertex2f( rx2 , r.y() ); + glTexCoord2f( r.x(), r.y() ); glVertex2f( r.x(), r.y() ); + glEnd(); + pixels += r.width()*r.height(); + } + tex->disableUnnormalizedTexCoords(); + tex->unbind(); + + if( mask & PAINT_SCREEN_TRANSFORMED ) + { + // Restore the original matrix + glPopMatrix(); + } +// kDebug() << "Copied" << mScreenDirty.rects().count() << "rects, pixels:" << pixels; + } + + } + else + { + effects->paintScreen( mask, region, data ); + } + } + +void BlurEffect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + if( mValid /*&& mTransparentWindows*/ ) + { + if( mask & PAINT_WINDOW_TRANSLUCENT && + (data.opacity != 1.0 || data.contents_opacity != 1.0 || data.decoration_opacity != 1.0 )) + { // 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); + updateBlurTexture( mBlurDirty ); + mBlurDirty = QRegion(); if( mask & PAINT_SCREEN_TRANSFORMED ) // Restore the original matrix glPopMatrix(); @@ -191,97 +275,87 @@ void BlurEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowP 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(); - } + effects->drawWindow( w, mask, region, data ); // Disable blur texture and shader glActiveTexture(GL_TEXTURE4); 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(); + // Paint to the rendertarget (which is already being used) + effects->drawWindow( w, mask, region, data ); + } + // Mark the window's region as dirty + mScreenDirty += region; + mBlurDirty += region & mBlurMask; } - } else // If there are no translucent windows then paint as usual - effects->paintWindow( w, mask, region, data ); -} + effects->drawWindow( w, mask, region, data ); + } void BlurEffect::updateBlurTexture(const QRegion& region) -{ + { QRect bounding = region.boundingRect(); QVector 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 tmp( 1, bounding ); updateBlurTexture( tmp ); + } } -} void BlurEffect::updateBlurTexture(const QVector& rects) -{ + { // Blur // First pass (vertical) - effects->pushRenderTarget(mTmpTarget); mBlurShader->bind(); - mSceneTexture->bind(); + effects->pushRenderTarget(mTmpTarget); + mBlurShader->setAttribute("xBlur", 0.0f); + mBlurShader->setAttribute("yBlur", 1.0f); - mBlurShader->setAttribute("xBlur", 0); - mBlurShader->setAttribute("yBlur", 1); + mSceneTexture->bind(); foreach( QRect r, rects ) { - r.adjust(-mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius); + // We change x coordinates here because horizontal blur pass (which + // comes after this one) also uses pixels that are horizontally edging + // the blurred area. Thus we need to make sure that those pixels are + // also updated. 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() ); + glVertex2f( r.x()-mBlurRadius , r.y() + r.height() ); + glVertex2f( r.x() + r.width()+mBlurRadius, r.y() + r.height() ); + glVertex2f( r.x() + r.width()+mBlurRadius, r.y() ); + glVertex2f( r.x()-mBlurRadius , r.y() ); glEnd(); } mSceneTexture->unbind(); - mBlurShader->unbind(); effects->popRenderTarget(); // Second pass (horizontal) effects->pushRenderTarget(mBlurTarget); - mBlurShader->bind(); - mTmpTexture->bind(); + mBlurShader->setAttribute("xBlur", 1.0f); + mBlurShader->setAttribute("yBlur", 0.0f); - mBlurShader->setAttribute("xBlur", 1); - mBlurShader->setAttribute("yBlur", 0); + mTmpTexture->bind(); 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() ); @@ -292,9 +366,9 @@ void BlurEffect::updateBlurTexture(const QVector& rects) mTmpTexture->unbind(); - mBlurShader->unbind(); effects->popRenderTarget(); -} + mBlurShader->unbind(); + } } // namespace diff --git a/effects/blur.desktop b/effects/blur.desktop index 413b8b2d82..48aee9c8d0 100644 --- a/effects/blur.desktop +++ b/effects/blur.desktop @@ -75,9 +75,10 @@ X-KDE-ServiceTypes=KWin/Effect X-KDE-PluginInfo-Author=Rivo Laks X-KDE-PluginInfo-Email=rivolaks@hot.ee X-KDE-PluginInfo-Name=kwin4_effect_blur -X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Version=0.2.0 X-KDE-PluginInfo-Category=Appearance X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=false X-KDE-Library=kwin4_effect_builtins +X-Ordering=85 diff --git a/effects/blur.h b/effects/blur.h index 69c92ee00a..91f79902a5 100644 --- a/effects/blur.h +++ b/effects/blur.h @@ -24,6 +24,8 @@ along with this program. If not, see . // Include with base class for effects. #include +#include + template< class T > class QVector; @@ -44,9 +46,10 @@ class BlurEffect : public Effect ~BlurEffect(); virtual void prePaintScreen( ScreenPrePaintData& data, int time ); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ); - virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); + virtual void drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); static bool supported(); @@ -56,6 +59,8 @@ class BlurEffect : public Effect void updateBlurTexture(const QVector& rects); void updateBlurTexture(const QRegion& region); + QRegion expandedRegion( const QRegion& r ) const; + private: GLTexture* mSceneTexture; GLTexture* mTmpTexture; @@ -69,7 +74,11 @@ class BlurEffect : public Effect int mBlurRadius; int mTransparentWindows; - int mTime; + + QRegion mBlurDirty; + QRegion mScreenDirty; + + QRegion mBlurMask; }; } // namespace diff --git a/effects/data/blur-render.frag b/effects/data/blur-render.frag index 7f8650c82e..740f48149f 100644 --- a/effects/data/blur-render.frag +++ b/effects/data/blur-render.frag @@ -14,7 +14,7 @@ vec2 pix2tex(vec2 pix) } // Returns color of the window at given texture coordinate, taking into -// account opacity, brightness and saturation +// account brightness and saturation, but not opacity vec4 windowColor(vec2 texcoord) { vec4 color = texture2D(windowTex, texcoord); @@ -23,21 +23,18 @@ vec4 windowColor(vec2 texcoord) color.rgb = mix(vec3(grayscale), color.rgb, saturation); // Apply brightness color.rgb = color.rgb * brightness; - // Apply opacity - color.a = color.a * opacity; // and return return color; } void main() { - vec2 texcoord = (gl_TexCoord[0] * gl_TextureMatrix[0]).xy; - vec2 blurtexcoord = pix2tex(gl_FragCoord.xy); //(gl_FragCoord * gl_TextureMatrix[4]).xy; + vec2 blurtexcoord = pix2tex(gl_FragCoord.xy); - vec4 winColor = windowColor(texcoord); + vec4 winColor = windowColor(gl_TexCoord[0].xy); vec3 tex = mix(texture2D(backgroundTex, blurtexcoord).rgb, winColor.rgb, winColor.a * opacity); - gl_FragColor = vec4(tex, 1.0); + gl_FragColor = vec4(tex, pow(winColor.a, 0.2)); } diff --git a/effects/data/blur-render.vert b/effects/data/blur-render.vert index f1bef4e5ed..cbe3c17bd6 100644 --- a/effects/data/blur-render.vert +++ b/effects/data/blur-render.vert @@ -1,5 +1,5 @@ void main() { - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0] = gl_MultiTexCoord0 * gl_TextureMatrix[0]; gl_Position = ftransform(); } diff --git a/effects/data/blur.frag b/effects/data/blur.frag index a37521936d..72e867a4df 100644 --- a/effects/data/blur.frag +++ b/effects/data/blur.frag @@ -1,20 +1,19 @@ uniform sampler2D inputTex; -uniform float textureWidth; -uniform float textureHeight; -varying vec2 pos; -varying vec2 blurDirection; +varying vec2 samplePos1; +varying vec2 samplePos2; +varying vec2 samplePos3; +varying vec2 samplePos4; +varying vec2 samplePos5; -// Converts pixel coordinates to texture coordinates -vec2 pix2tex(vec2 pix) +// If defined, use five samples (blur radius = 5), otherwise 3 samples (radius = 3) +#define FIVE_SAMPLES + + +vec3 blurTex(vec2 pos, float strength) { - return vec2(pix.x / textureWidth, 1.0 - pix.y / textureHeight); -} - -vec3 blurTex(float offset, float strength) -{ - return texture2D(inputTex, pix2tex(pos + blurDirection * offset)).rgb * strength; + return texture2D(inputTex, pos).rgb * strength; } void main() @@ -23,11 +22,17 @@ void main() // 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); +#ifdef FIVE_SAMPLES + vec3 tex = blurTex(samplePos1, 0.30); + tex += blurTex(samplePos2, 0.25); + tex += blurTex(samplePos3, 0.25); + tex += blurTex(samplePos4, 0.1); + tex += blurTex(samplePos5, 0.1); +#else + vec3 tex = blurTex(samplePos1, 0.40); + tex += blurTex(samplePos2, 0.30); + tex += blurTex(samplePos3, 0.30); +#endif gl_FragColor = vec4(tex, 1.0); } diff --git a/effects/data/blur.vert b/effects/data/blur.vert index b6fe605e8d..4eb1e0d0ed 100644 --- a/effects/data/blur.vert +++ b/effects/data/blur.vert @@ -1,13 +1,28 @@ -varying vec2 pos; -varying vec2 blurDirection; +varying vec2 samplePos1; +varying vec2 samplePos2; +varying vec2 samplePos3; +varying vec2 samplePos4; +varying vec2 samplePos5; +uniform float textureWidth; +uniform float textureHeight; attribute float xBlur; attribute float yBlur; +vec2 mkSamplePos(vec2 origin, float offset) +{ + vec2 foo = origin + vec2(xBlur, yBlur) * offset; + return vec2(foo.x / textureWidth, 1.0 - foo.y / textureHeight); +} + void main() { - blurDirection = vec2(xBlur, yBlur); - pos = gl_Vertex.xy; + samplePos1 = mkSamplePos(gl_Vertex.xy, 0.0); + samplePos2 = mkSamplePos(gl_Vertex.xy, -1.5); + samplePos3 = mkSamplePos(gl_Vertex.xy, 1.5); + samplePos4 = mkSamplePos(gl_Vertex.xy, 3.5); + samplePos5 = mkSamplePos(gl_Vertex.xy, -3.5); + gl_Position = ftransform(); }