1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_slideshow.hxx" 24 25 #include <hslcolor.hxx> 26 #include <rgbcolor.hxx> 27 28 #include <basegfx/numeric/ftools.hxx> 29 30 #include <cmath> // for fmod 31 #include <algorithm> 32 33 namespace slideshow 34 { 35 namespace internal 36 { 37 namespace 38 { 39 // helper functions 40 // ================ 41 42 double getMagic( double nLuminance, double nSaturation ) 43 { 44 if( nLuminance <= 0.5 ) 45 return nLuminance*(1.0 + nSaturation); 46 else 47 return nLuminance + nSaturation - nLuminance*nSaturation; 48 } 49 50 HSLColor::HSLTriple rgb2hsl( double nRed, double nGreen, double nBlue ) 51 { 52 // r,g,b in [0,1], h in [0,360] and s,l in [0,1] 53 HSLColor::HSLTriple aRes; 54 55 const double nMax( ::std::max(nRed,::std::max(nGreen, nBlue)) ); 56 const double nMin( ::std::min(nRed,::std::min(nGreen, nBlue)) ); 57 58 const double nDelta( nMax - nMin ); 59 60 aRes.mnLuminance = (nMax + nMin) / 2.0; 61 62 if( ::basegfx::fTools::equalZero( nDelta ) ) 63 { 64 aRes.mnSaturation = 0.0; 65 66 // hue undefined (achromatic case) 67 aRes.mnHue = 0.0; 68 } 69 else 70 { 71 aRes.mnSaturation = aRes.mnLuminance > 0.5 ? 72 nDelta/(2.0-nMax-nMin) : 73 nDelta/(nMax + nMin); 74 75 if( nRed == nMax ) 76 aRes.mnHue = (nGreen - nBlue)/nDelta; 77 else if( nGreen == nMax ) 78 aRes.mnHue = 2.0 + (nBlue - nRed)/nDelta; 79 else if( nBlue == nMax ) 80 aRes.mnHue = 4.0 + (nRed - nGreen)/nDelta; 81 82 aRes.mnHue *= 60.0; 83 84 if( aRes.mnHue < 0.0 ) 85 aRes.mnHue += 360.0; 86 } 87 88 return aRes; 89 } 90 91 double hsl2rgbHelper( double nValue1, double nValue2, double nHue ) 92 { 93 // clamp hue to [0,360] 94 nHue = fmod( nHue, 360.0 ); 95 96 // cope with wrap-arounds 97 if( nHue < 0.0 ) 98 nHue += 360.0; 99 100 if( nHue < 60.0 ) 101 return nValue1 + (nValue2 - nValue1)*nHue/60.0; 102 else if( nHue < 180.0 ) 103 return nValue2; 104 else if( nHue < 240.0 ) 105 return nValue1 + (nValue2 - nValue1)*(240.0 - nHue)/60.0; 106 else 107 return nValue1; 108 } 109 110 RGBColor::RGBTriple hsl2rgb( double nHue, double nSaturation, double nLuminance ) 111 { 112 if( ::basegfx::fTools::equalZero( nSaturation ) ) 113 return RGBColor::RGBTriple(0.0, 0.0, nLuminance ); 114 115 const double nVal1( getMagic(nLuminance, nSaturation) ); 116 const double nVal2( 2.0*nLuminance - nVal1 ); 117 118 RGBColor::RGBTriple aRes; 119 120 aRes.mnRed = hsl2rgbHelper( nVal2, 121 nVal1, 122 nHue + 120.0 ); 123 aRes.mnGreen = hsl2rgbHelper( nVal2, 124 nVal1, 125 nHue ); 126 aRes.mnBlue = hsl2rgbHelper( nVal2, 127 nVal1, 128 nHue - 120.0 ); 129 130 return aRes; 131 } 132 133 // Truncate range of value to [0,1] 134 double truncateRangeStd( double nVal ) 135 { 136 return ::std::max( 0.0, 137 ::std::min( 1.0, 138 nVal ) ); 139 } 140 141 // Truncate range of value to [0,360] 142 double truncateRangeHue( double nVal ) 143 { 144 return ::std::max( 0.0, 145 ::std::min( 360.0, 146 nVal ) ); 147 } 148 149 // convert RGB color to sal_uInt8, truncate range appropriately before 150 sal_uInt8 colorToInt( double nCol ) 151 { 152 return static_cast< sal_uInt8 >( 153 ::basegfx::fround( truncateRangeStd( nCol ) * 255.0 ) ); 154 } 155 } 156 157 158 // HSLColor 159 // =============================================== 160 161 HSLColor::HSLTriple::HSLTriple() : 162 mnHue(), 163 mnSaturation(), 164 mnLuminance() 165 { 166 } 167 168 HSLColor::HSLTriple::HSLTriple( double nHue, double nSaturation, double nLuminance ) : 169 mnHue( nHue ), 170 mnSaturation( nSaturation ), 171 mnLuminance( nLuminance ) 172 { 173 } 174 175 HSLColor::HSLColor() : 176 maHSLTriple( 0.0, 0.0, 0.0 ), 177 mnMagicValue( getMagic( maHSLTriple.mnLuminance, 178 maHSLTriple.mnSaturation ) ) 179 { 180 } 181 182 HSLColor::HSLColor( ::cppcanvas::Color::IntSRGBA nRGBColor ) : 183 maHSLTriple( rgb2hsl( ::cppcanvas::getRed( nRGBColor ) / 255.0, 184 ::cppcanvas::getGreen( nRGBColor ) / 255.0, 185 ::cppcanvas::getBlue( nRGBColor ) / 255.0 ) ), 186 mnMagicValue( getMagic( maHSLTriple.mnLuminance, 187 maHSLTriple.mnSaturation ) ) 188 { 189 } 190 191 HSLColor::HSLColor( double nHue, double nSaturation, double nLuminance ) : 192 maHSLTriple( nHue, nSaturation, nLuminance ), 193 mnMagicValue( getMagic( maHSLTriple.mnLuminance, 194 maHSLTriple.mnSaturation ) ) 195 { 196 } 197 198 HSLColor::HSLColor( const RGBColor& rColor ) : 199 maHSLTriple( rgb2hsl( truncateRangeStd( rColor.getRed() ), 200 truncateRangeStd( rColor.getGreen() ), 201 truncateRangeStd( rColor.getBlue() ) ) ), 202 mnMagicValue( getMagic( maHSLTriple.mnLuminance, 203 maHSLTriple.mnSaturation ) ) 204 { 205 } 206 207 double HSLColor::getHue() const 208 { 209 return maHSLTriple.mnHue; 210 } 211 212 double HSLColor::getSaturation() const 213 { 214 return maHSLTriple.mnSaturation; 215 } 216 217 double HSLColor::getLuminance() const 218 { 219 return maHSLTriple.mnLuminance; 220 } 221 222 double HSLColor::getRed() const 223 { 224 if( ::basegfx::fTools::equalZero( getSaturation() ) ) 225 return getLuminance(); 226 227 return hsl2rgbHelper( 2.0*getLuminance() - mnMagicValue, 228 mnMagicValue, 229 getHue() + 120.0 ); 230 } 231 232 double HSLColor::getGreen() const 233 { 234 if( ::basegfx::fTools::equalZero( getSaturation() ) ) 235 return getLuminance(); 236 237 return hsl2rgbHelper( 2.0*getLuminance() - mnMagicValue, 238 mnMagicValue, 239 getHue() ); 240 } 241 242 double HSLColor::getBlue() const 243 { 244 if( ::basegfx::fTools::equalZero( getSaturation() ) ) 245 return getLuminance(); 246 247 return hsl2rgbHelper( 2.0*getLuminance() - mnMagicValue, 248 mnMagicValue, 249 getHue() - 120.0 ); 250 } 251 252 RGBColor HSLColor::getRGBColor() const 253 { 254 RGBColor::RGBTriple aColor( hsl2rgb( getHue(), 255 getSaturation(), 256 getLuminance() ) ); 257 return RGBColor( aColor.mnRed, aColor.mnGreen, aColor.mnBlue ); 258 } 259 260 RGBColor::RGBColor(const RGBColor& rLHS) 261 { 262 maRGBTriple.mnRed = rLHS.getRed(); 263 maRGBTriple.mnGreen = rLHS.getGreen(); 264 maRGBTriple.mnBlue = rLHS.getBlue(); 265 } 266 267 RGBColor& RGBColor::operator=( const RGBColor& rLHS ){ 268 269 maRGBTriple.mnRed = rLHS.getRed(); 270 maRGBTriple.mnGreen = rLHS.getGreen(); 271 maRGBTriple.mnBlue = rLHS.getBlue(); 272 return *this; 273 } 274 275 HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS ) 276 { 277 return HSLColor( rLHS.getHue() + rRHS.getHue(), 278 rLHS.getSaturation() + rRHS.getSaturation(), 279 rLHS.getLuminance() + rRHS.getLuminance() ); 280 } 281 282 HSLColor operator*( const HSLColor& rLHS, const HSLColor& rRHS ) 283 { 284 return HSLColor( rLHS.getHue() * rRHS.getHue(), 285 rLHS.getSaturation() * rRHS.getSaturation(), 286 rLHS.getLuminance() * rRHS.getLuminance() ); 287 } 288 289 HSLColor operator*( double nFactor, const HSLColor& rRHS ) 290 { 291 return HSLColor( nFactor * rRHS.getHue(), 292 nFactor * rRHS.getSaturation(), 293 nFactor * rRHS.getLuminance() ); 294 } 295 296 HSLColor interpolate( const HSLColor& rFrom, const HSLColor& rTo, double t, bool bCCW ) 297 { 298 const double nFromHue( rFrom.getHue() ); 299 const double nToHue ( rTo.getHue() ); 300 301 double nHue=0.0; 302 303 if( nFromHue <= nToHue && !bCCW ) 304 { 305 // interpolate hue clockwise. That is, hue starts at 306 // high values and ends at low ones. Therefore, we 307 // must 'cross' the 360 degrees and start at low 308 // values again (imagine the hues to lie on the 309 // circle, where values above 360 degrees are mapped 310 // back to [0,360)). 311 nHue = (1.0-t)*(nFromHue + 360.0) + t*nToHue; 312 } 313 else if( nFromHue > nToHue && bCCW ) 314 { 315 // interpolate hue counter-clockwise. That is, hue 316 // starts at high values and ends at low 317 // ones. Therefore, we must 'cross' the 360 degrees 318 // and start at low values again (imagine the hues to 319 // lie on the circle, where values above 360 degrees 320 // are mapped back to [0,360)). 321 nHue = (1.0-t)*nFromHue + t*(nToHue + 360.0); 322 } 323 else 324 { 325 // interpolate hue counter-clockwise. That is, hue 326 // starts at low values and ends at high ones (imagine 327 // the hue value as degrees on a circle, with 328 // increasing values going counter-clockwise) 329 nHue = (1.0-t)*nFromHue + t*nToHue; 330 } 331 332 return HSLColor( nHue, 333 (1.0-t)*rFrom.getSaturation() + t*rTo.getSaturation(), 334 (1.0-t)*rFrom.getLuminance() + t*rTo.getLuminance() ); 335 } 336 337 338 // RGBColor 339 // =============================================== 340 341 342 RGBColor::RGBTriple::RGBTriple() : 343 mnRed(), 344 mnGreen(), 345 mnBlue() 346 { 347 } 348 349 RGBColor::RGBTriple::RGBTriple( double nRed, double nGreen, double nBlue ) : 350 mnRed( nRed ), 351 mnGreen( nGreen ), 352 mnBlue( nBlue ) 353 { 354 } 355 356 RGBColor::RGBColor() : 357 maRGBTriple( 0.0, 0.0, 0.0 ) 358 { 359 } 360 361 RGBColor::RGBColor( ::cppcanvas::Color::IntSRGBA nRGBColor ) : 362 maRGBTriple( ::cppcanvas::getRed( nRGBColor ) / 255.0, 363 ::cppcanvas::getGreen( nRGBColor ) / 255.0, 364 ::cppcanvas::getBlue( nRGBColor ) / 255.0 ) 365 { 366 } 367 368 RGBColor::RGBColor( double nRed, double nGreen, double nBlue ) : 369 maRGBTriple( nRed, nGreen, nBlue ) 370 { 371 } 372 373 RGBColor::RGBColor( const HSLColor& rColor ) : 374 maRGBTriple( hsl2rgb( truncateRangeHue( rColor.getHue() ), 375 truncateRangeStd( rColor.getSaturation() ), 376 truncateRangeStd( rColor.getLuminance() ) ) ) 377 { 378 } 379 380 double RGBColor::getHue() const 381 { 382 return rgb2hsl( getRed(), 383 getGreen(), 384 getBlue() ).mnHue; 385 } 386 387 double RGBColor::getSaturation() const 388 { 389 return rgb2hsl( getRed(), 390 getGreen(), 391 getBlue() ).mnSaturation; 392 } 393 394 double RGBColor::getLuminance() const 395 { 396 return rgb2hsl( getRed(), 397 getGreen(), 398 getBlue() ).mnLuminance; 399 } 400 401 double RGBColor::getRed() const 402 { 403 return maRGBTriple.mnRed; 404 } 405 406 double RGBColor::getGreen() const 407 { 408 return maRGBTriple.mnGreen; 409 } 410 411 double RGBColor::getBlue() const 412 { 413 return maRGBTriple.mnBlue; 414 } 415 416 HSLColor RGBColor::getHSLColor() const 417 { 418 HSLColor::HSLTriple aColor( rgb2hsl( getRed(), 419 getGreen(), 420 getBlue() ) ); 421 return HSLColor( aColor.mnHue, aColor.mnSaturation, aColor.mnLuminance ); 422 } 423 424 ::cppcanvas::Color::IntSRGBA RGBColor::getIntegerColor() const 425 { 426 return ::cppcanvas::makeColor( colorToInt( getRed() ), 427 colorToInt( getGreen() ), 428 colorToInt( getBlue() ), 429 255 ); 430 } 431 432 RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS ) 433 { 434 return RGBColor( rLHS.getRed() + rRHS.getRed(), 435 rLHS.getGreen() + rRHS.getGreen(), 436 rLHS.getBlue() + rRHS.getBlue() ); 437 } 438 439 RGBColor operator*( const RGBColor& rLHS, const RGBColor& rRHS ) 440 { 441 return RGBColor( rLHS.getRed() * rRHS.getRed(), 442 rLHS.getGreen() * rRHS.getGreen(), 443 rLHS.getBlue() * rRHS.getBlue() ); 444 } 445 446 RGBColor operator*( double nFactor, const RGBColor& rRHS ) 447 { 448 return RGBColor( nFactor * rRHS.getRed(), 449 nFactor * rRHS.getGreen(), 450 nFactor * rRHS.getBlue() ); 451 } 452 453 RGBColor interpolate( const RGBColor& rFrom, const RGBColor& rTo, double t ) 454 { 455 return RGBColor( (1.0-t)*rFrom.getRed() + t*rTo.getRed(), 456 (1.0-t)*rFrom.getGreen() + t*rTo.getGreen(), 457 (1.0-t)*rFrom.getBlue() + t*rTo.getBlue() ); 458 } 459 } 460 } 461 462 /* vim: set noet sw=4 ts=4: */ 463