1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
23 #include <kwinglutils.h>
26 #include <KStandardDirs>
34 KWIN_EFFECT( blur
, BlurEffect
)
35 KWIN_EFFECT_SUPPORTED( blur
, BlurEffect::supported() )
38 BlurEffect::BlurEffect() : Effect()
53 kWarning(1212) << "Loading failed";
55 effects
->addRepaintFull();
58 BlurEffect::~BlurEffect()
60 effects
->addRepaintFull();
72 bool BlurEffect::loadData()
74 // Create texture and render target
75 int texw
= displayWidth();
76 int texh
= displayHeight();
77 if( !GLTexture::NPOTTextureSupported() )
79 kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ;
80 texw
= nearestPowerOfTwo(texw
);
81 texh
= nearestPowerOfTwo(texh
);
83 mSceneTexture
= new GLTexture(texw
, texh
);
84 mSceneTexture
->setFilter(GL_LINEAR
);
85 mTmpTexture
= new GLTexture(texw
, texh
);
86 mTmpTexture
->setFilter(GL_LINEAR
);
87 mBlurTexture
= new GLTexture(texw
, texh
);
89 mSceneTarget
= new GLRenderTarget(mSceneTexture
);
90 if( !mSceneTarget
->valid() )
92 mTmpTarget
= new GLRenderTarget(mTmpTexture
);
93 if( !mTmpTarget
->valid() )
95 mBlurTarget
= new GLRenderTarget(mBlurTexture
);
96 if( !mBlurTarget
->valid() )
99 mBlurShader
= loadShader("blur");
102 mWindowShader
= loadShader("blur-render");
107 mBlurShader
->setUniform("inputTex", 0);
108 mBlurShader
->setUniform("textureWidth", (float)texw
);
109 mBlurShader
->setUniform("textureHeight", (float)texh
);
110 mBlurShader
->unbind();
112 mWindowShader
->bind();
113 mWindowShader
->setUniform("windowTex", 0);
114 mWindowShader
->setUniform("backgroundTex", 4);
115 mWindowShader
->setUniform("textureWidth", (float)texw
);
116 mWindowShader
->setUniform("textureHeight", (float)texh
);
117 mWindowShader
->unbind();
122 GLShader
* BlurEffect::loadShader(const QString
& name
)
124 QString fragmentshader
= KGlobal::dirs()->findResource("data", "kwin/" + name
+ ".frag");
125 QString vertexshader
= KGlobal::dirs()->findResource("data", "kwin/" + name
+ ".vert");
126 if(fragmentshader
.isEmpty() || vertexshader
.isEmpty())
128 kError(1212) << "Couldn't locate shader files for '" << name
<< "'" << endl
;
131 GLShader
* shader
= new GLShader(vertexshader
, fragmentshader
);
132 if(!shader
->isValid())
134 kError(1212) << "Shader '" << name
<< "' failed to load!" << endl
;
141 bool BlurEffect::supported()
143 return GLRenderTarget::supported() &&
144 GLShader::fragmentShaderSupported() &&
145 (effects
->compositingType() == OpenGLCompositing
);
148 QRegion
BlurEffect::expandedRegion( const QRegion
& region
) const
150 QRegion expandedregion
;
151 foreach( QRect r
, region
.rects() )
153 r
.adjust( -mBlurRadius
, -mBlurRadius
, mBlurRadius
, mBlurRadius
);
156 return expandedregion
;
159 void BlurEffect::prePaintScreen( ScreenPrePaintData
& data
, int time
)
161 mTransparentWindows
= 0;
162 mScreenDirty
= QRegion();
163 mBlurDirty
= QRegion();
164 mBlurMask
= QRegion();
166 effects
->prePaintScreen(data
, time
);
169 void BlurEffect::prePaintWindow( EffectWindow
* w
, WindowPrePaintData
& data
, int time
)
171 // Expand the painted area
172 mBlurMask
|= expandedRegion( data
.paint
);
173 data
.paint
|= expandedRegion( mBlurMask
);
174 effects
->prePaintWindow( w
, data
, time
);
176 if( w
->isPaintingEnabled() && ( data
.mask
& PAINT_WINDOW_TRANSLUCENT
))
177 mTransparentWindows
++;
178 data
.setTranslucent();
181 void BlurEffect::paintScreen( int mask
, QRegion region
, ScreenPaintData
& data
)
183 // TODO: prePaintWindow() gets called _after_ paintScreen(), so we have no
184 // way of knowing here whether there will be any translucent windows or
185 // not. If we'd know that there's no translucent windows then we could
186 // render straight onto screen, saving some time.
187 // HACK disable blur when there is a fullscreen effects. Needed for e.g. cube to work
188 if( mValid
&& !effects
->activeFullScreenEffect() /*&& mTransparentWindows*/ )
190 // rendering everything onto render target
191 effects
->pushRenderTarget(mSceneTarget
);
192 effects
->paintScreen( mask
, region
, data
);
193 effects
->popRenderTarget();
195 // Copy changed areas back onto screen
196 mScreenDirty
&= mBlurMask
;
197 if( !mScreenDirty
.isEmpty() )
199 if( mask
& PAINT_SCREEN_TRANSFORMED
)
201 // We don't want any transformations when working with our own
202 // textures, so load an identity matrix
203 glMatrixMode( GL_MODELVIEW
);
208 GLTexture
* tex
= mSceneTexture
;
211 tex
->enableUnnormalizedTexCoords();
212 foreach( QRect r
, mScreenDirty
.rects() )
214 r
.adjust(0, -1, 0, -1);
215 int rx2
= r
.x() + r
.width();
216 int ry2
= r
.y() + r
.height();
218 glTexCoord2f( r
.x(), ry2
); glVertex2f( r
.x(), ry2
);
219 glTexCoord2f( rx2
, ry2
); glVertex2f( rx2
, ry2
);
220 glTexCoord2f( rx2
, r
.y() ); glVertex2f( rx2
, r
.y() );
221 glTexCoord2f( r
.x(), r
.y() ); glVertex2f( r
.x(), r
.y() );
223 pixels
+= r
.width()*r
.height();
225 tex
->disableUnnormalizedTexCoords();
228 if( mask
& PAINT_SCREEN_TRANSFORMED
)
230 // Restore the original matrix
233 // kDebug(1212) << "Copied" << mScreenDirty.rects().count() << "rects, pixels:" << pixels;
239 effects
->paintScreen( mask
, region
, data
);
243 void BlurEffect::drawWindow( EffectWindow
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
245 // HACK disable blur when there is a fullscreen effects. Needed for e.g. cube to work
246 if( mValid
&& !effects
->activeFullScreenEffect() /*&& mTransparentWindows*/ )
248 if( mask
& PAINT_WINDOW_TRANSLUCENT
&&
249 (data
.opacity
!= 1.0 || data
.contents_opacity
!= 1.0 || data
.decoration_opacity
!= 1.0 ))
252 double blurAmount
= data
.opacity
;
253 if( blurAmount
>= 1.0 )
255 blurAmount
= data
.contents_opacity
;
256 if( blurAmount
>= 1.0 )
258 blurAmount
= data
.decoration_opacity
;
259 if( blurAmount
>= 1.0 )
263 // Round to nearest 0.05
264 blurAmount
= double( qRound( blurAmount
* 20.0 )) / 20.0;
266 // Make sure the blur texture is up to date
267 if( mask
& PAINT_SCREEN_TRANSFORMED
)
269 // We don't want any transformations when working with our own
270 // textures, so load an identity matrix
274 // If we're having transformations, we don't know the window's
275 // transformed position on the screen and thus have to update the
277 if( mask
& ( PAINT_WINDOW_TRANSFORMED
| PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
) )
278 updateBlurTexture( QRegion(0, 0, displayWidth(), displayHeight()) , blurAmount
);
280 updateBlurTexture( mBlurDirty
, blurAmount
);
281 mBlurDirty
= QRegion();
282 if( mask
& PAINT_SCREEN_TRANSFORMED
)
283 // Restore the original matrix
286 // Set custom shader to render the window with
287 mWindowShader
->bind();
288 data
.shader
= mWindowShader
;
289 // Put the blur texture to tex unit 4
290 glActiveTexture(GL_TEXTURE4
);
291 mBlurTexture
->bind();
292 glActiveTexture(GL_TEXTURE0
);
295 effects
->drawWindow( w
, mask
, region
, data
);
297 // Disable blur texture and shader
298 glActiveTexture(GL_TEXTURE4
);
299 mBlurTexture
->unbind();
300 glActiveTexture(GL_TEXTURE0
);
301 mWindowShader
->unbind();
306 // Paint to the rendertarget (which is already being used)
307 effects
->drawWindow( w
, mask
, region
, data
);
309 // Mark the window's region as dirty
310 mScreenDirty
+= region
;
311 mBlurDirty
+= region
& mBlurMask
;
314 // If there are no translucent windows then paint as usual
315 effects
->drawWindow( w
, mask
, region
, data
);
318 void BlurEffect::updateBlurTexture(const QRegion
& region
, double blurAmount
)
320 QRect bounding
= region
.boundingRect();
321 QVector
<QRect
> rects
= region
.rects();
323 foreach( const QRect
&r
, rects
)
324 totalarea
+= r
.width() * r
.height();
325 if( (int)(totalarea
* 1.33 + 100 ) < bounding
.width() * bounding
.height() )
328 updateBlurTexture(rects
, blurAmount
);
332 // Bounding rect is probably cheaper
333 QVector
<QRect
> tmp( 1, bounding
);
334 updateBlurTexture( tmp
, blurAmount
);
338 void BlurEffect::updateBlurTexture(const QVector
<QRect
>& rects
, double blurAmount
)
341 // First pass (vertical)
343 effects
->pushRenderTarget(mTmpTarget
);
344 mBlurShader
->setAttribute("xBlur", 0.0f
);
345 mBlurShader
->setAttribute("yBlur", float(blurAmount
) );
347 mSceneTexture
->bind();
349 foreach( const QRect
&r
, rects
)
351 // We change x coordinates here because horizontal blur pass (which
352 // comes after this one) also uses pixels that are horizontally edging
353 // the blurred area. Thus we need to make sure that those pixels are
356 glVertex2f( r
.x()-mBlurRadius
, r
.y() + r
.height() );
357 glVertex2f( r
.x() + r
.width()+mBlurRadius
, r
.y() + r
.height() );
358 glVertex2f( r
.x() + r
.width()+mBlurRadius
, r
.y() );
359 glVertex2f( r
.x()-mBlurRadius
, r
.y() );
364 mSceneTexture
->unbind();
365 effects
->popRenderTarget();
367 // Second pass (horizontal)
368 effects
->pushRenderTarget(mBlurTarget
);
369 mBlurShader
->setAttribute("xBlur", float(blurAmount
) );
370 mBlurShader
->setAttribute("yBlur", 0.0f
);
374 foreach( const QRect
&r
, rects
)
377 glVertex2f( r
.x() , r
.y() + r
.height() );
378 glVertex2f( r
.x() + r
.width(), r
.y() + r
.height() );
379 glVertex2f( r
.x() + r
.width(), r
.y() );
380 glVertex2f( r
.x() , r
.y() );
385 mTmpTexture
->unbind();
386 effects
->popRenderTarget();
387 mBlurShader
->unbind();