xref: /trunk/main/slideshow/source/engine/color.cxx (revision a3cdc23e488c57f3433f22cd4458e65c27aa499c)
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