From 37f7fdacfd4c9a632ec22c1b5bfe18069ba5d6ef Mon Sep 17 00:00:00 2001 From: Lucas Murray Date: Mon, 24 Nov 2008 13:52:25 +0000 Subject: [PATCH] Added support for decoration shadows in XRender mode. Fixed memory leak where the default shadow textures were not being deleted when shadows are reloaded. Renamed a few OpenGL variables so they are not as confusing. svn path=/trunk/KDE/kdebase/workspace/; revision=888449 --- effects/shadow.cpp | 366 +++++++++++++++++++++++++++------------------ effects/shadow.h | 18 +-- 2 files changed, 223 insertions(+), 161 deletions(-) diff --git a/effects/shadow.cpp b/effects/shadow.cpp index d6a84659a9..8eb56d28b7 100644 --- a/effects/shadow.cpp +++ b/effects/shadow.cpp @@ -30,9 +30,6 @@ along with this program. If not, see . #include #include #include -#ifdef KWIN_HAVE_XRENDER_COMPOSITING -#include -#endif #include @@ -41,48 +38,8 @@ namespace KWin KWIN_EFFECT( shadow, ShadowEffect ) -#ifdef KWIN_HAVE_XRENDER_COMPOSITING -ShadowTiles::ShadowTiles(const QPixmap& shadow) - { - int w = shadow.width() / 2, h = shadow.height() / 2; - cornerSize = QSize(w, h); -#define DUMP_CNR(_TILE_, _W_, _H_, _XOFF_, _YOFF_)\ - dump = QPixmap(_W_, _H_);\ - dump.fill(Qt::transparent);\ - p.begin(&dump);\ - p.drawPixmap( 0, 0, shadow, _XOFF_, _YOFF_, _W_, _H_ );\ - p.end();\ - _TILE_ = dump - - QPixmap dump; QPainter p; - DUMP_CNR(topLeft, w, h, 0, 0); - DUMP_CNR(topRight, w, h, w, 0); - DUMP_CNR(btmLeft, w, h, 0, h); - DUMP_CNR(btmRight, w, h, w, h); - - XRenderPictureAttributes pa; pa.repeat = True; -#define DUMP_TILE(_TILE_, _W_, _H_, _XOFF_, _YOFF_)\ - DUMP_CNR(_TILE_, _W_, _H_, _XOFF_, _YOFF_);\ - XRenderChangePicture (display(), _TILE_, CPRepeat, &pa) - - DUMP_TILE(top, 1, h, w, 0); - DUMP_TILE(btm, 1, h, w, h); - DUMP_TILE(left, w, 1, 0, h); - DUMP_TILE(right, w, 1, w, h); - - DUMP_TILE(center, 1, 1, w, h); - } - -#undef DUMP_CNR -#undef DUMP_TILE - -#endif - ShadowEffect::ShadowEffect() : shadowSize( 0 ) -#ifdef KWIN_HAVE_XRENDER_COMPOSITING - , mShadowPics( NULL ) -#endif { reconfigure( ReconfigureAll ); connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), @@ -95,9 +52,15 @@ ShadowEffect::~ShadowEffect() for( int i = 0; i < mShadowTextures.size(); i++ ) for( int j = 0; j < mShadowTextures.at( i ).size(); j++ ) delete mShadowTextures.at( i ).at( j ); + for( int i = 0; i < mDefaultShadowTextures.size(); i++ ) + delete mDefaultShadowTextures.at( i ); #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING - delete mShadowPics; + for( int i = 0; i < mShadowPics.size(); i++ ) + for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) + delete mShadowPics.at( i ).at( j ); + for( int i = 0; i < mDefaultShadowPics.size(); i++ ) + delete mDefaultShadowPics.at( i ); #endif } @@ -110,31 +73,6 @@ void ShadowEffect::reconfigure( ReconfigureFlags ) shadowFuzzyness = conf.readEntry( "Fuzzyness", 10 ); shadowSize = conf.readEntry( "Size", 5 ); intensifyActiveShadow = conf.readEntry( "IntensifyActiveShadow", true ); -#ifdef KWIN_HAVE_XRENDER_COMPOSITING - delete mShadowPics; - mShadowPics = NULL; - if ( effects->compositingType() == XRenderCompositing) - { - qreal size = 2*(shadowFuzzyness+shadowSize)+1; - QPixmap *shadow = new QPixmap(size, size); shadow->fill(Qt::transparent); - size /= 2.0; - QRadialGradient rg(size, size, size); - QColor c(0,0,0,255); - rg.setColorAt(0, c); - c.setAlpha(0.3*c.alpha()); - if (shadowSize > 0) - rg.setColorAt(((float)shadowSize)/(shadowFuzzyness+shadowSize), c); - c.setAlpha(0); rg.setColorAt(0.8, c); - QPainter p(shadow); - p.setRenderHint(QPainter::Antialiasing); - p.setPen(Qt::NoPen); p.setBrush(rg); - p.drawRect(shadow->rect()); - p.end(); - - mShadowPics = new ShadowTiles(*shadow); - delete shadow; - } -#endif updateShadowColor(); // Load decoration shadow related things @@ -145,37 +83,96 @@ void ShadowEffect::reconfigure( ReconfigureFlags ) #ifdef KWIN_HAVE_OPENGL_COMPOSITING if( effects->compositingType() == OpenGLCompositing ) { + // Delete any other textures in memory for( int i = 0; i < mShadowTextures.size(); i++ ) for( int j = 0; j < mShadowTextures.at( i ).size(); j++ ) delete mShadowTextures.at( i ).at( j ); mShadowTextures.clear(); + for( int i = 0; i < mDefaultShadowTextures.size(); i++ ) + delete mDefaultShadowTextures.at( i ); + mDefaultShadowTextures.clear(); + + // Create decoration shadows if( effects->hasDecorationShadows() ) { - QList< QList > shadowTextures = effects->shadowTextures(); - for( int i = 0; i < shadowTextures.size(); i++ ) + QList< QList > shadowImages = effects->shadowTextures(); + for( int i = 0; i < shadowImages.size(); i++ ) { mShadowQuadTypes.append( effects->newWindowQuadType() ); QList textures; - for( int j = 0; j < shadowTextures.at( i ).size(); j++ ) - textures.append( new GLTexture( shadowTextures.at( i ).at( j ))); + for( int j = 0; j < shadowImages.at( i ).size(); j++ ) + textures.append( new GLTexture( shadowImages.at( i ).at( j ))); mShadowTextures.append( textures ); } } + // Create default textures mDefaultShadowQuadType = effects->newWindowQuadType(); // TODO: Unregister? - QImage shadowTexture( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" )); - int hw = shadowTexture.width() / 2; - int hh = shadowTexture.height() / 2; - mDefaultShadowTextures.clear(); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( 0, 0, hw, hh ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, 0, 1, hh ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, 0, hw, hh ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( 0, hh, hw, 1 ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, hh, 1, 1 ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, hh, hw, 1 ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( 0, hh, hw, hh ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, hh, 1, hh ))); - mDefaultShadowTextures.append( new GLTexture( shadowTexture.copy( hw, hh, hw, hh ))); + QImage shadowImage( KGlobal::dirs()->findResource( "data", "kwin/shadow-texture.png" )); + 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 < mShadowPics.size(); i++ ) + for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) + delete mShadowPics.at( i ).at( j ); + mShadowPics.clear(); + for( int i = 0; i < mDefaultShadowPics.size(); i++ ) + delete mDefaultShadowPics.at( i ); + mDefaultShadowPics.clear(); + + // Create decoration pictures + if( effects->hasDecorationShadows() ) + { + QList< QList > shadowImages = effects->shadowTextures(); + for( int i = 0; i < shadowImages.size(); i++ ) + { + mShadowQuadTypes.append( effects->newWindowQuadType() ); + QList pictures; + for( int j = 0; j < shadowImages.at( i ).size(); j++ ) + pictures.append( new XRenderPicture( QPixmap::fromImage( shadowImages.at( i ).at( j )))); + mShadowPics.append( pictures ); + } + } + + // 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 < mShadowPics.size(); i++ ) + for( int j = 0; j < mShadowPics.at( i ).size(); j++ ) + XRenderChangePicture( display(), *mShadowPics.at( i ).at( j ), CPRepeat, &pa ); + for( int i = 0; i < mDefaultShadowPics.size(); i++ ) + XRenderChangePicture( display(), *mDefaultShadowPics.at( i ), CPRepeat, &pa ); } #endif @@ -271,8 +268,6 @@ void ShadowEffect::drawWindow( EffectWindow* w, int mask, QRegion region, Window void ShadowEffect::buildQuads( EffectWindow* w, WindowQuadList& quadList ) { - if( effects->compositingType() == XRenderCompositing ) - return; // TODO: Disable quad-based shadows in XRender mode for the moment bool shadowDefined = false; if( effects->hasDecorationShadows() ) { @@ -614,10 +609,52 @@ void ShadowEffect::restoreRenderStates( GLTexture *texture, double opacity, doub #endif } +void ShadowEffect::drawShadowQuadXRender( XRenderPicture *picture, QRect rect, float xScale, float yScale, + QColor color, float opacity, float brightness, float saturation ) + { + XRenderColor xc; + if( color.isValid() ) + xc = preMultiply( color, opacity ); + else + xc = preMultiply( QColor( 255, 255, 255 ), opacity ); + XRenderPicture fill = xRenderFill( &xc ); + + // 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() ); + + // 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 ); + } + } + void ShadowEffect::drawShadow( EffectWindow* window, int mask, QRegion region, const WindowPaintData& data ) { #ifdef KWIN_HAVE_OPENGL_COMPOSITING - if( effects->compositingType() == OpenGLCompositing) + if( effects->compositingType() == OpenGLCompositing ) { glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT ); glEnable( GL_BLEND ); @@ -845,74 +882,107 @@ void ShadowEffect::drawShadow( EffectWindow* window, int mask, QRegion region, c } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING - if( effects->compositingType() == XRenderCompositing) + if( effects->compositingType() == XRenderCompositing ) { - // calculate opacity ================================================= - float opacity; - if( intensifyActiveShadow && window == effects->activeWindow() ) + XRenderSetPictureClipRegion( display(), effects->xrenderBufferPicture(), region.handle() ); + + foreach( const WindowQuad &quad, data.quads ) { - opacity = (1 - (1 - shadowOpacity)*(1 - shadowOpacity)) * data.opacity; - } - else - { - opacity = data.opacity * shadowOpacity; - } - - // query rect and translate in case (may have impact on opacity)=========================== - QRect r = window->geometry(); - if ( mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)) - { - float xScale = 1.0, yScale = 1.0, xTranslate = 0.0, yTranslate = 0.0; - if ( mask & PAINT_SCREEN_TRANSFORMED) + if( !mShadowQuadTypes.contains( quad.type() ) && 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)*r.x() + gScreenData.xTranslate; - yTranslate += (yScale-1.0)*r.y() + gScreenData.yTranslate; + 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) + if( mask & PAINT_WINDOW_TRANSFORMED) { - xTranslate += xScale*data.xTranslate; - yTranslate += yScale*data.yTranslate; - xScale *= data.xScale; yScale *= data.yScale; + xTranslate += xScale * data.xTranslate; + yTranslate += yScale * data.yTranslate; + xScale *= data.xScale; + yScale *= data.yScale; } - - r.translate(xTranslate, yTranslate); - - if (xScale != 1.0 || yScale != 1.0) + 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 + int texture = mShadowQuadTypes.indexOf( quad.type() ); + if( texture != -1 ) { - r.setWidth(xScale * r.width()); - r.setHeight(yScale * r.height()); -// opacity *= 2.0/(2 - (2 - (xScale + yScale))*(2 - (xScale + yScale))); + // Render it! + // Cheat a little, assume the active and inactive shadows have identical quads + if( effects->hasDecorationShadows() ) + { + if( window->hasDecoration() && + effects->shadowTextureList( ShadowBorderedActive ) == texture ) + { // Decorated windows + // Active shadow + drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, + xScale, yScale, QColor(), + data.opacity * window->shadowOpacity( ShadowBorderedActive ), + data.brightness * window->shadowBrightness( ShadowBorderedActive ), + data.saturation * window->shadowSaturation( ShadowBorderedActive )); + + // Inactive shadow + texture = effects->shadowTextureList( ShadowBorderedInactive ); + drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, + xScale, yScale, QColor(), + data.opacity * window->shadowOpacity( ShadowBorderedInactive ), + data.brightness * window->shadowBrightness( ShadowBorderedInactive ), + data.saturation * window->shadowSaturation( ShadowBorderedInactive )); + } + else if( effects->shadowTextureList( ShadowBorderlessActive ) == texture ) + { // Decoration-less normal windows + if( effects->activeWindow() == window ) + { // Active shadow + drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, + xScale, yScale, QColor(), + data.opacity * window->shadowOpacity( ShadowBorderlessActive ), + data.brightness * window->shadowBrightness( ShadowBorderlessActive ), + data.saturation * window->shadowSaturation( ShadowBorderlessActive )); + } + else + { // Inactive shadow + texture = effects->shadowTextureList( ShadowBorderedInactive ); + drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, + xScale, yScale, QColor(), + data.opacity * window->shadowOpacity( ShadowBorderlessInactive ), + data.brightness * window->shadowBrightness( ShadowBorderlessInactive ), + data.saturation * window->shadowSaturation( ShadowBorderlessInactive )); + } + } + else + { // Other windows + drawShadowQuadXRender( mShadowPics.at( texture ).at( quad.id() ), quadRect, + xScale, yScale, QColor(), + data.opacity * window->shadowOpacity( ShadowOther ), + data.brightness * window->shadowBrightness( ShadowOther ), + data.saturation * window->shadowSaturation( ShadowOther )); + } + } + } + if( quad.type() == mDefaultShadowQuadType ) + { // Default shadow + 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 ); } } - r = shadowRectangle(r); - - // create render mask ================================================================== - - XRenderColor xc = preMultiply(shadowColor, opacity); - XRenderPicture fill = xRenderFill(&xc); - - // clip, then paint shadow tiles ======================================================== - XRenderSetPictureClipRegion (display(), effects->xrenderBufferPicture(), region.handle()); -#define DRAW_CORNER(_CNR_, _X_, _Y_)\ -XRenderComposite( display(), PictOpOver, fill, mShadowPics->_CNR_, effects->xrenderBufferPicture(), 0, 0, 0, 0, _X_, _Y_, w, h ) -#define DRAW_TILE(_TILE_, _X_, _Y_, _W_, _H_)\ -XRenderComposite( display(), PictOpOver, fill, mShadowPics->_TILE_, effects->xrenderBufferPicture(), 0, 0, 0, 0, _X_, _Y_, _W_, _H_ ) - int w = qMin(mShadowPics->cornerSize.width(), r.width()/2); - int h = qMin(mShadowPics->cornerSize.height(), r.height()/2); - DRAW_CORNER(topLeft, r.x(), r.y()); - DRAW_CORNER(topRight, r.right()-w, r.y()); - DRAW_CORNER(btmLeft, r.x(), r.bottom()-h); - DRAW_CORNER(btmRight, r.right()-w, r.bottom()-h); - int w2 = r.width()-2*w-1, h2 = r.height()-2*h-1; - DRAW_TILE(top, r.x()+w, r.y(), w2, h); - DRAW_TILE(btm, r.x()+w, r.bottom()-h, w2, h); - DRAW_TILE(left, r.x(), r.y()+h, w, h2); - DRAW_TILE(right, r.right()-w, r.y()+h, w, h2); - DRAW_TILE(center, r.x()+w, r.y()+h, w2, h2); -#undef DRAW_CORNER -#undef DRAW_TILE } #endif } diff --git a/effects/shadow.h b/effects/shadow.h index 6caabba1f6..22f58ab1ef 100644 --- a/effects/shadow.h +++ b/effects/shadow.h @@ -31,18 +31,6 @@ namespace KWin class GLTexture; -#ifdef KWIN_HAVE_XRENDER_COMPOSITING -class ShadowTiles - { - public: - ShadowTiles(const QPixmap& shadow); - XRenderPicture topLeft, top, topRight, - left, center, right, - btmLeft, btm, btmRight; - QSize cornerSize; - }; -#endif - class ShadowEffect : public QObject, public Effect { @@ -65,6 +53,9 @@ class ShadowEffect void prepareRenderStates( GLTexture *texture, double opacity, double brightness, double saturation ); void restoreRenderStates( GLTexture *texture, double opacity, double brightness, double saturation ); + void drawShadowQuadXRender( XRenderPicture *picture, QRect rect, float xScale, float yScale, + QColor color, float opacity, float brightness, float saturation ); + void drawShadow( EffectWindow* w, int mask, QRegion region, const WindowPaintData& data ); void addQuadVertices(QVector& verts, float x1, float y1, float x2, float y2) const; // transforms window rect -> shadow rect @@ -83,7 +74,8 @@ class ShadowEffect QList mDefaultShadowTextures; #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING - ShadowTiles *mShadowPics; + QList< QList > mShadowPics; + QList mDefaultShadowPics; #endif QList mShadowQuadTypes;