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

Last change on this file since 2199 was 2199, checked in by sam, 8 years ago

color: add conversion from CIE-xyY to CIE-XYZ and start working on
a wavelength to saturated xyY function based on the standard observer
data.

File size: 7.5 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2012 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 = abs(fract(vec3(src.x) + vec3(3.f, 2.f, 1.f) / 3.f) * 6.f - vec3(3.f));
74        return mix(vec3(1.f), clamp(tmp - vec3(1.f), 0.f, 1.f), src.y) * src.z;
75    }
76
77    static vec4 HSVToRGB(vec4 src)
78    {
79        return vec4(HSVToRGB(src.rgb), src.a);
80    }
81
82    /*
83     * Convert linear HSV to linear HSL
84     */
85    static vec3 HSVToHSL(vec3 src)
86    {
87        float tmp = (2 - src.y) * src.z;
88        return vec3(src.x, src.y * src.z / (1.f - abs(1.f - tmp)), 0.5f * tmp);
89    }
90
91    static vec4 HSVToHSL(vec4 src)
92    {
93        return vec4(HSVToHSL(src.rgb), src.a);
94    }
95
96    /*
97     * Convert linear HSL to linear HSV
98     */
99    static vec3 HSLToHSV(vec3 src)
100    {
101        float tmp = src.y * (0.5f - abs(0.5f - src.z));
102        return vec3(src.x, 2.f * tmp / (src.z + tmp), src.z + tmp);
103    }
104
105    static vec4 HSLToHSV(vec4 src)
106    {
107        return vec4(HSLToHSV(src.rgb), src.a);
108    }
109
110    /*
111     * Convert linear RGB [0 1] to CIE XYZ [0 100]
112     */
113    static vec3 LinearRGBToCIEXYZ(vec3 src)
114    {
115        mat3 m(vec3(41.24f, 21.26f,  1.93f),
116               vec3(35.76f, 71.52f, 11.92f),
117               vec3(18.05f,  7.22f, 95.05f));
118        return m * src;
119    }
120
121    static vec4 LinearRGBToCIEXYZ(vec4 src)
122    {
123        return vec4(LinearRGBToCIEXYZ(src.rgb), src.a);
124    }
125
126    /*
127     * Convert CIE XYZ [0 100] to linear RGB [0 1]
128     */
129    static vec3 CIEXYZToLinearRGB(vec3 src)
130    {
131        mat3 m(vec3( 0.032406f, -0.009689f,  0.000557f),
132               vec3(-0.015372f,  0.018758f, -0.002040f),
133               vec3(-0.004986f,  0.000415f,  0.010570f));
134        return m * src;
135    }
136
137    static vec4 CIEXYZToLinearRGB(vec4 src)
138    {
139        return vec4(CIEXYZToLinearRGB(src.rgb), src.a);
140    }
141
142    /*
143     * Convert CIE XYZ to CIE xyY
144     */
145    static vec3 CIEXYZToCIExyY(vec3 src)
146    {
147        float tmp = 1.0f / (src.x + src.y + src.z);
148        return vec3(src.x * tmp, src.y * tmp, src.y);
149    }
150
151    static vec4 CIEXYZToCIExyY(vec4 src)
152    {
153        return vec4(CIEXYZToCIExyY(src.rgb), src.a);
154    }
155
156    /*
157     * Convert CIE xyY to CIE XYZ
158     */
159    static vec3 CIExyYToCIEXYZ(vec3 src)
160    {
161        return src.z * vec3(src.x / src.y,
162                            1.0f,
163                            (1.0f - src.x - src.y) / src.y);
164    }
165
166    static vec4 CIExyYToCIEXYZ(vec4 src)
167    {
168        return vec4(CIEXYZToCIExyY(src.rgb), src.a);
169    }
170
171    /*
172     * Convert CIE XYZ to CIE L*a*b*
173     * Note: XYZ values are normalised using a D65 illuminant if
174     * no white value is provided.
175     */
176    static vec3 CIEXYZToCIELab(vec3 src, vec3 white)
177    {
178        float const a = (6.0 * 6.0 * 6.0) / (29 * 29 * 29);
179        float const b = (29.0 * 29.0) / (3 * 6 * 6);
180        float const c = 4.0 / 29;
181        float const d = 1.0 / 3;
182
183        src /= white;
184
185        vec3 f = b * src + vec3(c);
186        if (src.x > a)
187            f.x = pow(src.x, d);
188        if (src.y > a)
189            f.y = pow(src.y, d);
190        if (src.z > a)
191            f.z = pow(src.z, d);
192
193        return vec3(116.f * f.y - 16.f,
194                    500.f * (f.x - f.y),
195                    200.f * (f.y - f.z));
196    }
197
198    static vec3 CIEXYZToCIELab(vec3 src)
199    {
200        return CIEXYZToCIELab(src, vec3(95.047f, 100.f, 108.883f));
201    }
202
203    static vec4 CIEXYZToCIELab(vec4 src, vec4 white)
204    {
205        return vec4(CIEXYZToCIELab(src.rgb, white.rgb), src.a);
206    }
207
208    static vec4 CIEXYZToCIELab(vec4 src)
209    {
210        return vec4(CIEXYZToCIELab(src.rgb), src.a);
211    }
212
213    /*
214     * Return distance using the CIEDE2000 metric,
215     * input should be in CIE L*a*b*.
216     */
217    static float DistanceCIEDE2000(vec3 lab1, vec3 lab2)
218    {
219        float const deg2rad = 6.28318530718f / 360.f;
220        float const rad2deg = 360.f / 6.28318530718f;
221
222        float C1 = length(lab1.yz);
223        float C2 = length(lab2.yz);
224        float C_ = 0.5f * (C1 + C2);
225
226        float L1 = lab1.x;
227        float L2 = lab2.x;
228        float dLp = L2 - L1;
229        float L_ = 0.5f * (L1 + L2);
230
231        float tmp1 = pow(C_, 7.f);
232        float tmp2 = 1.5f - 0.5f * sqrt(tmp1 / (tmp1 + pow(25.f, 7.f)));
233        float ap1 = lab1.y * tmp2;
234        float ap2 = lab2.y * tmp2;
235        float Cp1 = sqrt(ap1 * ap1 + lab1.z * lab1.z);
236        float Cp2 = sqrt(ap2 * ap2 + lab2.z * lab2.z);
237        float dCp = Cp2 - Cp1;
238        float Cp_ = 0.5f * (Cp1 + Cp2);
239
240        float hp1 = fmod(atan2(lab1.z, ap1) * rad2deg, 360.f);
241        if (hp1 < 0.f)
242            hp1 += 360.f;
243        float hp2 = fmod(atan2(lab2.z, ap2) * rad2deg, 360.f);
244        if (hp2 < 0.f)
245            hp2 += 360.f;
246        float dhp;
247        if (abs(hp1 - hp2) <= 180.f)
248            dhp = hp2 - hp1;
249        else if (hp2 <= hp1)
250            dhp = hp2 - hp1 + 360.f;
251        else
252            dhp = hp2 - hp1 - 360.f;
253        float dHp = 2.f * sqrt(Cp1 * Cp2) * sin(dhp / 2.f * deg2rad);
254        float Hp_;
255        if (abs(hp1 - hp2) > 180.f)
256            Hp_ = 0.5f * (hp1 + hp2 + 360.f);
257        else
258            Hp_ = 0.5f * (hp1 + hp2);
259
260        float T = 1.f - 0.17f * cos((Hp_ - 30.f) * deg2rad)
261                      + 0.24f * cos(2 * Hp_ * deg2rad)
262                      + 0.32f * cos((3.f * Hp_ + 6.f) * deg2rad)
263                      - 0.20f * cos((4.f * Hp_ - 63.f) * deg2rad);
264        float SL = 1.f + 0.015f * (L_ - 50) * (L_ - 50)
265                                / sqrt(20.f + (L_ - 50) * (L_ - 50));
266        float SC = 1.f + 0.045f * Cp_;
267        float SH = 1.f + 0.015f * Cp_ * T;
268        float RT = -2.f * sqrt(pow(Cp_, 7.f) / (pow(Cp_, 7.f) + pow(25.f, 7.f)))
269                        * sin(60.f * deg2rad * exp(-pow((Hp_ - 275.f) / 25.f, 2.f)));
270
271        dLp /= SL;
272        dCp /= SC;
273        dHp /= SH;
274
275        return sqrt(dLp * dLp + dCp * dCp + dHp * dHp + RT * dCp * dHp);
276    }
277
278    /* Convert a wavelength to an xyY fully saturated colour */
279    vec3 WavelengthToCIExyY(float nm);
280};
281
282} /* namespace lol */
283
284#endif // __LOL_IMAGE_COLOR_H__
285
Note: See TracBrowser for help on using the repository browser.