////////////////////////////////////////////////////////////////////////////// // oxygenshadowcache.cpp // handles caching of TileSet objects to draw shadows // ------------------- // // Copyright (c) 2009 Hugo Pereira Da Costa // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////// #include "oxygenshadowcache.h" #include "oxygenclient.h" #include "oxygen.h" #include #include #include #include namespace Oxygen { //_______________________________________________________ OxygenShadowCache::OxygenShadowCache( int maxIndex ): maxIndex_( maxIndex ), activeShadowConfiguration_( OxygenShadowConfiguration( QPalette::Active ) ), inactiveShadowConfiguration_( OxygenShadowConfiguration( QPalette::Inactive ) ) { kDebug(1212) << endl; shadowCache_.setMaxCost( 1<<6 ); animatedShadowCache_.setMaxCost( maxIndex_<<6 ); } //_______________________________________________________ bool OxygenShadowCache::shadowConfigurationChanged( const OxygenShadowConfiguration& other ) const { const OxygenShadowConfiguration& local = (other.colorGroup() == QPalette::Active ) ? activeShadowConfiguration_:inactiveShadowConfiguration_; return !(local == other); } //_______________________________________________________ void OxygenShadowCache::setShadowConfiguration( const OxygenShadowConfiguration& other ) { OxygenShadowConfiguration& local = (other.colorGroup() == QPalette::Active ) ? activeShadowConfiguration_:inactiveShadowConfiguration_; local = other; } //_______________________________________________________ TileSet* OxygenShadowCache::tileSet( const OxygenClient* client ) { // construct key Key key( client ); // check if tileset already in cache int hash( key.hash() ); if( shadowCache_.contains(hash) ) return shadowCache_.object(hash); // create tileset otherwise qreal size( shadowSize() ); TileSet* tileSet = new TileSet( shadowPixmap( client, key.active ), size, size, 1, 1); shadowCache_.insert( hash, tileSet ); return tileSet; } //_______________________________________________________ TileSet* OxygenShadowCache::tileSet( const OxygenClient* client, int index ) { assert( index <= maxIndex_ ); // construct key Key key( client ); key.index = index; // check if tileset already in cache int hash( key.hash() ); if( animatedShadowCache_.contains(hash) ) return animatedShadowCache_.object(hash); // create shadow and tileset otherwise qreal size( shadowSize() ); qreal opacity( qreal(index)/qreal(maxIndex_) ); QPixmap shadow( size*2, size*2 ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); QPixmap inactiveShadow( shadowPixmap( client, false ) ); { QPainter pp( &inactiveShadow ); pp.setRenderHint( QPainter::Antialiasing ); pp.setCompositionMode(QPainter::CompositionMode_DestinationIn); pp.fillRect( inactiveShadow.rect(), QColor( 0, 0, 0, 255*(1.0-opacity ) ) ); } QPixmap activeShadow( shadowPixmap( client, true ) ); { QPainter pp( &activeShadow ); pp.setRenderHint( QPainter::Antialiasing ); pp.setCompositionMode(QPainter::CompositionMode_DestinationIn); pp.fillRect( activeShadow.rect(), QColor( 0, 0, 0, 255*( opacity ) ) ); } p.drawPixmap( QPointF(0,0), inactiveShadow ); p.drawPixmap( QPointF(0,0), activeShadow ); p.end(); TileSet* tileSet = new TileSet(shadow, size, size, 1, 1); animatedShadowCache_.insert( hash, tileSet ); return tileSet; } //_________________________________________________________________ QPixmap OxygenShadowCache::shadowPixmap(const OxygenClient* client, bool active ) const { // get window color Key key( client ); QPalette palette( client->backgroundPalette( client->widget(), client->widget()->palette() ) ); QColor color( palette.color( client->widget()->backgroundRole() ) ); if( active && client->configuration().drawTitleOutline() && client->configuration().frameBorder() == OxygenConfiguration::BorderNone ) { // a more complex tile set is needed for the configuration above: // top corners must be beveled with the "active titlebar color" while // bottom corners must be beveled with the "window background color". // this requires generating two shadow pixmaps and tiling them in the tileSet. qreal size( shadowSize() ); QPixmap shadow( 2*size, 2*size ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); QPixmap shadowTop = simpleShadowPixmap( color, key, active ); QRect topRect( shadow.rect() ); topRect.setBottom( int( size )-1 ); p.setClipRect( topRect ); p.drawPixmap( QPointF( 0, 0 ), shadowTop ); // get window color palette = client->widget()->palette(); color = palette.color( client->widget()->backgroundRole() ); QPixmap shadowBottom = simpleShadowPixmap( color, key, active ); QRect bottomRect( shadow.rect() ); bottomRect.setTop( int( size ) ); p.setClipRect( bottomRect ); p.drawPixmap( QPointF( 0, 0 ), shadowBottom ); p.end(); return shadow; } else return simpleShadowPixmap( color, key, active ); } //_______________________________________________________ QPixmap OxygenShadowCache::simpleShadowPixmap( const QColor& color, const Key& key, bool active ) const { // local reference to relevant shadow configuration const OxygenShadowConfiguration& shadowConfiguration( active && key.useOxygenShadows ? activeShadowConfiguration_:inactiveShadowConfiguration_ ); static const qreal fixedSize = 25.5; qreal size( shadowSize() ); qreal shadowSize( shadowConfiguration.shadowSize() ); QPixmap shadow = QPixmap( size*2, size*2 ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); p.setPen( Qt::NoPen ); // offsets are scaled with the shadow size // so that the ratio Top-shadow/Bottom-shadow is kept constant // when shadow size is changed qreal hoffset = shadowConfiguration.horizontalOffset()*shadowSize/fixedSize; qreal voffset = shadowConfiguration.verticalOffset()*shadowSize/fixedSize; // some gradients rendering are different at bottom corners for NoBorders windows bool noBorder( key.frameBorder == Key::BorderNone && !key.isShade ); if( active && key.useOxygenShadows ) { { int nPoints = 5; int values[5] = {255, 220, 180, 25, 0}; qreal x[5] = {4.4, 4.5, 5, 5.5, 6.5}; // the first point of this gradient does not scaled QRadialGradient rg( size, size, shadowSize ); QColor c = color; for( int i = 0; icalcLightColor( oxygenHelper()->backgroundTopColor(color) )); lg.setColorAt(0.52, oxygenHelper()->backgroundTopColor(color) ); lg.setColorAt(1.0, oxygenHelper()->backgroundBottomColor(color) ); // draw ellipse. // note: special tricks are needed to cope with some rounding issues when size is not an integer. p.setBrush( lg ); int sizeInt( size ); p.drawEllipse( QRectF( sizeInt-4, sizeInt-4, 8+2*(size-sizeInt), 8+2*(size-sizeInt) ) ); p.end(); return shadow; } //_______________________________________________________ void OxygenShadowCache::renderGradient( QPainter& p, const QRectF& rect, const QRadialGradient& rg, bool noBorders ) const { if( !noBorders ) { p.setBrush( rg ); p.drawRect( rect ); return; } qreal size( rect.width()/2.0 ); qreal hoffset( rg.center().x() - size ); qreal voffset( rg.center().y() - size ); // load gradient stops QGradientStops stops( rg.stops() ); qreal radius( rg.radius() ); // draw ellipse for the upper rect { QRectF rect( hoffset, voffset, 2*size-hoffset, size ); p.setBrush( rg ); p.drawRect( rect ); } // draw square gradients for the lower rect { // vertical lines QRectF rect( hoffset, size+voffset, 2*size-hoffset, 4 ); QLinearGradient lg( hoffset, 0.0, 2*size+hoffset, 0.0 ); for( int i = 0; i