Ok, I give up, whoever wants more complicated transformations than just

translation are welcome to implement it themselves. I'm no graphics guy
after all and this seems to be far beyond me.


svn path=/branches/work/kwin_composite/; revision=559182
This commit is contained in:
Luboš Luňák 2006-07-06 18:22:01 +00:00
parent 3da31a53af
commit e6c23e4cd3
9 changed files with 191 additions and 100 deletions

View file

@ -165,8 +165,7 @@ void Workspace::compositeTimeout()
else if( Unmanaged* c = findUnmanaged( HandleMatchPredicate( children[ i ] )))
windows.append( c );
}
scene->setWindows( windows );
scene->paint( damage );
scene->paint( damage, windows );
XFixesDestroyRegion( display(), damage );
damage = None;
}

View file

@ -132,11 +132,11 @@ void Effect::windowUserMovedResized( Toplevel* , bool, bool )
{
}
void Effect::transformWindow( Toplevel*, EffectData& )
void Effect::transformWindow( Toplevel*, Matrix&, EffectData& )
{
}
void Effect::transformWorkspace( Workspace*, EffectData& )
void Effect::transformWorkspace( Workspace*, Matrix&, EffectData& )
{
}
@ -144,7 +144,7 @@ void Effect::windowDeleted( Toplevel* )
{
}
void MakeHalfTransparent::transformWindow( Toplevel* c, EffectData& data )
void MakeHalfTransparent::transformWindow( Toplevel* c, Matrix&, EffectData& data )
{
if( c->isDialog())
data.opacity *= 0.8;
@ -167,13 +167,13 @@ ShakyMove::ShakyMove()
static const int shaky_diff[] = { 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1 };
static const int SHAKY_MAX = sizeof( shaky_diff ) / sizeof( shaky_diff[ 0 ] );
void ShakyMove::transformWindow( Toplevel* c, EffectData& data )
void ShakyMove::transformWindow( Toplevel* c, Matrix& matrix, EffectData& )
{
if( windows.contains( c ))
{
Matrix m;
m.m[ 0 ][ 3 ] = shaky_diff[ windows[ c ]];
data.matrix *= m;
matrix *= m;
}
}
@ -217,8 +217,31 @@ void ShakyMove::tick()
}
}
void GrowMove::transformWindow( Toplevel* c, Matrix& matrix, EffectData& )
{
if( Client* c2 = dynamic_cast< Client* >( c ))
if( c2->isMove())
{
Matrix m;
m.m[ 0 ][ 0 ] = 1.2;
m.m[ 1 ][ 1 ] = 1.4;
matrix *= m;
}
}
void GrowMove::windowUserMovedResized( Toplevel* c, bool first, bool last )
{
if( first || last )
{
c->workspace()->addDamage( c, c->geometry());
scene->updateTransformation( c );
c->workspace()->addDamage( c, c->geometry());
}
}
static MakeHalfTransparent* mht;
static ShakyMove* sm;
static GrowMove* gm;
//****************************************
// EffectsHandler
@ -228,6 +251,7 @@ EffectsHandler::EffectsHandler()
{
mht = new MakeHalfTransparent;
sm = new ShakyMove;
// gm = new GrowMove;
}
void EffectsHandler::windowUserMovedResized( Toplevel* c, bool first, bool last )
@ -236,22 +260,28 @@ void EffectsHandler::windowUserMovedResized( Toplevel* c, bool first, bool last
mht->windowUserMovedResized( c, first, last );
if( sm )
sm->windowUserMovedResized( c, first, last );
if( gm )
gm->windowUserMovedResized( c, first, last );
}
void EffectsHandler::transformWindow( Toplevel* c, EffectData& data )
void EffectsHandler::transformWindow( Toplevel* c, Matrix& matrix, EffectData& data )
{
if( mht )
mht->transformWindow( c, data );
mht->transformWindow( c, matrix, data );
if( sm )
sm->transformWindow( c, data );
sm->transformWindow( c, matrix, data );
if( gm )
gm->transformWindow( c, matrix, data );
}
void EffectsHandler::transformWorkspace( Workspace* w, EffectData& data )
void EffectsHandler::transformWorkspace( Workspace* w, Matrix& matrix, EffectData& data )
{
if( mht )
mht->transformWorkspace( w, data );
mht->transformWorkspace( w, matrix, data );
if( sm )
sm->transformWorkspace( w, data );
sm->transformWorkspace( w, matrix, data );
if( gm )
gm->transformWorkspace( w, matrix, data );
}
void EffectsHandler::windowDeleted( Toplevel* c )
@ -260,6 +290,8 @@ void EffectsHandler::windowDeleted( Toplevel* c )
mht->windowDeleted( c );
if( sm )
sm->windowDeleted( c );
if( gm )
gm->windowDeleted( c );
}
EffectsHandler* effects;

View file

@ -62,7 +62,6 @@ inline double Matrix::zTranslate() const
class EffectData
{
public:
Matrix matrix;
double opacity;
};
@ -73,8 +72,8 @@ class Effect
// called when moved/resized or once after it's finished
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void windowDeleted( Toplevel* c );
virtual void transformWindow( Toplevel* c, EffectData& data );
virtual void transformWorkspace( Workspace*, EffectData& data );
virtual void transformWindow( Toplevel* c, Matrix& m, EffectData& data );
virtual void transformWorkspace( Workspace*, Matrix& m, EffectData& data );
};
class EffectsHandler
@ -83,8 +82,8 @@ class EffectsHandler
EffectsHandler();
void windowUserMovedResized( Toplevel* c, bool first, bool last );
void windowDeleted( Toplevel* c );
void transformWindow( Toplevel* c, EffectData& data );
void transformWorkspace( Workspace*, EffectData& data );
void transformWindow( Toplevel* c, Matrix& m, EffectData& data );
void transformWorkspace( Workspace*, Matrix& m, EffectData& data );
};
extern EffectsHandler* effects;
@ -94,7 +93,7 @@ class MakeHalfTransparent
{
public:
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void transformWindow( Toplevel* c, EffectData& data );
virtual void transformWindow( Toplevel* c, Matrix& m, EffectData& data );
};
class ShakyMove
@ -104,7 +103,7 @@ class ShakyMove
public:
ShakyMove();
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void transformWindow( Toplevel* c, EffectData& data );
virtual void transformWindow( Toplevel* c, Matrix& m, EffectData& data );
virtual void windowDeleted( Toplevel* c );
private slots:
void tick();
@ -113,6 +112,13 @@ class ShakyMove
QTimer timer;
};
class GrowMove
: public Effect
{
public:
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void transformWindow( Toplevel* c, Matrix& m, EffectData& data );
};
} // namespace

View file

@ -26,11 +26,6 @@ Scene::~Scene()
{
}
void Scene::setWindows( const ToplevelList& list )
{
windows = list;
}
void Scene::windowGeometryShapeChanged( Toplevel* )
{
}

View file

@ -23,8 +23,7 @@ class Scene
public:
Scene( Workspace* ws );
virtual ~Scene();
void setWindows( const ToplevelList& list );
virtual void paint( XserverRegion damage ) = 0;
virtual void paint( XserverRegion damage, ToplevelList windows ) = 0;
virtual void windowGeometryShapeChanged( Toplevel* );
virtual void windowOpacityChanged( Toplevel* );
virtual void windowDeleted( Toplevel* );
@ -32,7 +31,6 @@ class Scene
virtual void updateTransformation( Toplevel* );
protected:
Workspace* wspace;
ToplevelList windows;
};
extern Scene* scene;

View file

@ -29,7 +29,7 @@ SceneBasic::~SceneBasic()
{
}
void SceneBasic::paint( XserverRegion )
void SceneBasic::paint( XserverRegion, ToplevelList windows )
{
Pixmap composite_pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), QX11Info::appDepth());
XGCValues val;

View file

@ -22,7 +22,7 @@ class SceneBasic
public:
SceneBasic( Workspace* ws );
virtual ~SceneBasic();
virtual void paint( XserverRegion damage );
virtual void paint( XserverRegion damage, ToplevelList windows );
};
} // namespace

View file

@ -42,9 +42,9 @@ SceneXrender::~SceneXrender()
(*it).free();
}
void SceneXrender::paint( XserverRegion damage )
void SceneXrender::paint( XserverRegion damage, ToplevelList windows )
{
#if 0
#if 1
XRectangle r;
r.x = 0;
r.y = 0;
@ -54,8 +54,38 @@ void SceneXrender::paint( XserverRegion damage )
#endif
// Use the damage region as the clip region for the root window
XFixesSetPictureClipRegion( display(), front, 0, 0, damage );
// Client list for clients that are either translucent or have a shadow
ToplevelList translucents;
// Prepare pass for windows
// Go top to bottom so that clipping is computed properly for phase1
for( int i = windows.count() - 1;
i >= 0;
--i )
{
Toplevel* c = windows[ i ];
resetWindowData( c );
WindowData& data = window_data[ c ];
Picture picture = windowPicture( c );
if( picture == None ) // The render format can be null for GL and/or Xv visuals
{
windows.removeAt( i );
continue;
}
effects->transformWindow( c, data.matrix, data.effect ); // TODO remove, instead add initWindow() to effects
saveWindowClipRegion( c, damage );
if( data.simpleTransformation() && isOpaque( c ))
{ // is opaque, has simple shape, can be clipped, will be painted using simpler faster method
// Subtract the clients shape from the damage region
XserverRegion shape = windowShape( c );
assert( shape != None );
XFixesSubtractRegion( display(), damage, damage, shape );
data.phase = 1;
}
else
data.phase = 2; // will be painted later bottom to top
}
// Fill any areas of the root window not covered by windows
XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage );
XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff };
XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
// Draw each opaque window top to bottom, subtracting the bounding rect of
// each window from the clip region after it's been drawn.
for( int i = windows.count() - 1;
@ -63,59 +93,50 @@ void SceneXrender::paint( XserverRegion damage )
--i )
{
Toplevel* c = windows[ i ];
checkWindowData( c );
WindowData& data = window_data[ c ];
effects->transformWindow( c, data.effect ); // TODO remove, instead add initWindow() to effects
if( isOpaque( c ))
{
if( data.phase != 1 )
continue;
XFixesSetPictureClipRegion( display(), buffer, 0, 0, savedWindowClipRegion( c ));
Picture picture = windowPicture( c );
XserverRegion shape = windowShape( c );
if( picture != None && shape != None )
{
// Set the clip region for the buffer to the damage region, and
// subtract the clients shape from the damage region
XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage );
const Matrix& matrix = data.effect.matrix;
if( !matrix.isIdentity())
{
assert( matrix.isOnlyTranslate());
XFixesTranslateRegion( display(), shape, int( matrix.xTranslate()), int( matrix.yTranslate()));
}
XFixesSubtractRegion( display(), damage, damage, shape );
XRenderComposite( display(), PictOpSrc, picture, None, buffer, 0, 0, 0, 0,
c->x() + int( matrix.xTranslate()), c->y() + int( matrix.yTranslate()), c->width(), c->height());
c->x() + int( data.matrix.xTranslate()), c->y() + int( data.matrix.yTranslate()), c->width(), c->height());
}
}
saveWindowClipRegion( c, damage );
translucents.prepend( c );
}
// Fill any areas of the root window not covered by windows
XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage );
XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff };
XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
// Now walk the list bottom to top, drawing translucent windows and shadows.
// That we draw bottom to top is important now since we're drawing translucent objects.
// Now walk the list bottom to top, drawing translucent and complicated windows.
// That we draw bottom to top is important now since we're drawing translucent objects
// and also are clipping only by opaque windows.
for( int i = 0;
i < translucents.count();
i < windows.count();
++i )
{
Toplevel* c = translucents[ i ];
// Restore the previously saved clip region
XserverRegion r = savedWindowClipRegion( c );
XFixesSetPictureClipRegion( display(), buffer, 0, 0, r );
if( !isOpaque( c ))
{
Toplevel* c = windows[ i ];
WindowData& data = window_data[ c ];
if( data.phase != 2 && false)
continue;
XFixesSetPictureClipRegion( display(), buffer, 0, 0, savedWindowClipRegion( c ));
Picture picture = windowPicture( c );
Picture alpha = windowAlphaMask( c );
if( picture != None )
if( data.simpleTransformation())
{
const Matrix& matrix = window_data[ c ].effect.matrix;
// TODO clip also using shape? also above?
XRenderComposite( display(), PictOpOver, picture, alpha, buffer, 0, 0, 0, 0,
c->x() + int( matrix.xTranslate()), c->y() + int( matrix.yTranslate()), c->width(), c->height());
c->x() + int( data.matrix.xTranslate()), c->y() + int( data.matrix.yTranslate()), c->width(), c->height());
}
else
{
// TODO Okay, I'm at loss here. Whoever wants advanced transformations can implement
// it themselves. If not, they actually don't want it that badly *shrug*.
// setPictureMatrix( picture, data.matrix );
XRenderComposite( display(), PictOpSrc, picture, alpha, buffer, 0, 0, 0, 0,
c->x(), c->y(), c->width(), c->height());
// setPictureMatrix( picture, Matrix());
}
}
XFixesDestroyRegion( display(), r );
// cleanup
for( int i = 0;
i < windows.count();
++i )
{
Toplevel* c = windows[ i ];
cleanup( c );
}
// copy composed buffer to the root window
XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
@ -127,24 +148,33 @@ void SceneXrender::transformWindowDamage( Toplevel* c, XserverRegion r ) const
{
if( !window_data.contains( c ))
return;
const Matrix& matrix = window_data[ c ].effect.matrix;
const Matrix& matrix = window_data[ c ].matrix;
if( matrix.isIdentity())
return;
assert( matrix.isOnlyTranslate());
// TODO the matrix here is not valid after it changes but before it's first painted
// (i.e. a changes to state where it should be translated but the matrix is not yet updated)
if( matrix.isOnlyTranslate())
XFixesTranslateRegion( display(), r, int( matrix.xTranslate()), int( matrix.yTranslate()));
else
{
// The region here should be translated using the matrix, but that's not possible
// (well, maybe fetch the region and transform manually - TODO check). So simply
// mark whole screen as damaged.
XRectangle s;
s.x = s.y = 0;
s.width = displayWidth();
s.height = displayHeight();
XFixesSetRegion( display(), r, &s, 1 );
}
}
void SceneXrender::updateTransformation( Toplevel* c )
{
// TODO maybe only mark as invalid and update on-demand
checkWindowData( c );
resetWindowData( c );
WindowData& data = window_data[ c ];
effects->transformWindow( c, data.effect );
effects->transformWindow( c, data.matrix, data.effect );
}
void SceneXrender::checkWindowData( Toplevel* c )
void SceneXrender::resetWindowData( Toplevel* c )
{
if( !window_data.contains( c ))
{
@ -152,7 +182,7 @@ void SceneXrender::checkWindowData( Toplevel* c )
window_data[ c ].format = XRenderFindVisualFormat( display(), c->visual());
}
WindowData& data = window_data[ c ];
data.effect.matrix = Matrix();
data.matrix = Matrix();
data.effect.opacity = c->opacity();
}
@ -206,11 +236,15 @@ void SceneXrender::saveWindowClipRegion( Toplevel* c, XserverRegion r )
}
XserverRegion SceneXrender::savedWindowClipRegion( Toplevel* c )
{ // always called after saveWindowClipRegion(), also resets
{
return window_data[ c ].saved_clip_region;
}
void SceneXrender::cleanup( Toplevel* c )
{
WindowData& data = window_data[ c ];
XserverRegion r = data.saved_clip_region;
XFixesDestroyRegion( display(), data.saved_clip_region );
data.saved_clip_region = None;
return r;
}
bool SceneXrender::isOpaque( Toplevel* c ) const
@ -249,10 +283,11 @@ Picture SceneXrender::windowAlphaMask( Toplevel* c )
return data.alpha;
}
XserverRegion SceneXrender::windowShape( Toplevel* c )
{
#if 0 // it probably doesn't make sense to cache this, and perhaps some others - they aren't roundtrips
WindowData& data = window_data[ c ];
#if 0 // it probably doesn't make sense to cache this, and perhaps some others - they aren't roundtrips
if( data.shape == None )
{
data.shape = XFixesCreateRegionFromWindow( display(), c->handle(), WindowRegionBounding );
@ -260,8 +295,15 @@ XserverRegion SceneXrender::windowShape( Toplevel* c )
}
return data.shape;
#else
if( !data.simpleTransformation())
{
// The region here should be translated using the matrix, but that's not possible
// (well, maybe fetch the region and transform manually - TODO check).
return None;
}
XserverRegion shape = XFixesCreateRegionFromWindow( display(), c->handle(), WindowRegionBounding );
XFixesTranslateRegion( display(), shape, c->x(), c->y());
XFixesTranslateRegion( display(), shape,
c->x() + int( data.matrix.xTranslate()), c->y() + int( data.matrix.yTranslate()));
return shape;
#endif
}
@ -281,19 +323,28 @@ void SceneXrender::setPictureMatrix( Picture pic, const Matrix& m )
{
if( pic == None )
return;
#if 0
XTransform t;
// ignore z axis
t.matrix[ 0 ][ 0 ] = XDoubleToFixed( m.m[ 0 ][ 0 ] );
t.matrix[ 0 ][ 0 ] = XDoubleToFixed( 1 / m.m[ 0 ][ 0 ] );
t.matrix[ 0 ][ 1 ] = XDoubleToFixed( m.m[ 0 ][ 1 ] );
t.matrix[ 0 ][ 2 ] = -XDoubleToFixed( m.m[ 0 ][ 3 ] ); // translation seems to be inverted
t.matrix[ 1 ][ 0 ] = XDoubleToFixed( m.m[ 1 ][ 0 ] );
t.matrix[ 1 ][ 1 ] = XDoubleToFixed( m.m[ 1 ][ 1 ] );
t.matrix[ 1 ][ 1 ] = XDoubleToFixed( 1 / m.m[ 1 ][ 1 ] );
t.matrix[ 1 ][ 2 ] = -XDoubleToFixed( m.m[ 1 ][ 3 ] );
t.matrix[ 2 ][ 0 ] = XDoubleToFixed( m.m[ 3 ][ 0 ] );
t.matrix[ 2 ][ 1 ] = XDoubleToFixed( m.m[ 3 ][ 1 ] );
t.matrix[ 2 ][ 2 ] = XDoubleToFixed( m.m[ 3 ][ 3 ] );
// and scaling seems to be wrong too
// or maybe I just don't get it, but anyway, for now
if( m.m[ 3 ][ 3 ] != 1 )
{
t.matrix[ 0 ][ 0 ] = XDoubleToFixed( 1 / m.m[ 0 ][ 0 ] * m.m[ 3 ][ 3 ] );
t.matrix[ 1 ][ 1 ] = XDoubleToFixed( 1 / m.m[ 1 ][ 1 ] * m.m[ 3 ][ 3 ] );
t.matrix[ 2 ][ 2 ] = XDoubleToFixed( 1 );
}
XRenderSetPictureTransform( display(), pic, &t );
if( t.matrix[ 0 ][ 0 ] != XDoubleToFixed( 1 ) // fast filter for identity or translation
if( t.matrix[ 0 ][ 0 ] != XDoubleToFixed( 1 )
|| t.matrix[ 1 ][ 1 ] != XDoubleToFixed( 1 )
|| t.matrix[ 2 ][ 2 ] != XDoubleToFixed( 1 )
|| t.matrix[ 0 ][ 1 ] != XDoubleToFixed( 0 )
@ -301,10 +352,11 @@ void SceneXrender::setPictureMatrix( Picture pic, const Matrix& m )
{
XRenderSetPictureFilter( display(), pic, const_cast< char* >( FilterGood ), 0, 0 );
}
else
else // fast filter for identity or translation
{
XRenderSetPictureFilter( display(), pic, const_cast< char* >( FilterFast ), 0, 0 );
}
#endif
}
SceneXrender::WindowData::WindowData()
@ -326,5 +378,10 @@ void SceneXrender::WindowData::free()
XRenderFreePicture( display(), shape );
}
bool SceneXrender::WindowData::simpleTransformation() const
{
return ( matrix.isIdentity() || matrix.isOnlyTranslate());
}
} // namespace
#endif

View file

@ -30,7 +30,7 @@ class SceneXrender
public:
SceneXrender( Workspace* ws );
virtual ~SceneXrender();
virtual void paint( XserverRegion damage );
virtual void paint( XserverRegion damage, ToplevelList windows );
virtual void windowGeometryShapeChanged( Toplevel* );
virtual void windowOpacityChanged( Toplevel* );
virtual void windowDeleted( Toplevel* );
@ -38,7 +38,7 @@ class SceneXrender
virtual void updateTransformation( Toplevel* );
private:
void createBuffer();
void checkWindowData( Toplevel* c );
void resetWindowData( Toplevel* c );
Picture windowPicture( Toplevel* c );
void saveWindowClipRegion( Toplevel* c, XserverRegion r );
XserverRegion savedWindowClipRegion( Toplevel* c );
@ -46,6 +46,7 @@ class SceneXrender
Picture windowAlphaMask( Toplevel* c );
XserverRegion windowShape( Toplevel* c );
static void setPictureMatrix( Picture pic, const Matrix& m );
void cleanup( Toplevel* c );
XRenderPictFormat* format;
Picture front;
Picture buffer;
@ -53,13 +54,16 @@ class SceneXrender
{
WindowData();
void free();
bool simpleTransformation() const;
Picture picture;
XRenderPictFormat* format;
XserverRegion saved_clip_region;
Picture alpha;
double alpha_cached_opacity;
XserverRegion shape;
Matrix matrix;
EffectData effect;
int phase;
};
QMap< Toplevel*, WindowData > window_data;
};