File indexing completed on 2024-04-28 05:32:22

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004     SPDX-FileCopyrightText: 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "oxygenwindowshadow.h"
0010 #include "oxygencachekey.h"
0011 #include "oxygencairoutils.h"
0012 #include "oxygencolorutils.h"
0013 
0014 namespace Oxygen
0015 {
0016 
0017     //________________________________________________________________________________
0018     void WindowShadow::render(cairo_t* cr, gint x, gint y, gint w, gint h)
0019     {
0020         ColorUtils::Rgba background = settings().palette().color(Palette::Window);
0021         WindowShadowKey key;
0022         key.active=_wopt&WinDeco::Active;
0023         tileSet( background, key).render( cr, x, y, w, h, TileSet::Full );
0024     }
0025 
0026     //________________________________________________________________________________
0027     const TileSet& WindowShadow::tileSet(const ColorUtils::Rgba& color, WindowShadowKey key) const
0028     {
0029 
0030         // check if tileset already in cache
0031         const TileSet& tileSet( helper().windowShadowCache().value(key) );
0032         if( tileSet.isValid() ) return tileSet;
0033 
0034         const double size( shadowSize() );
0035         return helper().windowShadowCache().insert(key, TileSet( shadowPixmap( color, key ), int(size), int(size), 1, 1 ) );
0036 
0037     }
0038 
0039     //________________________________________________________________________________
0040     Cairo::Surface WindowShadow::shadowPixmap(const ColorUtils::Rgba& color, const WindowShadowKey& key ) const
0041     {
0042 
0043         const bool active( key.active );
0044         const ShadowConfiguration& shadowConfiguration( active ? activeShadowConfiguration_ : inactiveShadowConfiguration_ );
0045 
0046         static const double fixedSize=25.5;
0047         const double size( shadowSize() );
0048         const double shadowSize( shadowConfiguration.isEnabled() ? shadowConfiguration.shadowSize() : 0 );
0049 
0050         Cairo::Surface shadow( helper().createSurface( int(size*2), int(size*2) ) );
0051         Cairo::Context p(shadow);
0052 
0053         // some gradients rendering are different at bottom corners if client has no border
0054         const bool hasTopBorder( key.hasTopBorder );
0055         const bool hasBottomBorder( key.hasBottomBorder );
0056 
0057         if( shadowSize )
0058         {
0059 
0060             if( active )
0061             {
0062                 {
0063                     // inner (shark) gradient
0064                     const double gradientSize = std::min( shadowSize, (shadowSize+fixedSize)/2 );
0065                     const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
0066                     const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;
0067 
0068                     Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
0069                     cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );
0070 
0071                     // gaussian shadow is used
0072                     int nPoints( int( (10*gradientSize)/fixedSize ) );
0073                     Gaussian f( 0.85, 0.17 );
0074                     ColorUtils::Rgba c(shadowConfiguration.innerColor());
0075                     for( double i=0; i < nPoints; i++ )
0076                     {
0077                         double x = i/nPoints;
0078                         c.setAlpha( f(x) );
0079                         cairo_pattern_add_color_stop( rg, x, c );
0080                     }
0081 
0082                     cairo_set_source(p, rg);
0083                     GdkRectangle rect={0,0,int(size*2),int(size*2)};
0084                     renderGradient(p, rect, rg, hasTopBorder, hasBottomBorder );
0085                 }
0086 
0087                 {
0088                     // outer (spread) gradient
0089                     const double gradientSize = shadowSize;
0090                     const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
0091                     const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;
0092 
0093                     Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) );
0094                     cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );
0095 
0096                     // gaussian shadow is used
0097                     int nPoints( int( (10*gradientSize)/fixedSize ) );
0098                     Gaussian f( 0.46, 0.34 );
0099                     ColorUtils::Rgba c(shadowConfiguration.outerColor());
0100                     for( double i=0; i < nPoints; i++ )
0101                     {
0102                         double x = i/nPoints;
0103                         c.setAlpha( f(x) );
0104                         cairo_pattern_add_color_stop( rg, x, c );
0105                     }
0106 
0107                     cairo_set_source(p, rg);
0108                     cairo_rectangle(p, 0,0,size*2,size*2);
0109                     cairo_fill(p);
0110                 }
0111             } else {
0112 
0113                 {
0114                     // inner (sharp) gradient
0115                     const double gradientSize = std::min( shadowSize, fixedSize );
0116                     const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
0117                     const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;
0118 
0119                     Cairo::Pattern rg( cairo_pattern_create_radial( size+hoffset, size+voffset, gradientSize ) );
0120                     cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );
0121 
0122                     // parabolic shadow is used
0123                     int nPoints( int( (10*gradientSize)/fixedSize ) );
0124                     Parabolic f( 1, 0.22 );
0125                     ColorUtils::Rgba c(shadowConfiguration.outerColor());
0126                     for( double i=0; i < nPoints; i++ )
0127                     {
0128                         double x = i/nPoints;
0129                         c.setAlpha( f(x) );
0130                         cairo_pattern_add_color_stop( rg, x, c );
0131                     }
0132 
0133                     cairo_set_source(p, rg);
0134                     GdkRectangle rect={0,0,int(size*2),int(size*2)};
0135                     renderGradient(p, rect, rg, hasTopBorder, hasBottomBorder );
0136                 }
0137 
0138                 {
0139                     // mid gradient
0140                     const double gradientSize = std::min( shadowSize, (shadowSize+2*fixedSize)/3 );
0141                     const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
0142                     const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;
0143 
0144                     Cairo::Pattern rg( cairo_pattern_create_radial( size+8.0*hoffset, size+8.0*voffset, gradientSize ) );
0145                     cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );
0146 
0147                     // gaussian shadow is used
0148                     int nPoints( int( (10*gradientSize)/fixedSize ) );
0149                     Gaussian f( 0.54, 0.21 );
0150                     ColorUtils::Rgba c(shadowConfiguration.outerColor());
0151                     for( double i=0; i < nPoints; i++ )
0152                     {
0153                         double x = i/nPoints;
0154                         c.setAlpha( f(x) );
0155                         cairo_pattern_add_color_stop( rg, x, c );
0156                     }
0157 
0158                     cairo_set_source(p, rg);
0159                     cairo_rectangle(p, 0,0,size*2,size*2);
0160                     cairo_fill(p);
0161                 }
0162 
0163                 {
0164                     // outer (spread) gradient
0165                     const double gradientSize = shadowSize;
0166                     const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize;
0167                     const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize;
0168 
0169                     Cairo::Pattern rg( cairo_pattern_create_radial( size+20.0*hoffset, size+20.0*voffset, gradientSize ) );
0170                     cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() );
0171 
0172                     // gaussian shadow is used
0173                     int nPoints( int( (20*gradientSize)/fixedSize ) );
0174                     Gaussian f( 0.155, 0.445 );
0175                     ColorUtils::Rgba c(shadowConfiguration.outerColor());
0176                     for( double i=0; i < nPoints; i++ )
0177                     {
0178                         double x = i/nPoints;
0179                         c.setAlpha( f(x) );
0180                         cairo_pattern_add_color_stop( rg, x, c );
0181                     }
0182 
0183                     cairo_set_source(p, rg);
0184                     cairo_rectangle(p, 0,0,size*2,size*2);
0185                     cairo_fill(p);
0186                 }
0187             }
0188 
0189             cairo_set_source_rgb(p,0,0,0);
0190             cairo_set_operator(p,CAIRO_OPERATOR_CLEAR);
0191             cairo_ellipse(p, size-3,size-3,6,6);
0192             cairo_fill(p);
0193 
0194         }
0195 
0196         return shadow;
0197     }
0198 
0199     //________________________________________________________________________________
0200     void WindowShadow::renderGradient( cairo_t* p, const GdkRectangle& rect, cairo_pattern_t* rg, bool hasTopBorder, bool hasBottomBorder ) const
0201     {
0202         if( hasTopBorder && hasBottomBorder )
0203         {
0204             cairo_set_source(p,rg);
0205             gdk_cairo_rectangle(p, &rect);
0206             cairo_fill(p);
0207             return;
0208         }
0209 
0210         double size( rect.width/2.0 );
0211 
0212         // get pattern definition
0213         double x0(0), y0(0), r0(0);
0214         double x1(0), y1(0), r1(0);
0215         const cairo_status_t status( cairo_pattern_get_radial_circles( rg, &x0, &y0, &r0, &x1, &y1, &r1 ) );
0216         assert( status == CAIRO_STATUS_SUCCESS );
0217 
0218         const double hoffset( x0 - size );
0219         const double voffset( y0 - size );
0220         const double radius( r1 );
0221 
0222         ColorStop::List stops( cairo_pattern_get_color_stops( rg ) );
0223 
0224         // draw ellipse for the upper rect
0225         if( hasTopBorder ) {
0226             cairo_set_source( p, rg );
0227             cairo_rectangle( p, hoffset, voffset, 2*size-hoffset, size );
0228             cairo_fill( p );
0229         } else {
0230 
0231             // draw square gradients for the lower rect
0232             {
0233                 // vertical lines
0234                 Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) );
0235                 for( unsigned int i = 0; i < stops.size(); ++i )
0236                 {
0237                     const ColorUtils::Rgba c( stops[i]._color );
0238                     const double x( stops[i]._x * radius );
0239                     cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c );
0240                     cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
0241                 }
0242 
0243                 cairo_set_source( p, pattern );
0244                 cairo_rectangle( p, hoffset, size+voffset-4, 2*size-hoffset, 4 );
0245                 cairo_fill( p );
0246             }
0247 
0248             {
0249 
0250                 // horizontal line
0251                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) );
0252                 for( unsigned int i = 0; i < stops.size(); ++i )
0253                 {
0254                     const ColorUtils::Rgba c( stops[i]._color );
0255                     const double x( stops[i]._x * radius );
0256                     cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c );
0257                 }
0258 
0259                 cairo_set_source( p, pattern );
0260                 cairo_rectangle( p, size-4+hoffset, voffset, 8, size );
0261                 cairo_fill( p );
0262 
0263             }
0264 
0265             {
0266 
0267                 // top-left corner
0268                 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset-4, radius ) );
0269                 for( unsigned int i = 0; i < stops.size(); ++i )
0270                 {
0271                     ColorUtils::Rgba c( stops[i]._color );
0272                     double x( stops[i]._x -4.0/radius );
0273                     if( x<0 )
0274                     {
0275                         if( i < stops.size()-1 )
0276                         {
0277                             const double x1( stops[i+1]._x - 4.0/radius );
0278                             if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
0279                             else continue;
0280                         }
0281 
0282                         x = 0;
0283 
0284                     }
0285 
0286                     cairo_pattern_add_color_stop( pattern, x, c );
0287 
0288                 }
0289 
0290 
0291                 cairo_set_source( p, pattern );
0292                 cairo_rectangle( p, hoffset, voffset-4, size-4, size );
0293                 cairo_fill( p );
0294 
0295             }
0296 
0297             {
0298 
0299                 // top-right corner
0300                 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset-4, radius ) );
0301                 for( unsigned int i = 0; i < stops.size(); ++i )
0302                 {
0303                     ColorUtils::Rgba c( stops[i]._color );
0304                     double x( stops[i]._x -4.0/radius );
0305                     if( x<0 )
0306                     {
0307                         if( i < stops.size()-1 )
0308                         {
0309                             const double x1( stops[i+1]._x - 4.0/radius );
0310                             if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
0311                             else continue;
0312                         }
0313 
0314                         x = 0;
0315 
0316                     }
0317 
0318                     cairo_pattern_add_color_stop( pattern, x, c );
0319 
0320                 }
0321 
0322 
0323                 cairo_set_source( p, pattern );
0324                 cairo_rectangle( p, size+hoffset+4, voffset-4, size-4, size );
0325                 cairo_fill( p );
0326 
0327             }
0328 
0329         }
0330 
0331         // Bottom part
0332         if( hasBottomBorder ) {
0333 
0334             cairo_set_source( p, rg );
0335             cairo_rectangle( p, hoffset, voffset+size, 2*size-hoffset, size );
0336             cairo_fill( p );
0337 
0338         } else {
0339 
0340             // draw square gradients for the lower rect
0341             {
0342                 // vertical lines
0343                 Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) );
0344                 for( unsigned int i = 0; i < stops.size(); ++i )
0345                 {
0346                     const ColorUtils::Rgba c( stops[i]._color );
0347                     const double x( stops[i]._x * radius );
0348                     cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c );
0349                     cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
0350                 }
0351 
0352                 cairo_set_source( p, pattern );
0353                 cairo_rectangle( p, hoffset, size+voffset, 2*size-hoffset, 4 );
0354                 cairo_fill( p );
0355             }
0356 
0357             {
0358 
0359                 // horizontal line
0360                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) );
0361                 for( unsigned int i = 0; i < stops.size(); ++i )
0362                 {
0363                     const ColorUtils::Rgba c( stops[i]._color );
0364                     const double x( stops[i]._x * radius );
0365                     cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c );
0366                 }
0367 
0368                 cairo_set_source( p, pattern );
0369                 cairo_rectangle( p, size-4+hoffset, size+voffset, 8, size );
0370                 cairo_fill( p );
0371 
0372             }
0373 
0374             {
0375 
0376                 // bottom-left corner
0377                 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset+4, radius ) );
0378                 for( unsigned int i = 0; i < stops.size(); ++i )
0379                 {
0380                     ColorUtils::Rgba c( stops[i]._color );
0381                     double x( stops[i]._x -4.0/radius );
0382                     if( x<0 )
0383                     {
0384                         if( i < stops.size()-1 )
0385                         {
0386                             const double x1( stops[i+1]._x - 4.0/radius );
0387                             if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
0388                             else continue;
0389                         }
0390 
0391                         x = 0;
0392 
0393                     }
0394 
0395                     cairo_pattern_add_color_stop( pattern, x, c );
0396 
0397                 }
0398 
0399 
0400                 cairo_set_source( p, pattern );
0401                 cairo_rectangle( p, hoffset, size+voffset+4, size-4, size );
0402                 cairo_fill( p );
0403 
0404             }
0405 
0406             {
0407 
0408                 // bottom-right corner
0409                 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset+4, radius ) );
0410                 for( unsigned int i = 0; i < stops.size(); ++i )
0411                 {
0412                     ColorUtils::Rgba c( stops[i]._color );
0413                     double x( stops[i]._x -4.0/radius );
0414                     if( x<0 )
0415                     {
0416                         if( i < stops.size()-1 )
0417                         {
0418                             const double x1( stops[i+1]._x - 4.0/radius );
0419                             if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) );
0420                             else continue;
0421                         }
0422 
0423                         x = 0;
0424 
0425                     }
0426 
0427                     cairo_pattern_add_color_stop( pattern, x, c );
0428 
0429                 }
0430 
0431 
0432                 cairo_set_source( p, pattern );
0433                 cairo_rectangle( p, size+hoffset+4, size+voffset+4, size-4, size );
0434                 cairo_fill( p );
0435 
0436             }
0437 
0438         }
0439 
0440     }
0441 }