1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <hslcolor.hxx>
22 #include <rgbcolor.hxx>
24 #include <basegfx/numeric/ftools.hxx>
39 double getMagic( double nLuminance
, double nSaturation
)
41 if( nLuminance
<= 0.5 )
42 return nLuminance
*(1.0 + nSaturation
);
44 return nLuminance
+ nSaturation
- nLuminance
*nSaturation
;
47 HSLColor::HSLTriple
rgb2hsl( double nRed
, double nGreen
, double nBlue
)
49 // r,g,b in [0,1], h in [0,360] and s,l in [0,1]
50 HSLColor::HSLTriple aRes
;
52 const double nMax( ::std::max(nRed
,::std::max(nGreen
, nBlue
)) );
53 const double nMin( ::std::min(nRed
,::std::min(nGreen
, nBlue
)) );
55 const double nDelta( nMax
- nMin
);
57 aRes
.mnLuminance
= (nMax
+ nMin
) / 2.0;
59 if( ::basegfx::fTools::equalZero( nDelta
) )
61 aRes
.mnSaturation
= 0.0;
63 // hue undefined (achromatic case)
68 aRes
.mnSaturation
= aRes
.mnLuminance
> 0.5 ?
69 nDelta
/(2.0-nMax
-nMin
) :
72 if( rtl::math::approxEqual(nRed
, nMax
) )
73 aRes
.mnHue
= (nGreen
- nBlue
)/nDelta
;
74 else if( rtl::math::approxEqual(nGreen
, nMax
) )
75 aRes
.mnHue
= 2.0 + (nBlue
- nRed
)/nDelta
;
76 else if( rtl::math::approxEqual(nBlue
, nMax
) )
77 aRes
.mnHue
= 4.0 + (nRed
- nGreen
)/nDelta
;
81 if( aRes
.mnHue
< 0.0 )
88 double hsl2rgbHelper( double nValue1
, double nValue2
, double nHue
)
90 // clamp hue to [0,360]
91 nHue
= fmod( nHue
, 360.0 );
93 // cope with wrap-arounds
98 return nValue1
+ (nValue2
- nValue1
)*nHue
/60.0;
99 else if( nHue
< 180.0 )
101 else if( nHue
< 240.0 )
102 return nValue1
+ (nValue2
- nValue1
)*(240.0 - nHue
)/60.0;
107 RGBColor::RGBTriple
hsl2rgb( double nHue
, double nSaturation
, double nLuminance
)
109 if( ::basegfx::fTools::equalZero( nSaturation
) )
110 return RGBColor::RGBTriple(0.0, 0.0, nLuminance
);
112 const double nVal1( getMagic(nLuminance
, nSaturation
) );
113 const double nVal2( 2.0*nLuminance
- nVal1
);
115 RGBColor::RGBTriple aRes
;
117 aRes
.mnRed
= hsl2rgbHelper( nVal2
,
120 aRes
.mnGreen
= hsl2rgbHelper( nVal2
,
123 aRes
.mnBlue
= hsl2rgbHelper( nVal2
,
130 /// Truncate range of value to [0,1]
131 double truncateRangeStd( double nVal
)
133 return ::std::max( 0.0,
138 /// Truncate range of value to [0,360]
139 double truncateRangeHue( double nVal
)
141 return ::std::max( 0.0,
146 /// convert RGB color to sal_uInt8, truncate range appropriately before
147 sal_uInt8
colorToInt( double nCol
)
149 return static_cast< sal_uInt8
>(
150 ::basegfx::fround( truncateRangeStd( nCol
) * 255.0 ) );
158 HSLColor::HSLTriple::HSLTriple() :
165 HSLColor::HSLTriple::HSLTriple( double nHue
, double nSaturation
, double nLuminance
) :
167 mnSaturation( nSaturation
),
168 mnLuminance( nLuminance
)
172 HSLColor::HSLColor() :
173 maHSLTriple( 0.0, 0.0, 0.0 )
177 HSLColor::HSLColor( double nHue
, double nSaturation
, double nLuminance
) :
178 maHSLTriple( nHue
, nSaturation
, nLuminance
)
182 HSLColor::HSLColor( const RGBColor
& rColor
) :
183 maHSLTriple( rgb2hsl( truncateRangeStd( rColor
.getRed() ),
184 truncateRangeStd( rColor
.getGreen() ),
185 truncateRangeStd( rColor
.getBlue() ) ) )
190 bool operator==( const HSLColor
& rLHS
, const HSLColor
& rRHS
)
192 return ( rLHS
.getHue() == rRHS
.getHue() &&
193 rLHS
.getSaturation() == rRHS
.getSaturation() &&
194 rLHS
.getLuminance() == rRHS
.getLuminance() );
197 bool operator!=( const HSLColor
& rLHS
, const HSLColor
& rRHS
)
199 return !( rLHS
== rRHS
);
202 HSLColor
operator+( const HSLColor
& rLHS
, const HSLColor
& rRHS
)
204 return HSLColor( rLHS
.getHue() + rRHS
.getHue(),
205 rLHS
.getSaturation() + rRHS
.getSaturation(),
206 rLHS
.getLuminance() + rRHS
.getLuminance() );
209 HSLColor
operator*( double nFactor
, const HSLColor
& rRHS
)
211 return HSLColor( nFactor
* rRHS
.getHue(),
212 nFactor
* rRHS
.getSaturation(),
213 nFactor
* rRHS
.getLuminance() );
216 HSLColor
interpolate( const HSLColor
& rFrom
, const HSLColor
& rTo
, double t
, bool bCCW
)
218 const double nFromHue( rFrom
.getHue() );
219 const double nToHue ( rTo
.getHue() );
223 if( nFromHue
<= nToHue
&& !bCCW
)
225 // interpolate hue clockwise. That is, hue starts at
226 // high values and ends at low ones. Therefore, we
227 // must 'cross' the 360 degrees and start at low
228 // values again (imagine the hues to lie on the
229 // circle, where values above 360 degrees are mapped
231 nHue
= (1.0-t
)*(nFromHue
+ 360.0) + t
*nToHue
;
233 else if( nFromHue
> nToHue
&& bCCW
)
235 // interpolate hue counter-clockwise. That is, hue
236 // starts at high values and ends at low
237 // ones. Therefore, we must 'cross' the 360 degrees
238 // and start at low values again (imagine the hues to
239 // lie on the circle, where values above 360 degrees
240 // are mapped back to [0,360)).
241 nHue
= (1.0-t
)*nFromHue
+ t
*(nToHue
+ 360.0);
245 // interpolate hue counter-clockwise. That is, hue
246 // starts at low values and ends at high ones (imagine
247 // the hue value as degrees on a circle, with
248 // increasing values going counter-clockwise)
249 nHue
= (1.0-t
)*nFromHue
+ t
*nToHue
;
252 return HSLColor( nHue
,
253 (1.0-t
)*rFrom
.getSaturation() + t
*rTo
.getSaturation(),
254 (1.0-t
)*rFrom
.getLuminance() + t
*rTo
.getLuminance() );
261 RGBColor::RGBTriple::RGBTriple() :
268 RGBColor::RGBTriple::RGBTriple( double nRed
, double nGreen
, double nBlue
) :
275 RGBColor::RGBColor() :
276 maRGBTriple( 0.0, 0.0, 0.0 )
280 RGBColor::RGBColor( ::cppcanvas::IntSRGBA nRGBColor
) :
281 maRGBTriple( ::cppcanvas::getRed( nRGBColor
) / 255.0,
282 ::cppcanvas::getGreen( nRGBColor
) / 255.0,
283 ::cppcanvas::getBlue( nRGBColor
) / 255.0 )
287 RGBColor::RGBColor( double nRed
, double nGreen
, double nBlue
) :
288 maRGBTriple( nRed
, nGreen
, nBlue
)
292 RGBColor::RGBColor( const HSLColor
& rColor
) :
293 maRGBTriple( hsl2rgb( truncateRangeHue( rColor
.getHue() ),
294 truncateRangeStd( rColor
.getSaturation() ),
295 truncateRangeStd( rColor
.getLuminance() ) ) )
300 ::cppcanvas::IntSRGBA
RGBColor::getIntegerColor() const
302 return ::cppcanvas::makeColor( colorToInt( getRed() ),
303 colorToInt( getGreen() ),
304 colorToInt( getBlue() ),
308 bool operator==( const RGBColor
& rLHS
, const RGBColor
& rRHS
)
310 return ( rLHS
.getRed() == rRHS
.getRed() &&
311 rLHS
.getGreen() == rRHS
.getGreen() &&
312 rLHS
.getBlue() == rRHS
.getBlue() );
315 bool operator!=( const RGBColor
& rLHS
, const RGBColor
& rRHS
)
317 return !( rLHS
== rRHS
);
320 RGBColor
operator+( const RGBColor
& rLHS
, const RGBColor
& rRHS
)
322 return RGBColor( rLHS
.getRed() + rRHS
.getRed(),
323 rLHS
.getGreen() + rRHS
.getGreen(),
324 rLHS
.getBlue() + rRHS
.getBlue() );
327 RGBColor
operator*( const RGBColor
& rLHS
, const RGBColor
& rRHS
)
329 return RGBColor( rLHS
.getRed() * rRHS
.getRed(),
330 rLHS
.getGreen() * rRHS
.getGreen(),
331 rLHS
.getBlue() * rRHS
.getBlue() );
334 RGBColor
operator*( double nFactor
, const RGBColor
& rRHS
)
336 return RGBColor( nFactor
* rRHS
.getRed(),
337 nFactor
* rRHS
.getGreen(),
338 nFactor
* rRHS
.getBlue() );
341 RGBColor
interpolate( const RGBColor
& rFrom
, const RGBColor
& rTo
, double t
)
343 return RGBColor( (1.0-t
)*rFrom
.getRed() + t
*rTo
.getRed(),
344 (1.0-t
)*rFrom
.getGreen() + t
*rTo
.getGreen(),
345 (1.0-t
)*rFrom
.getBlue() + t
*rTo
.getBlue() );
350 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */