1 /* 2 * this file is part of the oxygen gtk engine 3 * Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 4 * Copyright (c) 2010 Ruslan Kabatsayev <b7.10110111@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or( at your option ) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free 18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 19 * MA 02110-1301, USA. 20 */ 21 22 #include "oxygenwindowshadow.h" 23 #include "oxygencachekey.h" 24 #include "oxygencairoutils.h" 25 #include "oxygencolorutils.h" 26 27 namespace Oxygen 28 { 29 30 //________________________________________________________________________________ render(cairo_t * cr,gint x,gint y,gint w,gint h)31 void WindowShadow::render(cairo_t* cr, gint x, gint y, gint w, gint h) 32 { 33 ColorUtils::Rgba background = settings().palette().color(Palette::Window); 34 WindowShadowKey key; 35 key.active=_wopt&WinDeco::Active; 36 tileSet( background, key).render( cr, x, y, w, h, TileSet::Full ); 37 } 38 39 //________________________________________________________________________________ tileSet(const ColorUtils::Rgba & color,WindowShadowKey key) const40 const TileSet& WindowShadow::tileSet(const ColorUtils::Rgba& color, WindowShadowKey key) const 41 { 42 43 // check if tileset already in cache 44 const TileSet& tileSet( helper().windowShadowCache().value(key) ); 45 if( tileSet.isValid() ) return tileSet; 46 47 const double size( shadowSize() ); 48 return helper().windowShadowCache().insert(key, TileSet( shadowPixmap( color, key ), int(size), int(size), 1, 1 ) ); 49 50 } 51 52 //________________________________________________________________________________ shadowPixmap(const ColorUtils::Rgba & color,const WindowShadowKey & key) const53 Cairo::Surface WindowShadow::shadowPixmap(const ColorUtils::Rgba& color, const WindowShadowKey& key ) const 54 { 55 56 const bool active( key.active ); 57 const ShadowConfiguration& shadowConfiguration( active ? activeShadowConfiguration_ : inactiveShadowConfiguration_ ); 58 59 static const double fixedSize=25.5; 60 const double size( shadowSize() ); 61 const double shadowSize( shadowConfiguration.isEnabled() ? shadowConfiguration.shadowSize() : 0 ); 62 63 Cairo::Surface shadow( helper().createSurface( int(size*2), int(size*2) ) ); 64 Cairo::Context p(shadow); 65 66 // some gradients rendering are different at bottom corners if client has no border 67 const bool hasTopBorder( key.hasTopBorder ); 68 const bool hasBottomBorder( key.hasBottomBorder ); 69 70 if( shadowSize ) 71 { 72 73 if( active ) 74 { 75 { 76 // inner (shark) gradient 77 const double gradientSize = std::min( shadowSize, (shadowSize+fixedSize)/2 ); 78 const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize; 79 const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize; 80 81 Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) ); 82 cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() ); 83 84 // gaussian shadow is used 85 int nPoints( int( (10*gradientSize)/fixedSize ) ); 86 Gaussian f( 0.85, 0.17 ); 87 ColorUtils::Rgba c(shadowConfiguration.innerColor()); 88 for( double i=0; i < nPoints; i++ ) 89 { 90 double x = i/nPoints; 91 c.setAlpha( f(x) ); 92 cairo_pattern_add_color_stop( rg, x, c ); 93 } 94 95 cairo_set_source(p, rg); 96 GdkRectangle rect={0,0,int(size*2),int(size*2)}; 97 renderGradient(p, rect, rg, hasTopBorder, hasBottomBorder ); 98 } 99 100 { 101 // outer (spread) gradient 102 const double gradientSize = shadowSize; 103 const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize; 104 const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize; 105 106 Cairo::Pattern rg( cairo_pattern_create_radial( size+12.0*hoffset, size+12.0*voffset, gradientSize ) ); 107 cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() ); 108 109 // gaussian shadow is used 110 int nPoints( int( (10*gradientSize)/fixedSize ) ); 111 Gaussian f( 0.46, 0.34 ); 112 ColorUtils::Rgba c(shadowConfiguration.outerColor()); 113 for( double i=0; i < nPoints; i++ ) 114 { 115 double x = i/nPoints; 116 c.setAlpha( f(x) ); 117 cairo_pattern_add_color_stop( rg, x, c ); 118 } 119 120 cairo_set_source(p, rg); 121 cairo_rectangle(p, 0,0,size*2,size*2); 122 cairo_fill(p); 123 } 124 } else { 125 126 { 127 // inner (sharp) gradient 128 const double gradientSize = std::min( shadowSize, fixedSize ); 129 const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize; 130 const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize; 131 132 Cairo::Pattern rg( cairo_pattern_create_radial( size+hoffset, size+voffset, gradientSize ) ); 133 cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() ); 134 135 // parabolic shadow is used 136 int nPoints( int( (10*gradientSize)/fixedSize ) ); 137 Parabolic f( 1, 0.22 ); 138 ColorUtils::Rgba c(shadowConfiguration.outerColor()); 139 for( double i=0; i < nPoints; i++ ) 140 { 141 double x = i/nPoints; 142 c.setAlpha( f(x) ); 143 cairo_pattern_add_color_stop( rg, x, c ); 144 } 145 146 cairo_set_source(p, rg); 147 GdkRectangle rect={0,0,int(size*2),int(size*2)}; 148 renderGradient(p, rect, rg, hasTopBorder, hasBottomBorder ); 149 } 150 151 { 152 // mid gradient 153 const double gradientSize = std::min( shadowSize, (shadowSize+2*fixedSize)/3 ); 154 const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize; 155 const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize; 156 157 Cairo::Pattern rg( cairo_pattern_create_radial( size+8.0*hoffset, size+8.0*voffset, gradientSize ) ); 158 cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() ); 159 160 // gaussian shadow is used 161 int nPoints( int( (10*gradientSize)/fixedSize ) ); 162 Gaussian f( 0.54, 0.21 ); 163 ColorUtils::Rgba c(shadowConfiguration.outerColor()); 164 for( double i=0; i < nPoints; i++ ) 165 { 166 double x = i/nPoints; 167 c.setAlpha( f(x) ); 168 cairo_pattern_add_color_stop( rg, x, c ); 169 } 170 171 cairo_set_source(p, rg); 172 cairo_rectangle(p, 0,0,size*2,size*2); 173 cairo_fill(p); 174 } 175 176 { 177 // outer (spread) gradient 178 const double gradientSize = shadowSize; 179 const double hoffset = shadowConfiguration.horizontalOffset()*gradientSize/fixedSize; 180 const double voffset = shadowConfiguration.verticalOffset()*gradientSize/fixedSize; 181 182 Cairo::Pattern rg( cairo_pattern_create_radial( size+20.0*hoffset, size+20.0*voffset, gradientSize ) ); 183 cairo_pattern_add_color_stop( rg, 1, ColorUtils::Rgba::transparent() ); 184 185 // gaussian shadow is used 186 int nPoints( int( (20*gradientSize)/fixedSize ) ); 187 Gaussian f( 0.155, 0.445 ); 188 ColorUtils::Rgba c(shadowConfiguration.outerColor()); 189 for( double i=0; i < nPoints; i++ ) 190 { 191 double x = i/nPoints; 192 c.setAlpha( f(x) ); 193 cairo_pattern_add_color_stop( rg, x, c ); 194 } 195 196 cairo_set_source(p, rg); 197 cairo_rectangle(p, 0,0,size*2,size*2); 198 cairo_fill(p); 199 } 200 } 201 202 cairo_set_source_rgb(p,0,0,0); 203 cairo_set_operator(p,CAIRO_OPERATOR_CLEAR); 204 cairo_ellipse(p, size-3,size-3,6,6); 205 cairo_fill(p); 206 207 } 208 209 return shadow; 210 } 211 212 //________________________________________________________________________________ renderGradient(cairo_t * p,const GdkRectangle & rect,cairo_pattern_t * rg,bool hasTopBorder,bool hasBottomBorder) const213 void WindowShadow::renderGradient( cairo_t* p, const GdkRectangle& rect, cairo_pattern_t* rg, bool hasTopBorder, bool hasBottomBorder ) const 214 { 215 if( hasTopBorder && hasBottomBorder ) 216 { 217 cairo_set_source(p,rg); 218 gdk_cairo_rectangle(p, &rect); 219 cairo_fill(p); 220 return; 221 } 222 223 double size( rect.width/2.0 ); 224 225 // get pattern definition 226 double x0(0), y0(0), r0(0); 227 double x1(0), y1(0), r1(0); 228 const cairo_status_t status( cairo_pattern_get_radial_circles( rg, &x0, &y0, &r0, &x1, &y1, &r1 ) ); 229 assert( status == CAIRO_STATUS_SUCCESS ); 230 231 const double hoffset( x0 - size ); 232 const double voffset( y0 - size ); 233 const double radius( r1 ); 234 235 ColorStop::List stops( cairo_pattern_get_color_stops( rg ) ); 236 237 // draw ellipse for the upper rect 238 if( hasTopBorder ) { 239 cairo_set_source( p, rg ); 240 cairo_rectangle( p, hoffset, voffset, 2*size-hoffset, size ); 241 cairo_fill( p ); 242 } else { 243 244 // draw square gradients for the lower rect 245 { 246 // vertical lines 247 Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) ); 248 for( unsigned int i = 0; i < stops.size(); ++i ) 249 { 250 const ColorUtils::Rgba c( stops[i]._color ); 251 const double x( stops[i]._x * radius ); 252 cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c ); 253 cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c ); 254 } 255 256 cairo_set_source( p, pattern ); 257 cairo_rectangle( p, hoffset, size+voffset-4, 2*size-hoffset, 4 ); 258 cairo_fill( p ); 259 } 260 261 { 262 263 // horizontal line 264 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) ); 265 for( unsigned int i = 0; i < stops.size(); ++i ) 266 { 267 const ColorUtils::Rgba c( stops[i]._color ); 268 const double x( stops[i]._x * radius ); 269 cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c ); 270 } 271 272 cairo_set_source( p, pattern ); 273 cairo_rectangle( p, size-4+hoffset, voffset, 8, size ); 274 cairo_fill( p ); 275 276 } 277 278 { 279 280 // top-left corner 281 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset-4, radius ) ); 282 for( unsigned int i = 0; i < stops.size(); ++i ) 283 { 284 ColorUtils::Rgba c( stops[i]._color ); 285 double x( stops[i]._x -4.0/radius ); 286 if( x<0 ) 287 { 288 if( i < stops.size()-1 ) 289 { 290 const double x1( stops[i+1]._x - 4.0/radius ); 291 if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) ); 292 else continue; 293 } 294 295 x = 0; 296 297 } 298 299 cairo_pattern_add_color_stop( pattern, x, c ); 300 301 } 302 303 304 cairo_set_source( p, pattern ); 305 cairo_rectangle( p, hoffset, voffset-4, size-4, size ); 306 cairo_fill( p ); 307 308 } 309 310 { 311 312 // top-right corner 313 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset-4, radius ) ); 314 for( unsigned int i = 0; i < stops.size(); ++i ) 315 { 316 ColorUtils::Rgba c( stops[i]._color ); 317 double x( stops[i]._x -4.0/radius ); 318 if( x<0 ) 319 { 320 if( i < stops.size()-1 ) 321 { 322 const double x1( stops[i+1]._x - 4.0/radius ); 323 if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) ); 324 else continue; 325 } 326 327 x = 0; 328 329 } 330 331 cairo_pattern_add_color_stop( pattern, x, c ); 332 333 } 334 335 336 cairo_set_source( p, pattern ); 337 cairo_rectangle( p, size+hoffset+4, voffset-4, size-4, size ); 338 cairo_fill( p ); 339 340 } 341 342 } 343 344 // Bottom part 345 if( hasBottomBorder ) { 346 347 cairo_set_source( p, rg ); 348 cairo_rectangle( p, hoffset, voffset+size, 2*size-hoffset, size ); 349 cairo_fill( p ); 350 351 } else { 352 353 // draw square gradients for the lower rect 354 { 355 // vertical lines 356 Cairo::Pattern pattern( cairo_pattern_create_linear( hoffset, 0.0, 2*size+hoffset, 0.0 ) ); 357 for( unsigned int i = 0; i < stops.size(); ++i ) 358 { 359 const ColorUtils::Rgba c( stops[i]._color ); 360 const double x( stops[i]._x * radius ); 361 cairo_pattern_add_color_stop( pattern, (size-x)/(2.0*size), c ); 362 cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c ); 363 } 364 365 cairo_set_source( p, pattern ); 366 cairo_rectangle( p, hoffset, size+voffset, 2*size-hoffset, 4 ); 367 cairo_fill( p ); 368 } 369 370 { 371 372 // horizontal line 373 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, voffset, 0, 2*size+voffset ) ); 374 for( unsigned int i = 0; i < stops.size(); ++i ) 375 { 376 const ColorUtils::Rgba c( stops[i]._color ); 377 const double x( stops[i]._x * radius ); 378 cairo_pattern_add_color_stop( pattern, (size+x)/(2.0*size), c ); 379 } 380 381 cairo_set_source( p, pattern ); 382 cairo_rectangle( p, size-4+hoffset, size+voffset, 8, size ); 383 cairo_fill( p ); 384 385 } 386 387 { 388 389 // bottom-left corner 390 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset-4, size+voffset+4, radius ) ); 391 for( unsigned int i = 0; i < stops.size(); ++i ) 392 { 393 ColorUtils::Rgba c( stops[i]._color ); 394 double x( stops[i]._x -4.0/radius ); 395 if( x<0 ) 396 { 397 if( i < stops.size()-1 ) 398 { 399 const double x1( stops[i+1]._x - 4.0/radius ); 400 if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) ); 401 else continue; 402 } 403 404 x = 0; 405 406 } 407 408 cairo_pattern_add_color_stop( pattern, x, c ); 409 410 } 411 412 413 cairo_set_source( p, pattern ); 414 cairo_rectangle( p, hoffset, size+voffset+4, size-4, size ); 415 cairo_fill( p ); 416 417 } 418 419 { 420 421 // bottom-right corner 422 Cairo::Pattern pattern( cairo_pattern_create_radial( size+hoffset+4, size+voffset+4, radius ) ); 423 for( unsigned int i = 0; i < stops.size(); ++i ) 424 { 425 ColorUtils::Rgba c( stops[i]._color ); 426 double x( stops[i]._x -4.0/radius ); 427 if( x<0 ) 428 { 429 if( i < stops.size()-1 ) 430 { 431 const double x1( stops[i+1]._x - 4.0/radius ); 432 if( x != x1 ) c = ColorUtils::mix( c, stops[i+1]._color, -x/(x1-x) ); 433 else continue; 434 } 435 436 x = 0; 437 438 } 439 440 cairo_pattern_add_color_stop( pattern, x, c ); 441 442 } 443 444 445 cairo_set_source( p, pattern ); 446 cairo_rectangle( p, size+hoffset+4, size+voffset+4, size-4, size ); 447 cairo_fill( p ); 448 449 } 450 451 } 452 453 } 454 } 455