From c2830a636096dc27f7f5a08194bb39f7c95b3444 Mon Sep 17 00:00:00 2001 From: Rivo Laks Date: Wed, 24 Jan 2007 11:51:38 +0000 Subject: [PATCH] Effects can now request windows to be subdivided into multiple quads. Effects also get access to window's vertices. This can be used to change shape of the window, e.g. for wobble effect svn path=/branches/work/kwin_composite/; revision=626706 --- scene.cpp | 1 + scene.h | 1 + scene_opengl.cpp | 157 ++++++++++++++++++++++++++++++++++++++--------- scene_opengl.h | 49 ++++++++++++++- 4 files changed, 177 insertions(+), 31 deletions(-) diff --git a/scene.cpp b/scene.cpp index 0ceda160a9..064b4b03a4 100644 --- a/scene.cpp +++ b/scene.cpp @@ -223,6 +223,7 @@ void Scene::paintWindow( Window* w, int mask, QRegion region ) { WindowPaintData data; data.opacity = w->window()->opacity(); + w->prepareForPainting(); effects->paintWindow( effectWindow( w ), mask, region, data ); } diff --git a/scene.h b/scene.h index 5771e332b1..d54ca15f7d 100644 --- a/scene.h +++ b/scene.h @@ -118,6 +118,7 @@ class Scene::Window virtual void free(); // perform the actual painting of the window virtual void performPaint( int mask, QRegion region, WindowPaintData data ) = 0; + virtual void prepareForPainting() {} int x() const; int y() const; int width() const; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index b49d17a4d1..d0f27a0e73 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -65,6 +65,7 @@ Sources and other compositing managers: #include #include +#include namespace KWinInternal { @@ -620,6 +621,11 @@ SceneOpenGL::Window::Window( Toplevel* c ) , texture( 0 ) , texture_y_inverted( false ) , bound_glxpixmap( None ) + , currentXResolution( -1 ) + , currentYResolution( -1 ) + , requestedXResolution( 0 ) + , requestedYResolution( 0 ) + , verticesDirty( false ) { } @@ -628,6 +634,86 @@ void SceneOpenGL::Window::free() discardTexture(); } +void SceneOpenGL::Window::requestVertexGrid(int maxquadsize) + { + requestedXResolution = (requestedXResolution <= 0) ? maxquadsize : qMin(maxquadsize, requestedXResolution); + requestedYResolution = (requestedYResolution <= 0) ? maxquadsize : qMin(maxquadsize, requestedYResolution); + } + +void SceneOpenGL::Window::createVertexGrid(int xres, int yres) + { + int oldcount = verticeslist.count(); + verticeslist.clear(); + // FIXME: this creates a QRegion out of rectangles just to get the list of + // rectangles. Maybe the QRegion construction can be bypassed? + QRegion region = shape(); + foreach( QRect r, region.rects()) + { + // First calculate number of columns/rows that this rect will be + // divided into + int cols = (xres <= 0) ? 1 : (int)ceilf( r.width() / (float)xres ); + int rows = (yres <= 0) ? 1 : (int)ceilf( r.height() / (float)yres ); + // Now calculate actual size of each cell + int cellw = r.width() / cols; + int cellh = r.height() / rows; + int maxx = r.x() + r.width(); + int maxy = r.y() + r.height(); + for( int x1 = r.x(); x1 < maxx; x1 += cellw ) + { + int x2 = qMin(x1 + cellw, maxx); + for( int y1 = r.y(); y1 < maxy; y1 += cellh ) + { + int y2 = qMin(y1 + cellh, maxy); + // Add this quad to vertices' list + verticeslist.append( Vertex( x1, y1 )); + verticeslist.append( Vertex( x1, y2 )); + verticeslist.append( Vertex( x2, y2 )); + verticeslist.append( Vertex( x2, y1 )); + } + } + } + Client* c = qobject_cast(window()); + kDebug() << k_funcinfo << "'" << (c ? c->caption() : "") << "': Resized vertex grid from " << + oldcount/4 << " quads (minreso: " << currentXResolution << "x" << currentYResolution << + ") to " << verticeslist.count()/4 << " quads (minreso: " << xres << "x" << yres << ")" << endl; + + currentXResolution = xres; + currentYResolution = yres; + verticesDirty = false; + } + +void SceneOpenGL::Window::resetVertices() + { + // This assumes that texcoords of the vertices are unchanged. If they are, + // we need to do this in some other way (or maybe the effects should then + // clean things up themselves) + for(int i = 0; i < verticeslist.count(); i++) + { + verticeslist[i].pos[0] = verticeslist[i].texcoord[0]; + verticeslist[i].pos[1] = verticeslist[i].texcoord[1]; + } + verticesDirty = false; + } + +void SceneOpenGL::Window::prepareVertices() + { + if( requestedXResolution != currentXResolution || requestedYResolution != currentYResolution ) + createVertexGrid( requestedXResolution, requestedYResolution ); + else if( verticesDirty ) + resetVertices(); + + // Reset requests for the next painting + requestedXResolution = 0; + requestedYResolution = 0; + } + +void SceneOpenGL::Window::prepareForPainting() + { + prepareVertices(); + // We should also bind texture here so that effects could access it in the + // paint pass + } + // Bind the window pixmap to an OpenGL texture. void SceneOpenGL::Window::bindTexture() { @@ -856,20 +942,6 @@ void SceneOpenGL::Window::discardTexture() texture = 0; } -// paint a quad (rectangle), ty1/ty2 are texture coordinates (for handling -// swapped y coordinate, see below) -static void quadPaint( int x1, int y1, int x2, int y2, int ty1, int ty2 ) - { - glTexCoord2i( x1, ty1 ); - glVertex2i( x1, y1 ); - glTexCoord2i( x2, ty1 ); - glVertex2i( x2, y1 ); - glTexCoord2i( x2, ty2 ); - glVertex2i( x2, y2 ); - glTexCoord2i( x1, ty2 ); - glVertex2i( x1, y2 ); - } - // paint the window void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintData data ) { @@ -1028,24 +1100,49 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat } } enableTexture(); - // actually paint the window - glBegin( GL_QUADS ); - foreach( QRect r, region.rects()) + if(verticeslist.isEmpty()) + createVertexGrid(0, 0); + // Enable arrays + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer(3, GL_FLOAT, sizeof(Vertex), verticeslist[0].pos); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), verticeslist[0].texcoord); + if( !texture_y_inverted ) { - int y1 = r.y(); - int y2 = r.y() + r.height(); - int ty1 = y1; - int ty2 = y2; - // tfp can result in the texture having y coordinate inverted (because - // of the internal format), so do the inversion if needed - if( !texture_y_inverted ) // "!" because of converting to OpenGL coords - { - ty1 = height() - y1; - ty2 = height() - y2; - } - quadPaint( r.x(), y1, r.x() + r.width(), y2, ty1, ty2 ); + // Modify texture matrix so that we could always use non-opengl + // coordinates for textures + glMatrixMode(GL_TEXTURE); + glScalef(1, -1, 1); + glTranslatef(0, -height(), 0); } - glEnd(); + // Render + if( mask & PAINT_WINDOW_TRANSFORMED ) + // Just draw the entire window, no clipping + glDrawArrays( GL_QUADS, 0, verticeslist.count() ); + else + { + // Make sure there's only a single quad (no transformed vertices) + // Clip using scissoring + glEnable( GL_SCISSOR_TEST ); + region.translate( x, y); // Back to screen coords + int dh = displayHeight(); + foreach( QRect r, region.rects()) + { + // Scissor rect has to be given in OpenGL coords + glScissor(r.x(), dh - r.y() - r.height(), r.width(), r.height()); + glDrawArrays( GL_QUADS, 0, verticeslist.count() ); + } + glDisable( GL_SCISSOR_TEST ); + } + // Restore texture matrix + if( !texture_y_inverted ) + { + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + // Disable arrays + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glPopMatrix(); if( data.opacity != 1.0 || data.saturation != 1.0 || data.brightness != 1.0f ) { diff --git a/scene_opengl.h b/scene_opengl.h index c9e9933f44..2e3b86594c 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -25,6 +25,7 @@ class SceneOpenGL : public Scene { public: + class Window; SceneOpenGL( Workspace* ws ); virtual ~SceneOpenGL(); virtual void paint( QRegion damage, ToplevelList windows ); @@ -61,7 +62,6 @@ class SceneOpenGL static bool strict_binding; static bool copy_buffer_hack; static bool supports_saturation; - class Window; QMap< Toplevel*, Window > windows; static XShmSegmentInfo shm; }; @@ -73,16 +73,63 @@ class SceneOpenGL::Window Window( Toplevel* c ); virtual void free(); virtual void performPaint( int mask, QRegion region, WindowPaintData data ); + virtual void prepareForPainting(); void bindTexture(); void enableTexture(); void disableTexture(); void discardTexture(); Window() {} // QMap sucks even in Qt4 + + /** + * @short Vertex class + * Vertex has position and texture coordinate which are equal at first, + * however effects can e.g. modify position to move the window or part of it. + **/ + class Vertex + { + public: + Vertex() {} + Vertex(float x, float y) + { + pos[0] = texcoord[0] = x; pos[1] = texcoord[1] = y; pos[2] = 0.0f; + } + Vertex(float x, float y, float u, float v) + { + pos[0] = x; pos[1] = y; pos[2] = 0.0f; texcoord[0] = u; texcoord[1] = v; + } + float pos[3]; + float texcoord[2]; + }; + // Returns list of vertices + QVector& vertices() { return verticeslist; } + // Can be called in pre-paint pass. Makes sure that all quads that the + // window consists of are not bigger than maxquadsize x maxquadsize + // (in pixels) in the following paint pass. + void requestVertexGrid(int maxquadsize); + // Marks vertices of the window as dirty. Call this if you change + // position of the vertices + void markVerticesDirty() { verticesDirty = true; } + protected: + // Makes sure that vertex grid requests are fulfilled and that vertices + // aren't dirty. Call this before paint pass + void prepareVertices(); + void createVertexGrid(int xres, int yres); + void resetVertices(); // Resets positions of vertices private: QRegion optimizeBindDamage( const QRegion& reg, int limit ); Texture texture; bool texture_y_inverted; // texture has y inverted GLXPixmap bound_glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode + + QVector verticeslist; + // Maximum size of the biggest quad that window currently has, in pixels + int currentXResolution; + int currentYResolution; + // Requested maximum size of the biggest quad that window would have + // during the next paint pass, in pixels + int requestedXResolution; + int requestedYResolution; + bool verticesDirty; // vertices have been modified in some way }; } // namespace