not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / effects / blur.cpp
blobe880c629c05819d13c372e516abf1311d1d36ff6
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 *********************************************************************/
21 #include "blur.h"
23 #include <kwinglutils.h>
25 #include <QString>
26 #include <KStandardDirs>
28 #include <kdebug.h>
31 namespace KWin
34 KWIN_EFFECT( blur, BlurEffect )
35 KWIN_EFFECT_SUPPORTED( blur, BlurEffect::supported() )
38 BlurEffect::BlurEffect() : Effect()
40 mSceneTexture = 0;
41 mTmpTexture = 0;
42 mBlurTexture = 0;
43 mSceneTarget = 0;
44 mTmpTarget = 0;
45 mBlurTarget = 0;
46 mBlurShader = 0;
47 mWindowShader = 0;
49 mBlurRadius = 4;
50 mValid = loadData();
51 if( !mValid )
53 kWarning(1212) << "Loading failed";
55 effects->addRepaintFull();
58 BlurEffect::~BlurEffect()
60 effects->addRepaintFull();
61 delete mSceneTexture;
62 delete mTmpTexture;
63 delete mBlurTexture;
64 delete mSceneTarget;
65 delete mTmpTarget;
66 delete mBlurTarget;
67 delete mBlurShader;
68 delete mWindowShader;
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() )
91 return false;
92 mTmpTarget = new GLRenderTarget(mTmpTexture);
93 if( !mTmpTarget->valid() )
94 return false;
95 mBlurTarget = new GLRenderTarget(mBlurTexture);
96 if( !mBlurTarget->valid() )
97 return false;
99 mBlurShader = loadShader("blur");
100 if( !mBlurShader )
101 return false;
102 mWindowShader = loadShader("blur-render");
103 if( !mWindowShader )
104 return false;
106 mBlurShader->bind();
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();
119 return true;
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;
129 return false;
131 GLShader* shader = new GLShader(vertexshader, fragmentshader);
132 if(!shader->isValid())
134 kError(1212) << "Shader '" << name << "' failed to load!" << endl;
135 delete shader;
136 return 0;
138 return shader;
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 );
154 expandedregion += r;
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 );
204 glPushMatrix();
205 glLoadIdentity();
208 GLTexture* tex = mSceneTexture;
209 int pixels = 0;
210 tex->bind();
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();
217 glBegin(GL_QUADS);
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() );
222 glEnd();
223 pixels += r.width()*r.height();
225 tex->disableUnnormalizedTexCoords();
226 tex->unbind();
228 if( mask & PAINT_SCREEN_TRANSFORMED )
230 // Restore the original matrix
231 glPopMatrix();
233 // kDebug(1212) << "Copied" << mScreenDirty.rects().count() << "rects, pixels:" << pixels;
237 else
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 )
260 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
271 glPushMatrix();
272 glLoadIdentity();
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
276 // entire screen
277 if( mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS ) )
278 updateBlurTexture( QRegion(0, 0, displayWidth(), displayHeight()) , blurAmount );
279 else
280 updateBlurTexture( mBlurDirty , blurAmount );
281 mBlurDirty = QRegion();
282 if( mask & PAINT_SCREEN_TRANSFORMED )
283 // Restore the original matrix
284 glPopMatrix();
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);
294 // Paint
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();
303 else
305 // Opaque window
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;
313 else
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();
322 int totalarea = 0;
323 foreach( const QRect &r, rects )
324 totalarea += r.width() * r.height();
325 if( (int)(totalarea * 1.33 + 100 ) < bounding.width() * bounding.height() )
327 // Use small rects
328 updateBlurTexture(rects, blurAmount);
330 else
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 )
340 // Blur
341 // First pass (vertical)
342 mBlurShader->bind();
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
354 // also updated.
355 glBegin(GL_QUADS);
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() );
360 glEnd();
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);
372 mTmpTexture->bind();
374 foreach( const QRect &r, rects )
376 glBegin(GL_QUADS);
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() );
381 glEnd();
385 mTmpTexture->unbind();
386 effects->popRenderTarget();
387 mBlurShader->unbind();
390 } // namespace