kwin/effects/shadow/shadow.cpp
Marco Martin f6bf95cb44 add a proxy effect to the fade effect, used to add and remove ignored
windows.
the slidingpopups effect adds its managed windows to the ignored list of
the fadeeffect, making them mutually exclusive

svn path=/trunk/KDE/kdebase/workspace/; revision=1021321
2009-09-08 20:42:45 +00:00

754 lines
32 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com>
Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "shadow.h"
#include "shadow_helper.h"
#include <kwinglutils.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <KStandardDirs>
#include <kcolorscheme.h>
#include <KGlobalSettings>
#include <cmath>
namespace KWin
{
KWIN_EFFECT( shadow, ShadowEffect )
ShadowEffect::ShadowEffect()
: shadowSize( 0 )
{
cachedBlendPicture = X::None;
reconfigure( ReconfigureAll );
connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
this, SLOT(updateShadowColor()));
}
ShadowEffect::~ShadowEffect()
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
delete mDefaultShadowTextures.at( i );
#endif
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
for( int i = 0; i < mDefaultShadowPics.size(); i++ )
delete mDefaultShadowPics.at( i );
#endif
}
void ShadowEffect::reconfigure( ReconfigureFlags )
{
KConfigGroup conf = effects->effectConfig("Shadow");
shadowXOffset = conf.readEntry( "XOffset", 0 );
shadowYOffset = conf.readEntry( "YOffset", 3 );
shadowOpacity = conf.readEntry( "Opacity", 0.25 );
shadowFuzzyness = conf.readEntry( "Fuzzyness", 10 );
shadowSize = conf.readEntry( "Size", 5 );
intensifyActiveShadow = conf.readEntry( "IntensifyActiveShadow", true );
updateShadowColor();
// Load decoration shadow related things
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
if( effects->compositingType() == OpenGLCompositing )
{
// Delete any other textures in memory
for( int i = 0; i < mDefaultShadowTextures.size(); i++ )
delete mDefaultShadowTextures.at( i );
mDefaultShadowTextures.clear();
//-------------------------
// Create default textures
mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister?
QImage shadowImageSrc( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" ));
// Might as well process the shadow color here
QImage shadowImage( shadowImageSrc.width(), shadowImageSrc.height(), QImage::Format_ARGB32 );
QPainter painter( &shadowImage );
//painter.setCompositionMode( QPainter::CompositionMode_Multiply );
painter.setPen( Qt::NoPen );
painter.setBrush( shadowColor );
painter.drawRect( 0, 0, shadowImage.width(), shadowImage.height() );
painter.end();
shadowImage.setAlphaChannel( shadowImageSrc.alphaChannel() ); // Cheat, just use the alpha mask
int hw = shadowImage.width() / 2;
int hh = shadowImage.height() / 2;
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, 0, hw, hh )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, 1, hh )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, 0, hw, hh )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, 1 )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, 1 )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, 1 )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( 0, hh, hw, hh )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, 1, hh )));
mDefaultShadowTextures.append( new GLTexture( shadowImage.copy( hw, hh, hw, hh )));
}
#endif
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if( effects->compositingType() == XRenderCompositing )
{
// Delete any other pictures in memory
for( int i = 0; i < mDefaultShadowPics.size(); i++ )
delete mDefaultShadowPics.at( i );
mDefaultShadowPics.clear();
// Create default pictures
mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister?
QPixmap shadowPixmap( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" ));
shadowPixmap = shadowPixmap.scaled( QSize( shadowFuzzyness * 4, shadowFuzzyness * 4 ),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
int hw = shadowPixmap.width() / 2;
int hh = shadowPixmap.height() / 2;
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, 0, hw, hh )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, 1, hh )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, 0, hw, hh )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, 1 )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, 1 )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, 1 )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( 0, hh, hw, hh )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, 1, hh )));
mDefaultShadowPics.append( new XRenderPicture( shadowPixmap.copy( hw, hh, hw, hh )));
// Apply repeat attribute to all pictures
XRenderPictureAttributes pa;
pa.repeat = true;
for( int i = 0; i < mDefaultShadowPics.size(); i++ )
XRenderChangePicture( display(), *mDefaultShadowPics.at( i ), CPRepeat, &pa );
}
#endif
// Force rebuild of all quads to clear their caches
foreach( EffectWindow *w, effects->stackingOrder() )
if( w ) // TODO/HACK: stackingOrder() should NOT return NULL
w->buildQuads( true );
}
void ShadowEffect::updateShadowColor()
{
KConfigGroup conf = effects->effectConfig("Shadow");
shadowColor = conf.readEntry( "Color", schemeShadowColor() );
}
QRect ShadowEffect::shadowRectangle( EffectWindow* w, const QRect& windowRectangle ) const
{
int shadowGrow = shadowFuzzyness + shadowSize;
return windowRectangle.adjusted( shadowXOffset - shadowGrow, shadowYOffset - shadowGrow,
shadowXOffset + shadowGrow, shadowYOffset + shadowGrow);
}
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
static ScreenPaintData gScreenData;
#endif
void ShadowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
{
shadowDatas.clear();
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if ((mask & PAINT_SCREEN_TRANSFORMED) &&
(effects->compositingType() == XRenderCompositing)) // TODO: copy constructor?
{
gScreenData.xTranslate = data.xTranslate;
gScreenData.yTranslate = data.yTranslate;
gScreenData.xScale = data.xScale;
gScreenData.yScale = data.yScale;
}
#endif
// Draw windows
effects->paintScreen( mask, region, data );
// Draw shadows
drawQueuedShadows( 0 );
}
void ShadowEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
{
if( useShadow( w ))
data.paint |= shadowRectangle( w, data.paint.boundingRect() );
effects->prePaintWindow( w, data, time );
}
void ShadowEffect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
// Whether the shadow drawing can be delayed or not.
bool optimize = !(mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED |
PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_WINDOW_TRANSLUCENT | PAINT_DECORATION_ONLY ));
QRegion rgn = region;
if ( w->keepAbove() )
{
QRegion shape = w->shape().translated( w->x(), w->y());
if (optimize)
{
rgn -= shape;
foreach( ShadowData d, shadowDatas )
d.clip |= shape;
}
optimize = false;
}
if( !optimize )
{
// Transformed or translucent windows are drawn bottom-to-top, so
// first we need to draw all queued shadows.
drawQueuedShadows( w );
}
if( useShadow( w ) && (!(mask & PAINT_DECORATION_ONLY) || w->keepAbove()) )
{
if( !optimize )
{
// For translucent windows, shadow needs to be drawn before the
// window itself.
drawShadow( w, mask, rgn, data );
}
else
{
// For opaque windows, just schedule the shadow to be drawn later
ShadowData d(w, data);
d.clip = w->shape().translated( w->x(), w->y());
if( !shadowDatas.isEmpty())
d.clip |= shadowDatas.last().clip;
d.mask = mask;
foreach( const QRect &r, region.rects() )
d.region |= shadowRectangle( w, r );
d.region &= region;
shadowDatas.append(d);
}
}
effects->drawWindow( w, mask, region, data );
}
void ShadowEffect::buildQuads( EffectWindow* w, WindowQuadList& quadList )
{
// TODO: add config option to not have shadows for menus, etc.
// Make our own shadow as the decoration doesn't support it
int fuzzy = shadowFuzzyness;
// Shadow's size must be a least 2*fuzzy in both directions (or the corners will be broken)
int width = qMax( fuzzy * 2, w->width() + 2 * shadowSize );
int height = qMax( fuzzy * 2, w->height() + 2 * shadowSize );
double x1, y1, x2, y2;
int id = 0;
// top-left
x1 = shadowXOffset - shadowSize + 0 - fuzzy;
y1 = shadowYOffset - shadowSize + 0 - fuzzy;
x2 = shadowXOffset - shadowSize + 0 + fuzzy;
y2 = shadowYOffset - shadowSize + 0 + fuzzy;
WindowQuad topLeftQuad( mDefaultShadowQuadType, id++ );
topLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
topLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
topLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
topLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( topLeftQuad );
// top
x1 = shadowXOffset - shadowSize + 0 + fuzzy;
y1 = shadowYOffset - shadowSize + 0 - fuzzy;
x2 = shadowXOffset - shadowSize + width - fuzzy;
y2 = shadowYOffset - shadowSize + 0 + fuzzy;
WindowQuad topQuad( mDefaultShadowQuadType, id++ );
topQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
topQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
topQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
topQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( topQuad );
// top-right
x1 = shadowXOffset - shadowSize + width - fuzzy;
y1 = shadowYOffset - shadowSize + 0 - fuzzy;
x2 = shadowXOffset - shadowSize + width + fuzzy;
y2 = shadowYOffset - shadowSize + 0 + fuzzy;
WindowQuad topRightQuad( mDefaultShadowQuadType, id++ );
topRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
topRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
topRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
topRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( topRightQuad );
// left
x1 = shadowXOffset - shadowSize + 0 - fuzzy;
y1 = shadowYOffset - shadowSize + 0 + fuzzy;
x2 = shadowXOffset - shadowSize + 0 + fuzzy;
y2 = shadowYOffset - shadowSize + height - fuzzy;
WindowQuad leftQuad( mDefaultShadowQuadType, id++ );
leftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
leftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
leftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
leftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( leftQuad );
// center
x1 = shadowXOffset - shadowSize + 0 + fuzzy;
y1 = shadowYOffset - shadowSize + 0 + fuzzy;
x2 = shadowXOffset - shadowSize + width - fuzzy;
y2 = shadowYOffset - shadowSize + height - fuzzy;
WindowQuad contentsQuad( mDefaultShadowQuadType, id++ );
contentsQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
contentsQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
contentsQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
contentsQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( contentsQuad );
// right
x1 = shadowXOffset - shadowSize + width - fuzzy;
y1 = shadowYOffset - shadowSize + 0 + fuzzy;
x2 = shadowXOffset - shadowSize + width + fuzzy;
y2 = shadowYOffset - shadowSize + height - fuzzy;
WindowQuad rightQuad( mDefaultShadowQuadType, id++ );
rightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
rightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
rightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
rightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( rightQuad );
// bottom-left
x1 = shadowXOffset - shadowSize + 0 - fuzzy;
y1 = shadowYOffset - shadowSize + height - fuzzy;
x2 = shadowXOffset - shadowSize + 0 + fuzzy;
y2 = shadowYOffset - shadowSize + height + fuzzy;
WindowQuad bottomLeftQuad( mDefaultShadowQuadType, id++ );
bottomLeftQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
bottomLeftQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
bottomLeftQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
bottomLeftQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( bottomLeftQuad );
// bottom
x1 = shadowXOffset - shadowSize + 0 + fuzzy;
y1 = shadowYOffset - shadowSize + height - fuzzy;
x2 = shadowXOffset - shadowSize + width - fuzzy;
y2 = shadowYOffset - shadowSize + height + fuzzy;
WindowQuad bottomQuad( mDefaultShadowQuadType, id++ );
bottomQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
bottomQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
bottomQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
bottomQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( bottomQuad );
// bottom-right
x1 = shadowXOffset - shadowSize + width - fuzzy;
y1 = shadowYOffset - shadowSize + height - fuzzy;
x2 = shadowXOffset - shadowSize + width + fuzzy;
y2 = shadowYOffset - shadowSize + height + fuzzy;
WindowQuad bottomRightQuad( mDefaultShadowQuadType, id++ );
bottomRightQuad[ 0 ] = WindowVertex( x1, y1, 0, 1 );
bottomRightQuad[ 1 ] = WindowVertex( x2, y1, 1, 1 );
bottomRightQuad[ 2 ] = WindowVertex( x2, y2, 1, 0 );
bottomRightQuad[ 3 ] = WindowVertex( x1, y2, 0, 0 );
quadList.append( bottomRightQuad );
effects->buildQuads( w, quadList );
}
QRect ShadowEffect::transformWindowDamage( EffectWindow* w, const QRect& r )
{
if( !useShadow( w ))
return effects->transformWindowDamage( w, r );
QRect r2 = r | shadowRectangle( w, r );
return effects->transformWindowDamage( w, r2 );
}
void ShadowEffect::windowClosed( EffectWindow* c )
{
effects->addRepaint( shadowRectangle( c, c->geometry() ));
}
bool ShadowEffect::useShadow( EffectWindow* w ) const
{
return !w->isDeleted() && !w->isDesktop()
// popups and docks may have shadow even if shaped, their shape is almost rectangular
&& ( !w->hasOwnShape() || w->isDropdownMenu() || w->isPopupMenu() || w->isComboBox() || w->isDock())
// If decoration has it's own shadow leave it alone
&& !( w->hasDecoration() && effects->hasDecorationShadows() );
}
void ShadowEffect::addQuadVertices(QVector<float>& verts, float x1, float y1, float x2, float y2) const
{
verts << x1 << y1;
verts << x1 << y2;
verts << x2 << y2;
verts << x2 << y1;
}
void ShadowEffect::drawQueuedShadows( EffectWindow* behindWindow )
{
QList<ShadowData> newShadowDatas;
QList<ShadowData> thisTime;
EffectWindowList stack = effects->stackingOrder();
foreach( const ShadowData &d, shadowDatas )
{
// If behindWindow is given then only render shadows of the windows
// that are behind that window.
if( !behindWindow || stack.indexOf(d.w) < stack.indexOf(behindWindow))
thisTime.append(d);
else
newShadowDatas.append(d);
}
if( thisTime.count() )
{ // Render them in stacking order
foreach( EffectWindow *w, stack )
for( int i = 0; i < thisTime.size(); i++ )
{ // Cannot use foreach() due to thisTime.removeOne()
const ShadowData d = thisTime.at(i);
if( d.w == w )
{
drawShadow( d.w, d.mask,
d.region.subtracted( d.clip ), d.data );
thisTime.removeAt( i );
break;
}
}
}
// Render the rest on the top (For menus, etc.)
foreach( const ShadowData &d, thisTime )
drawShadow( d.w, d.mask, d.region.subtracted( d.clip ), d.data );
shadowDatas = newShadowDatas;
}
// Modified version of SceneOpenGL::Window::prepareRenderStates() from scene_opengl.cpp
void ShadowEffect::prepareRenderStates( GLTexture *texture, double opacity, double brightness, double saturation )
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
// setup blending of transparent windows
glPushAttrib( GL_ENABLE_BIT );
/*if( saturation != 1.0 && texture->saturationSupported() )
{
// First we need to get the color from [0; 1] range to [0.5; 1] range
glActiveTexture( GL_TEXTURE0 );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5 };
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant );
texture->bind();
// Then we take dot product of the result of previous pass and
// saturation_constant. This gives us completely unsaturated
// (greyscale) image
// Note that both operands have to be in range [0.5; 1] since opengl
// automatically substracts 0.5 from them
glActiveTexture( GL_TEXTURE1 );
float saturation_constant[] = { 0.5 + 0.5*0.30, 0.5 + 0.5*0.59, 0.5 + 0.5*0.11, saturation };
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
texture->bind();
// Finally we need to interpolate between the original image and the
// greyscale image to get wanted level of saturation
glActiveTexture( GL_TEXTURE2 );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant );
// Also replace alpha by primary color's alpha here
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
// And make primary color contain the wanted opacity
glColor4f( opacity, opacity, opacity, opacity );
texture->bind();
if( brightness != 1.0 )
{
glActiveTexture( GL_TEXTURE3 );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
// The color has to be multiplied by both opacity and brightness
float opacityByBrightness = opacity * brightness;
glColor4f( opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS );
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
texture->bind();
}
glActiveTexture(GL_TEXTURE0 );
}
else*/ if( opacity != 1.0 || brightness != 1.0 )
{ // The window is additionally configured to have its opacity adjusted
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glColor4f( brightness, brightness, brightness, opacity );
}
#endif
}
// Modified version of SceneOpenGL::Window::restoreRenderStates() from scene_opengl.cpp
void ShadowEffect::restoreRenderStates( GLTexture *texture, double opacity, double brightness, double saturation )
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
if( opacity != 1.0 || saturation != 1.0 || brightness != 1.0 )
{
/*if( saturation != 1.0 && texture->saturationSupported())
{
glActiveTexture(GL_TEXTURE3);
glDisable( texture->target() );
glActiveTexture(GL_TEXTURE2);
glDisable( texture->target() );
glActiveTexture(GL_TEXTURE1);
glDisable( texture->target() );
glActiveTexture(GL_TEXTURE0);
}*/
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glColor4f( 0.0, 0.0, 0.0, 0.0 );
}
glPopAttrib(); // ENABLE_BIT
#endif
}
void ShadowEffect::drawShadowQuadOpenGL( GLTexture *texture, QVector<float> verts, QVector<float> texCoords,
QRegion region, float opacity, float brightness, float saturation, GLShader* shader )
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
prepareRenderStates( texture, opacity, brightness, saturation ); // Usually overrides the above
if( shader )
{
shader->setUniform("opacity", opacity);
shader->setUniform("saturation", saturation);
shader->setUniform("brightness", brightness);
shader->setUniform("textureWidth", 1.0f);
shader->setUniform("textureHeight", 1.0f);
}
texture->bind();
texture->enableNormalizedTexCoords();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
renderGLGeometry( region, 4, verts.data(), texCoords.data() );
texture->disableNormalizedTexCoords();
texture->unbind();
restoreRenderStates( texture, opacity, brightness, saturation );
#endif
}
void ShadowEffect::drawShadowQuadXRender( XRenderPicture *picture, QRect rect, float xScale, float yScale,
QColor color, float opacity, float brightness, float saturation )
{
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
XRenderPicture fill;
if( color.isValid() )
{
if ( color == cachedColor && cachedBlendPicture != X::None )
fill = cachedBlendPicture;
else
{
XRenderColor xc = preMultiply( color, opacity );
cachedBlendPicture = fill = xRenderFill( &xc );
cachedColor = color;
}
}
else
fill = xRenderBlendPicture(opacity);
// Scale if required
if( xScale != 1.0 || yScale != 1.0 )
{
XTransform xform = {{
{ XDoubleToFixed( 1.0 / xScale ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) },
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 / yScale ), XDoubleToFixed( 0.0 ) },
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) }
}};
XRenderSetPictureTransform( display(), *picture, &xform );
}
// Render it
// TODO: This always uses the fast filter, detect when to use smooth instead
if( color.isValid() )
XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0,
rect.x(), rect.y(), rect.width(), rect.height() );
else
XRenderComposite( display(), PictOpOver, *picture, fill, effects->xrenderBufferPicture(), 0, 0, 0, 0,
rect.x(), rect.y(), rect.width(), rect.height() );
// Fake brightness by overlaying black
// Cannot use XRenderFillRectangle() due to ARGB
fill = xRenderBlendPicture(( 1 - brightness ) * opacity);
XRenderComposite( display(), PictOpOver, fill, *picture, effects->xrenderBufferPicture(), 0, 0, 0, 0,
rect.x(), rect.y(), rect.width(), rect.height() );
// Return to scale to 1.0
if( xScale != 1.0 || yScale != 1.0 )
{
XTransform xform = {{
{ XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) },
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ) },
{ XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) }
}};
XRenderSetPictureTransform( display(), *picture, &xform );
}
#endif
}
void ShadowEffect::drawShadow( EffectWindow* window, int mask, QRegion region, const WindowPaintData& data )
{
// Don't allow windows to cast shadows on other displays
QRegion clipperGeom;
for( int screen = 0; screen < effects->numScreens(); screen++ )
{
QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 );
if( !( window->geometry() & screenGeom ).isNull() )
clipperGeom |= screenGeom;
}
PaintClipper pc( clipperGeom );
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
if( effects->compositingType() == OpenGLCompositing )
{
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
foreach( const WindowQuad &quad, data.quads )
{
if( quad.type() != mDefaultShadowQuadType )
continue; // Not a shadow quad
glPushMatrix();
// Use the window's top-left as the origin
glTranslatef( window->x(), window->y(), 0 );
if( mask & PAINT_WINDOW_TRANSFORMED )
glTranslatef( data.xTranslate, data.yTranslate, data.zTranslate );
if(( mask & PAINT_WINDOW_TRANSFORMED ) && ( data.xScale != 1 || data.yScale != 1 ))
glScalef( data.xScale, data.yScale, data.zScale );
if(( mask & PAINT_WINDOW_TRANSFORMED ) && data.rotation )
{
glTranslatef( data.rotation->xRotationPoint,
data.rotation->yRotationPoint,
data.rotation->zRotationPoint );
float xAxis = 0.0;
float yAxis = 0.0;
float zAxis = 0.0;
switch( data.rotation->axis )
{
case RotationData::XAxis:
xAxis = 1.0;
break;
case RotationData::YAxis:
yAxis = 1.0;
break;
case RotationData::ZAxis:
zAxis = 1.0;
break;
}
glRotatef( data.rotation->angle, xAxis, yAxis, zAxis );
glTranslatef( -data.rotation->xRotationPoint,
-data.rotation->yRotationPoint,
-data.rotation->zRotationPoint );
}
// Create our polygon
QVector<float> verts, texcoords;
verts.reserve(8);
texcoords.reserve(8);
verts << quad[0].x() << quad[0].y();
verts << quad[1].x() << quad[1].y();
verts << quad[2].x() << quad[2].y();
verts << quad[3].x() << quad[3].y();
texcoords << quad[0].textureX() << quad[0].textureY();
texcoords << quad[1].textureX() << quad[1].textureY();
texcoords << quad[2].textureX() << quad[2].textureY();
texcoords << quad[3].textureX() << quad[3].textureY();
// Work out which texture to use
float opacity = shadowOpacity;
if( intensifyActiveShadow && window == effects->activeWindow() )
opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity );
drawShadowQuadOpenGL( mDefaultShadowTextures.at( quad.id() ),
verts, texcoords, region,
data.opacity * opacity,
data.brightness,
data.saturation,
data.shader);
glPopMatrix();
}
glPopAttrib();
}
#endif
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if( effects->compositingType() == XRenderCompositing )
{
XRenderSetPictureClipRegion( display(), effects->xrenderBufferPicture(), region.handle() );
foreach( const WindowQuad &quad, data.quads )
{
if( quad.type() != mDefaultShadowQuadType )
continue; // Not a shadow quad
// Determine transformed quad position
QRect windowRect = window->geometry();
float xScale = 1.0;
float yScale = 1.0;
float xTranslate = 0.0;
float yTranslate = 0.0;
if( mask & PAINT_SCREEN_TRANSFORMED)
{
xScale = gScreenData.xScale;
yScale = gScreenData.yScale;
xTranslate += ( xScale - 1.0 ) * windowRect.x() + gScreenData.xTranslate;
yTranslate += ( yScale - 1.0 ) * windowRect.y() + gScreenData.yTranslate;
}
if( mask & PAINT_WINDOW_TRANSFORMED)
{
xTranslate += xScale * data.xTranslate;
yTranslate += yScale * data.yTranslate;
xScale *= data.xScale;
yScale *= data.yScale;
}
QRect quadRect(
window->x() + quad[0].x() * xScale + xTranslate,
window->y() + quad[0].y() * yScale + yTranslate,
( quad[2].x() - quad[0].x() ) * xScale,
( quad[2].y() - quad[0].y() ) * yScale );
// Work out which texture to use
float opacity = shadowOpacity;
if( intensifyActiveShadow && window == effects->activeWindow() )
opacity = 1 - ( 1 - shadowOpacity ) * ( 1 - shadowOpacity );
drawShadowQuadXRender( mDefaultShadowPics.at( quad.id() ), quadRect, xScale, yScale,
shadowColor, opacity * data.opacity, data.brightness, data.saturation );
}
}
#endif
}
} // namespace