1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <basegfx/numeric/ftools.hxx>
21 #include <basegfx/color/bcolor.hxx>
22 #include <basegfx/color/bcolortools.hxx>
23 #include <rtl/math.hxx>
24 
25 namespace basegfx::utils
26 {
rgb2hsl(const BColor & rRGBColor)27     BColor rgb2hsl(const BColor& rRGBColor)
28     {
29         const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
30         const double minVal = std::min( std::min( r, g ), b );
31         const double maxVal = std::max( std::max( r, g ), b );
32         const double d = maxVal - minVal;
33 
34         double h=0, s=0, l=0;
35 
36         l = (maxVal + minVal) / 2.0;
37 
38         if( ::basegfx::fTools::equalZero(d) )
39         {
40             s = h = 0; // hue undefined (achromatic case)
41         }
42         else
43         {
44             s = l > 0.5 ? d/(2.0-maxVal-minVal) :
45                 d/(maxVal + minVal);
46 
47             if( rtl::math::approxEqual(r, maxVal) )
48                 h = (g - b)/d;
49             else if( rtl::math::approxEqual(g, maxVal) )
50                 h = 2.0 + (b - r)/d;
51             else
52                 h = 4.0 + (r - g)/d;
53 
54             h *= 60.0;
55 
56             if( h < 0.0 )
57                 h += 360.0;
58         }
59 
60         return BColor(h,s,l);
61     }
62 
hsl2rgbHelper(double nValue1,double nValue2,double nHue)63     static double hsl2rgbHelper( double nValue1, double nValue2, double nHue )
64     {
65         // clamp hue to [0,360]
66         nHue = fmod( nHue, 360.0 );
67 
68         // cope with wrap-arounds
69         if( nHue < 0.0 )
70             nHue += 360.0;
71 
72         if( nHue < 60.0 )
73             return nValue1 + (nValue2 - nValue1)*nHue/60.0;
74         else if( nHue < 180.0 )
75             return nValue2;
76         else if( nHue < 240.0 )
77             return nValue1 + (nValue2 - nValue1)*(240.0 - nHue)/60.0;
78         else
79             return nValue1;
80     }
81 
hsl2rgb(const BColor & rHSLColor)82     BColor hsl2rgb(const BColor& rHSLColor)
83     {
84         const double h=rHSLColor.getRed(), s=rHSLColor.getGreen(), l=rHSLColor.getBlue();
85 
86         if( fTools::equalZero(s) )
87             return BColor(l, l, l ); // achromatic case
88 
89         const double nVal1( l <= 0.5 ? l*(1.0 + s) : l + s - l*s );
90         const double nVal2( 2.0*l - nVal1 );
91 
92         return BColor(
93             hsl2rgbHelper(nVal2,
94                           nVal1,
95                           h + 120.0),
96             hsl2rgbHelper(nVal2,
97                           nVal1,
98                           h),
99             hsl2rgbHelper(nVal2,
100                           nVal1,
101                           h - 120.0) );
102     }
103 
rgb2hsv(const BColor & rRGBColor)104     BColor rgb2hsv(const BColor& rRGBColor)
105     {
106         const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
107         const double maxVal = std::max(std::max(r,g),b);
108         const double minVal = std::min(std::min(r,g),b);
109         const double delta = maxVal-minVal;
110 
111         double h=0, s=0, v=0;
112 
113         v = maxVal;
114         if( fTools::equalZero(v) )
115             s = 0;
116         else
117             s = delta / v;
118 
119         if( !fTools::equalZero(s) )
120         {
121             if( rtl::math::approxEqual(maxVal, r) )
122             {
123                 h = (g - b) / delta;
124             }
125             else if( rtl::math::approxEqual(maxVal, g) )
126             {
127                 h = 2.0 + (b - r) / delta;
128             }
129             else
130             {
131                 h = 4.0 + (r - g) / delta;
132             }
133 
134             h *= 60.0;
135 
136             if( h < 0 )
137                 h += 360;
138         }
139 
140         return BColor(h,s,v);
141     }
142 
hsv2rgb(const BColor & rHSVColor)143     BColor hsv2rgb(const BColor& rHSVColor)
144     {
145         double h=rHSVColor.getRed();
146         const double s=rHSVColor.getGreen(), v=rHSVColor.getBlue();
147 
148         if( fTools::equalZero(s) )
149         {
150             // achromatic case: no hue.
151             return BColor(v,v,v);
152         }
153         else
154         {
155             if( fTools::equal(h,360) )
156                 h = 0; // 360 degrees is equivalent to 0 degrees
157 
158             h /= 60.0;
159             const sal_Int32 intval = static_cast< sal_Int32 >( h );
160             const double f = h - intval;
161             const double p = v*(1.0-s);
162             const double q = v*(1.0-(s*f));
163             const double t = v*(1.0-(s*(1.0-f)));
164 
165             /* which hue area? */
166             switch( intval )
167             {
168                 case 0:
169                     return BColor(v,t,p);
170 
171                 case 1:
172                     return BColor(q,v,p);
173 
174                 case 2:
175                     return BColor(p,v,t);
176 
177                 case 3:
178                     return BColor(p,q,v);
179 
180                 case 4:
181                     return BColor(t,p,v);
182 
183                 case 5:
184                     return BColor(v,p,q);
185 
186                 default:
187                     // hue overflow
188                     return BColor();
189             }
190         }
191     }
192 
193 } // end of namespace
194 
195 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
196