source: trunk/src/lol/image/color.h @ 2271

Last change on this file since 2271 was 2271, checked in by sam, 10 years ago

color: tweak RGB to HSV/HSL code for not yet investigated numerical
stability issues.

File size: 6.9 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://www.wtfpl.net/ for more details.
9//
10
11//
12// The Color class
13// ---------------
14// Provides various color conversion routines.
15//
16
17#if !defined __LOL_IMAGE_COLOR_H__
18#define __LOL_IMAGE_COLOR_H__
19
20#include <lol/math/vector.h>
21
22namespace lol
23{
24
25class Color
26{
27public:
28    /*
29     * Convert linear RGB to sRGB
30     */
31    static vec3 LinearRGBTosRGB(vec3 src)
32    {
33        vec3 ret = 12.92f * src;
34        if (src.r > 0.0031308f)
35            ret.r = 1.055f * pow(src.r, 1.0f / 2.4f) - 0.055f;
36        if (src.g > 0.0031308f)
37            ret.g = 1.055f * pow(src.g, 1.0f / 2.4f) - 0.055f;
38        if (src.b > 0.0031308f)
39            ret.b = 1.055f * pow(src.b, 1.0f / 2.4f) - 0.055f;
40        return ret;
41    }
42
43    static vec4 LinearRGBTosRGB(vec4 src)
44    {
45        return vec4(LinearRGBTosRGB(src.rgb), src.a);
46    }
47
48    /*
49     * Convert sRGB to linear RGB
50     */
51    static vec3 sRGBToLinearRGB(vec3 src)
52    {
53        vec3 ret = 1.0f / 12.92f * src;
54        if (src.r > 0.04045f)
55            ret.r = pow(src.r + 0.055f, 2.4f) / pow(1.055f, 2.4f);
56        if (src.g > 0.04045f)
57            ret.g = pow(src.g + 0.055f, 2.4f) / pow(1.055f, 2.4f);
58        if (src.b > 0.04045f)
59            ret.b = pow(src.b + 0.055f, 2.4f) / pow(1.055f, 2.4f);
60        return ret;
61    }
62
63    static vec4 sRGBToLinearRGB(vec4 src)
64    {
65        return vec4(sRGBToLinearRGB(src.rgb), src.a);
66    }
67
68    /*
69     * Convert linear HSV to linear RGB
70     */
71    static vec3 HSVToRGB(vec3 src)
72    {
73        vec3 tmp = vec3(-1.f + abs(6.f * src.x - 3.f),
74                         2.f - abs(6.f * src.x - 2.f),
75                         2.f - abs(6.f * src.x - 4.f));
76        return src.z * mix(vec3(1.f), clamp(tmp, 0.f, 1.f), src.y);
77    }
78
79    static vec4 HSVToRGB(vec4 src)
80    {
81        return vec4(HSVToRGB(src.rgb), src.a);
82    }
83
84    /*
85     * Convert RGB to HSV
86     */
87    static vec3 RGBToHSV(vec3 src)
88    {
89        float K = 0.f;
90
91        if (src.b - src.g > 1e-20f)
92            src = src.rbg, K = -1.f;
93
94        if (src.g - src.r > 1e-20f)
95            src = src.grb, K = -2.f / 6.f - K;
96
97        float chroma = src.r - min(src.g, src.b);
98        return vec3(abs(K + (src.g - src.b) / (6.f * chroma + 1e-20f)),
99                    chroma / (src.r + 1e-20f),
100                    src.r);
101    }
102
103    static vec4 RGBToHSV(vec4 src)
104    {
105        return vec4(RGBToHSV(src.rgb), src.a);
106    }
107
108    /*
109     * Convert RGB to HSL
110     */
111    static vec3 RGBToHSL(vec3 src)
112    {
113        float K = 0.f;
114
115        /* FIXME: this appears to be needed for numerical stability on
116         * i386 hardware using -ffast-math. Otherwise if (src.g < src.b)
117         * would suffice. */
118        if (src.b - src.g > 1e-20f)
119            src = src.rbg, K = -1.f;
120
121        if (src.g - src.r > 1e-20f)
122            src = src.grb, K = -2.f / 6.f - K;
123
124        float chroma = src.r - min(src.g, src.b);
125        float luma = src.r + min(src.g, src.b);
126        return vec3(abs(K + (src.g - src.b) / (6.f * chroma + 1e-20f)),
127                    chroma / (min(luma, 2.f - luma) + 1e-20f),
128                    0.5f * luma);
129    }
130
131    static vec4 RGBToHSL(vec4 src)
132    {
133        return vec4(RGBToHSL(src.rgb), src.a);
134    }
135
136    /*
137     * Convert linear HSV to linear HSL
138     */
139    static vec3 HSVToHSL(vec3 src)
140    {
141        float tmp = (2 - src.y) * src.z;
142        return vec3(src.x,
143                    src.y * src.z / (min(tmp, 2.f - tmp) + 1e-20f),
144                    0.5f * tmp);
145    }
146
147    static vec4 HSVToHSL(vec4 src)
148    {
149        return vec4(HSVToHSL(src.rgb), src.a);
150    }
151
152    /*
153     * Convert linear HSL to linear HSV
154     */
155    static vec3 HSLToHSV(vec3 src)
156    {
157        float tmp = src.y * (0.5f - abs(0.5f - src.z));
158        return vec3(src.x, 2.f * tmp / (src.z + tmp + 1e-20f), src.z + tmp);
159    }
160
161    static vec4 HSLToHSV(vec4 src)
162    {
163        return vec4(HSLToHSV(src.rgb), src.a);
164    }
165
166    /*
167     * Convert linear RGB [0 1] to CIE XYZ [0 100]
168     */
169    static vec3 LinearRGBToCIEXYZ(vec3 src)
170    {
171        mat3 m(vec3(41.24f, 21.26f,  1.93f),
172               vec3(35.76f, 71.52f, 11.92f),
173               vec3(18.05f,  7.22f, 95.05f));
174        return m * src;
175    }
176
177    static vec4 LinearRGBToCIEXYZ(vec4 src)
178    {
179        return vec4(LinearRGBToCIEXYZ(src.rgb), src.a);
180    }
181
182    /*
183     * Convert CIE XYZ [0 100] to linear RGB [0 1]
184     */
185    static vec3 CIEXYZToLinearRGB(vec3 src)
186    {
187        mat3 m(vec3( 0.032406f, -0.009689f,  0.000557f),
188               vec3(-0.015372f,  0.018758f, -0.002040f),
189               vec3(-0.004986f,  0.000415f,  0.010570f));
190        return m * src;
191    }
192
193    static vec4 CIEXYZToLinearRGB(vec4 src)
194    {
195        return vec4(CIEXYZToLinearRGB(src.rgb), src.a);
196    }
197
198    /*
199     * Convert CIE XYZ to CIE xyY
200     */
201    static vec3 CIEXYZToCIExyY(vec3 src)
202    {
203        float tmp = 1.0f / (src.x + src.y + src.z);
204        return vec3(src.x * tmp, src.y * tmp, src.y);
205    }
206
207    static vec4 CIEXYZToCIExyY(vec4 src)
208    {
209        return vec4(CIEXYZToCIExyY(src.rgb), src.a);
210    }
211
212    /*
213     * Convert CIE xyY to CIE XYZ
214     */
215    static vec3 CIExyYToCIEXYZ(vec3 src)
216    {
217        return src.z * vec3(src.x / src.y,
218                            1.0f,
219                            (1.0f - src.x - src.y) / src.y);
220    }
221
222    static vec4 CIExyYToCIEXYZ(vec4 src)
223    {
224        return vec4(CIEXYZToCIExyY(src.rgb), src.a);
225    }
226
227    /*
228     * Convert CIE XYZ to CIE L*a*b*
229     * Note: XYZ values are normalised using a D65 illuminant if
230     * no white value is provided.
231     */
232    static vec3 CIEXYZToCIELab(vec3 src, vec3 white)
233    {
234        float const a = (6.0 * 6.0 * 6.0) / (29 * 29 * 29);
235        float const b = (29.0 * 29.0) / (3 * 6 * 6);
236        float const c = 4.0 / 29;
237        float const d = 1.0 / 3;
238
239        src /= white;
240
241        vec3 f = b * src + vec3(c);
242        if (src.x > a)
243            f.x = pow(src.x, d);
244        if (src.y > a)
245            f.y = pow(src.y, d);
246        if (src.z > a)
247            f.z = pow(src.z, d);
248
249        return vec3(116.f * f.y - 16.f,
250                    500.f * (f.x - f.y),
251                    200.f * (f.y - f.z));
252    }
253
254    static vec3 CIEXYZToCIELab(vec3 src)
255    {
256        return CIEXYZToCIELab(src, vec3(95.047f, 100.f, 108.883f));
257    }
258
259    static vec4 CIEXYZToCIELab(vec4 src, vec4 white)
260    {
261        return vec4(CIEXYZToCIELab(src.rgb, white.rgb), src.a);
262    }
263
264    static vec4 CIEXYZToCIELab(vec4 src)
265    {
266        return vec4(CIEXYZToCIELab(src.rgb), src.a);
267    }
268
269    /*
270     * Return distance using the CIEDE2000 metric,
271     * input should be in CIE L*a*b*.
272     */
273    static float DistanceCIEDE2000(vec3 lab1, vec3 lab2);
274
275    /*
276     * Convert a wavelength into an xyY fully saturated colour.
277     */
278    static vec3 WavelengthToCIExyY(float nm);
279};
280
281} /* namespace lol */
282
283#endif // __LOL_IMAGE_COLOR_H__
284
Note: See TracBrowser for help on using the repository browser.