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 }