1 /* Addon which allows converting between different color
2  * representations. Included are:
3  * - HSV (like A4)
4  * - names (mostly X11 color names, except where CSS redefines them)
5  * - HSL (the "better" HSV)
6  * - CMYK (a bit like the opposite of RGB)
7  * - YUV (the Y channel is quite useful for creating grayscale pictures)
8  */
9 #include "allegro5/allegro.h"
10 #include "allegro5/allegro_color.h"
11 #include "allegro5/internal/aintern.h"
12 #include <math.h>
13 #include <stdio.h>
14 
15 typedef struct {
16    char const *name;
17    int r, g, b;
18 } ColorName;
19 
20 /* Taken from http://www.w3.org/TR/2010/PR-css3-color-20101028/#svg-color
21  * This must be sorted correctly for binary search.
22  */
23 static ColorName _al_color_names[] = {
24    {"aliceblue", 0xf0, 0xf8, 0xff},
25    {"antiquewhite", 0xfa, 0xeb, 0xd7},
26    {"aqua", 0x00, 0xff, 0xff},
27    {"aquamarine", 0x7f, 0xff, 0xd4},
28    {"azure", 0xf0, 0xff, 0xff},
29    {"beige", 0xf5, 0xf5, 0xdc},
30    {"bisque", 0xff, 0xe4, 0xc4},
31    {"black", 0x00, 0x00, 0x00},
32    {"blanchedalmond", 0xff, 0xeb, 0xcd},
33    {"blue", 0x00, 0x00, 0xff},
34    {"blueviolet", 0x8a, 0x2b, 0xe2},
35    {"brown", 0xa5, 0x2a, 0x2a},
36    {"burlywood", 0xde, 0xb8, 0x87},
37    {"cadetblue", 0x5f, 0x9e, 0xa0},
38    {"chartreuse", 0x7f, 0xff, 0x00},
39    {"chocolate", 0xd2, 0x69, 0x1e},
40    {"coral", 0xff, 0x7f, 0x50},
41    {"cornflowerblue", 0x64, 0x95, 0xed},
42    {"cornsilk", 0xff, 0xf8, 0xdc},
43    {"crimson", 0xdc, 0x14, 0x3c},
44    {"cyan", 0x00, 0xff, 0xff},
45    {"darkblue", 0x00, 0x00, 0x8b},
46    {"darkcyan", 0x00, 0x8b, 0x8b},
47    {"darkgoldenrod", 0xb8, 0x86, 0x0b},
48    {"darkgray", 0xa9, 0xa9, 0xa9},
49    {"darkgreen", 0x00, 0x64, 0x00},
50    {"darkgrey", 0xa9, 0xa9, 0xa9},
51    {"darkkhaki", 0xbd, 0xb7, 0x6b},
52    {"darkmagenta", 0x8b, 0x00, 0x8b},
53    {"darkolivegreen", 0x55, 0x6b, 0x2f},
54    {"darkorange", 0xff, 0x8c, 0x00},
55    {"darkorchid", 0x99, 0x32, 0xcc},
56    {"darkred", 0x8b, 0x00, 0x00},
57    {"darksalmon", 0xe9, 0x96, 0x7a},
58    {"darkseagreen", 0x8f, 0xbc, 0x8f},
59    {"darkslateblue", 0x48, 0x3d, 0x8b},
60    {"darkslategray", 0x2f, 0x4f, 0x4f},
61    {"darkslategrey", 0x2f, 0x4f, 0x4f},
62    {"darkturquoise", 0x00, 0xce, 0xd1},
63    {"darkviolet", 0x94, 0x00, 0xd3},
64    {"deeppink", 0xff, 0x14, 0x93},
65    {"deepskyblue", 0x00, 0xbf, 0xff},
66    {"dimgray", 0x69, 0x69, 0x69},
67    {"dimgrey", 0x69, 0x69, 0x69},
68    {"dodgerblue", 0x1e, 0x90, 0xff},
69    {"firebrick", 0xb2, 0x22, 0x22},
70    {"floralwhite", 0xff, 0xfa, 0xf0},
71    {"forestgreen", 0x22, 0x8b, 0x22},
72    {"fuchsia", 0xff, 0x00, 0xff},
73    {"gainsboro", 0xdc, 0xdc, 0xdc},
74    {"ghostwhite", 0xf8, 0xf8, 0xff},
75    {"gold", 0xff, 0xd7, 0x00},
76    {"goldenrod", 0xda, 0xa5, 0x20},
77    {"gray", 0x80, 0x80, 0x80},
78    {"green", 0x00, 0x80, 0x00},
79    {"greenyellow", 0xad, 0xff, 0x2f},
80    {"grey", 0x80, 0x80, 0x80},
81    {"honeydew", 0xf0, 0xff, 0xf0},
82    {"hotpink", 0xff, 0x69, 0xb4},
83    {"indianred", 0xcd, 0x5c, 0x5c},
84    {"indigo", 0x4b, 0x00, 0x82},
85    {"ivory", 0xff, 0xff, 0xf0},
86    {"khaki", 0xf0, 0xe6, 0x8c},
87    {"lavender", 0xe6, 0xe6, 0xfa},
88    {"lavenderblush", 0xff, 0xf0, 0xf5},
89    {"lawngreen", 0x7c, 0xfc, 0x00},
90    {"lemonchiffon", 0xff, 0xfa, 0xcd},
91    {"lightblue", 0xad, 0xd8, 0xe6},
92    {"lightcoral", 0xf0, 0x80, 0x80},
93    {"lightcyan", 0xe0, 0xff, 0xff},
94    {"lightgoldenrodyellow", 0xfa, 0xfa, 0xd2},
95    {"lightgray", 0xd3, 0xd3, 0xd3},
96    {"lightgreen", 0x90, 0xee, 0x90},
97    {"lightgrey", 0xd3, 0xd3, 0xd3},
98    {"lightpink", 0xff, 0xb6, 0xc1},
99    {"lightsalmon", 0xff, 0xa0, 0x7a},
100    {"lightseagreen", 0x20, 0xb2, 0xaa},
101    {"lightskyblue", 0x87, 0xce, 0xfa},
102    {"lightslategray", 0x77, 0x88, 0x99},
103    {"lightslategrey", 0x77, 0x88, 0x99},
104    {"lightsteelblue", 0xb0, 0xc4, 0xde},
105    {"lightyellow", 0xff, 0xff, 0xe0},
106    {"lime", 0x00, 0xff, 0x00},
107    {"limegreen", 0x32, 0xcd, 0x32},
108    {"linen", 0xfa, 0xf0, 0xe6},
109    {"magenta", 0xff, 0x00, 0xff},
110    {"maroon", 0x80, 0x00, 0x00},
111    {"mediumaquamarine", 0x66, 0xcd, 0xaa},
112    {"mediumblue", 0x00, 0x00, 0xcd},
113    {"mediumorchid", 0xba, 0x55, 0xd3},
114    {"mediumpurple", 0x93, 0x70, 0xdb},
115    {"mediumseagreen", 0x3c, 0xb3, 0x71},
116    {"mediumslateblue", 0x7b, 0x68, 0xee},
117    {"mediumspringgreen", 0x00, 0xfa, 0x9a},
118    {"mediumturquoise", 0x48, 0xd1, 0xcc},
119    {"mediumvioletred", 0xc7, 0x15, 0x85},
120    {"midnightblue", 0x19, 0x19, 0x70},
121    {"mintcream", 0xf5, 0xff, 0xfa},
122    {"mistyrose", 0xff, 0xe4, 0xe1},
123    {"moccasin", 0xff, 0xe4, 0xb5},
124    {"navajowhite", 0xff, 0xde, 0xad},
125    {"navy", 0x00, 0x00, 0x80},
126    {"oldlace", 0xfd, 0xf5, 0xe6},
127    {"olive", 0x80, 0x80, 0x00},
128    {"olivedrab", 0x6b, 0x8e, 0x23},
129    {"orange", 0xff, 0xa5, 0x00},
130    {"orangered", 0xff, 0x45, 0x00},
131    {"orchid", 0xda, 0x70, 0xd6},
132    {"palegoldenrod", 0xee, 0xe8, 0xaa},
133    {"palegreen", 0x98, 0xfb, 0x98},
134    {"paleturquoise", 0xaf, 0xee, 0xee},
135    {"palevioletred", 0xdb, 0x70, 0x93},
136    {"papayawhip", 0xff, 0xef, 0xd5},
137    {"peachpuff", 0xff, 0xda, 0xb9},
138    {"peru", 0xcd, 0x85, 0x3f},
139    {"pink", 0xff, 0xc0, 0xcb},
140    {"plum", 0xdd, 0xa0, 0xdd},
141    {"powderblue", 0xb0, 0xe0, 0xe6},
142    {"purple", 0x80, 0x00, 0x80},
143    {"rebeccapurple", 0x66, 0x33, 0x99},
144    {"red", 0xff, 0x00, 0x00},
145    {"rosybrown", 0xbc, 0x8f, 0x8f},
146    {"royalblue", 0x41, 0x69, 0xe1},
147    {"saddlebrown", 0x8b, 0x45, 0x13},
148    {"salmon", 0xfa, 0x80, 0x72},
149    {"sandybrown", 0xf4, 0xa4, 0x60},
150    {"seagreen", 0x2e, 0x8b, 0x57},
151    {"seashell", 0xff, 0xf5, 0xee},
152    {"sienna", 0xa0, 0x52, 0x2d},
153    {"silver", 0xc0, 0xc0, 0xc0},
154    {"skyblue", 0x87, 0xce, 0xeb},
155    {"slateblue", 0x6a, 0x5a, 0xcd},
156    {"slategray", 0x70, 0x80, 0x90},
157    {"slategrey", 0x70, 0x80, 0x90},
158    {"snow", 0xff, 0xfa, 0xfa},
159    {"springgreen", 0x00, 0xff, 0x7f},
160    {"steelblue", 0x46, 0x82, 0xb4},
161    {"tan", 0xd2, 0xb4, 0x8c},
162    {"teal", 0x00, 0x80, 0x80},
163    {"thistle", 0xd8, 0xbf, 0xd8},
164    {"tomato", 0xff, 0x63, 0x47},
165    {"turquoise", 0x40, 0xe0, 0xd0},
166    {"violet", 0xee, 0x82, 0xee},
167    {"wheat", 0xf5, 0xde, 0xb3},
168    {"white", 0xff, 0xff, 0xff},
169    {"whitesmoke", 0xf5, 0xf5, 0xf5},
170    {"yellow", 0xff, 0xff, 0x00},
171    {"yellowgreen", 0x9a, 0xcd, 0x32},
172 };
173 
174 #define NUM_COLORS (sizeof(_al_color_names) / sizeof(ColorName))
175 
176 static double const Xn = 0.95047;
177 static double const Yn = 1.00000;
178 static double const Zn = 1.08883;
179 static double const delta = 6.0 / 29;
180 static double const delta2 = 6.0 / 29 * 6.0 / 29;
181 static double const delta3 = 6.0 / 29 * 6.0 / 29 * 6.0 / 29;
182 static double const tf7 = 1.0 / 4 / 4 / 4 / 4 / 4 / 4 / 4;
183 
assert_sorted_names(void)184 static void assert_sorted_names(void)
185 {
186    /* In debug mode, check once that the array is sorted. */
187 #ifdef DEBUGMODE
188    static bool done = false;
189    unsigned i;
190 
191    if (!done) {
192       for (i = 1; i < NUM_COLORS; i++) {
193          ASSERT(strcmp(_al_color_names[i-1].name, _al_color_names[i].name) < 0);
194       }
195       done = true;
196    }
197 #endif
198 }
199 
compare(const void * va,const void * vb)200 static int compare(const void *va, const void *vb)
201 {
202    char const *ca = va;
203    ColorName const *cb = vb;
204    return strcmp(ca, cb->name);
205 }
206 
207 
208 /* Function: al_color_name_to_rgb
209  */
al_color_name_to_rgb(char const * name,float * r,float * g,float * b)210 bool al_color_name_to_rgb(char const *name, float *r, float *g, float *b)
211 {
212    void *result;
213    assert_sorted_names();
214    result = bsearch(name, _al_color_names, NUM_COLORS, sizeof(ColorName),
215       compare);
216    if (result) {
217       ColorName *c = result;
218       *r = c->r / 255.0;
219       *g = c->g / 255.0;
220       *b = c->b / 255.0;
221       return true;
222    }
223    return false;
224 }
225 
226 
227 /* Function: al_color_rgb_to_name
228  */
al_color_rgb_to_name(float r,float g,float b)229 char const *al_color_rgb_to_name(float r, float g, float b)
230 {
231    int i;
232    int ir = r * 255;
233    int ig = g * 255;
234    int ib = b * 255;
235    int n = NUM_COLORS;
236    int min = n, mind = 0;
237    /* Could optimize this, right now it does linear search. */
238    for (i = 0; i < n; i++) {
239       int dr = _al_color_names[i].r - ir;
240       int dg = _al_color_names[i].g - ig;
241       int db = _al_color_names[i].b - ib;
242       int d = dr * dr + dg * dg + db * db;
243       if (min == n || d < mind) {
244          min = i;
245          mind = d;
246       }
247    }
248    return _al_color_names[min].name;
249 }
250 
251 
252 /* Function: al_color_name
253  */
al_color_name(char const * name)254 ALLEGRO_COLOR al_color_name(char const *name)
255 {
256    float r, g, b;
257    if (al_color_name_to_rgb(name, &r, &g, &b))
258       return al_map_rgb_f(r, g, b);
259    else
260       return al_map_rgb_f(0, 0, 0);
261 }
262 
263 
264 /* Function: al_color_hsv_to_rgb
265  */
al_color_hsv_to_rgb(float hue,float saturation,float value,float * red,float * green,float * blue)266 void al_color_hsv_to_rgb(float hue, float saturation, float value,
267    float *red, float *green, float *blue)
268 {
269    int d;
270    float e, a, b, c;
271 
272    hue = fmodf(hue, 360);
273    if (hue < 0)
274       hue += 360;
275    d = hue / 60;
276    e = hue / 60 - d;
277    a = value * (1 - saturation);
278    b = value * (1 - e * saturation);
279    c = value * (1 - (1 - e) * saturation);
280    switch (d) {
281       default:
282       case 0: *red = value, *green = c,     *blue = a;     return;
283       case 1: *red = b,     *green = value, *blue = a;     return;
284       case 2: *red = a,     *green = value, *blue = c;     return;
285       case 3: *red = a,     *green = b,     *blue = value; return;
286       case 4: *red = c,     *green = a,     *blue = value; return;
287       case 5: *red = value, *green = a,     *blue = b;     return;
288    }
289 }
290 
291 
292 /* Function: al_color_rgb_to_hsv
293  */
al_color_rgb_to_hsv(float red,float green,float blue,float * hue,float * saturation,float * value)294 void al_color_rgb_to_hsv(float red, float green, float blue,
295    float *hue, float *saturation, float *value)
296 {
297    float a, b, c, d;
298    if (red > green) {
299       if (red > blue) {
300          if (green > blue)
301             a = red, b = green - blue, c = blue, d = 0;
302          else
303             a = red, b = green - blue, c = green, d = 0;
304       }
305       else {
306          a = blue, b = red - green, c = green, d = 4;
307       }
308    }
309    else {
310       if (red > blue)
311           a = green, b = blue - red, c = blue, d = 2;
312       else if (green > blue)
313          a = green, b = blue - red, c = red, d = 2;
314       else
315          a = blue, b = red - green, c = red, d = 4;
316    }
317 
318    if (a == c) {
319       *hue = 0;
320    }
321    else {
322       *hue = 60 * (d + b / (a - c));
323       if (*hue < 0)
324          *hue += 360;
325       if (*hue > 360)
326          *hue -= 360;
327    }
328 
329    if (a == 0)
330       *saturation = 0;
331    else
332       *saturation = (a - c) / a;
333    *value = a;
334 }
335 
336 
337 /* Function: al_color_hsv
338  */
al_color_hsv(float h,float s,float v)339 ALLEGRO_COLOR al_color_hsv(float h, float s, float v)
340 {
341    float r, g, b;
342 
343    al_color_hsv_to_rgb(h, s, v, &r, &g, &b);
344    return al_map_rgb_f(r, g, b);
345 }
346 
347 
hsl_to_rgb_helper(float x,float a,float b)348 static float hsl_to_rgb_helper(float x, float a, float b)
349 {
350    if (x < 0)
351       x += 1;
352    if (x > 1)
353       x -= 1;
354 
355    if (x * 6 < 1)
356       return b + (a - b) * 6 * x;
357    if (x * 6 < 3)
358       return a;
359    if (x * 6 < 4)
360       return b + (a - b) * (4.0 - 6.0 * x);
361    return b;
362 }
363 
364 
365 /* Function: al_color_hsl_to_rgb
366  */
al_color_hsl_to_rgb(float hue,float saturation,float lightness,float * red,float * green,float * blue)367 void al_color_hsl_to_rgb(float hue, float saturation, float lightness,
368    float *red, float *green, float *blue)
369 {
370    float a, b, h;
371 
372    hue = fmodf(hue, 360);
373    if (hue < 0)
374       hue += 360;
375    h = hue / 360;
376    if (lightness < 0.5)
377       a = lightness + lightness * saturation;
378    else
379       a = lightness + saturation - lightness * saturation;
380    b = lightness * 2 - a;
381    *red = hsl_to_rgb_helper(h + 1.0 / 3.0, a, b);
382    *green = hsl_to_rgb_helper(h, a, b);
383    *blue = hsl_to_rgb_helper(h - 1.0 / 3.0, a, b);
384 }
385 
386 
387 /* Function: al_color_rgb_to_hsl
388  */
al_color_rgb_to_hsl(float red,float green,float blue,float * hue,float * saturation,float * lightness)389 void al_color_rgb_to_hsl(float red, float green, float blue,
390    float *hue, float *saturation, float *lightness)
391 {
392    float a, b, c, d;
393 
394    if (red > green) {
395       if (red > blue) {
396          if (green > blue)
397             a = red, b = green - blue, c = blue, d = 0;
398          else
399             a = red, b = green - blue, c = green, d = 0;
400       }
401       else {
402          a = blue, b = red - green, c = green, d = 4;
403       }
404    }
405    else {
406       if (red > blue)
407          a = green, b = blue - red, c = blue, d = 2;
408       else if (green > blue)
409          a = green, b = blue - red, c = red, d = 2;
410       else
411          a = blue, b = red - green, c = red, d = 4;
412    }
413 
414    if (a == c) {
415       *hue = 0;
416    }
417    else {
418       *hue = 60 * (d + b / (a - c));
419       if (*hue < 0)
420          *hue += 360;
421    }
422 
423    if (a == c)
424       *saturation = 0;
425    else if (a + c < 1)
426       *saturation = (a - c) / (a + c);
427    else
428       *saturation = (a - c) / (2 - a - c);
429 
430    *lightness = (a + c) / 2;
431 }
432 
433 
434 /* Function: al_color_hsl
435  */
al_color_hsl(float h,float s,float l)436 ALLEGRO_COLOR al_color_hsl(float h, float s, float l)
437 {
438    float r, g, b;
439    al_color_hsl_to_rgb(h, s, l, &r, &g, &b);
440    return al_map_rgb_f(r, g, b);
441 }
442 
443 
444 /* Function: al_color_cmyk_to_rgb
445  */
al_color_cmyk_to_rgb(float cyan,float magenta,float yellow,float key,float * red,float * green,float * blue)446 void al_color_cmyk_to_rgb(float cyan, float magenta, float yellow,
447     float key, float *red, float *green, float *blue)
448 {
449    float max = 1 - key;
450    *red = max - cyan * max;
451    *green = max - magenta * max;
452    *blue = max - yellow * max;
453 }
454 
455 
456 /* Function: al_color_rgb_to_cmyk
457  */
al_color_rgb_to_cmyk(float red,float green,float blue,float * cyan,float * magenta,float * yellow,float * key)458 void al_color_rgb_to_cmyk(float red, float green, float blue,
459    float *cyan, float *magenta, float *yellow, float *key)
460 {
461    float max = red;
462    if (green > max)
463       max = green;
464    if (blue > max)
465       max = blue;
466    *key = 1 - max;
467    if (max > 0) {
468       *cyan = (max - red) / max;
469       *magenta = (max - green) / max;
470       *yellow = (max - blue) / max;
471    }
472    else {
473       *cyan = *magenta = *yellow = 0;
474    }
475 }
476 
477 
478 /* Function: al_color_cmyk
479  */
al_color_cmyk(float c,float m,float y,float k)480 ALLEGRO_COLOR al_color_cmyk(float c, float m, float y, float k)
481 {
482    float r, g, b;
483    al_color_cmyk_to_rgb(c, m, y, k, &r, &g, &b);
484    return al_map_rgb_f(r, g, b);
485 }
486 
487 
488 /* Function: al_color_yuv_to_rgb
489  */
al_color_yuv_to_rgb(float y,float u,float v,float * red,float * green,float * blue)490 void al_color_yuv_to_rgb(float y, float u, float v,
491     float *red, float *green, float *blue)
492 {
493    /* Translate range 0..1 to actual range. */
494    u = 0.436 * (u * 2 - 1);
495    v = 0.615 * (v * 2 - 1);
496    *red = _ALLEGRO_CLAMP(0, 1, y + v * 1.13983);
497    *green = _ALLEGRO_CLAMP(0, 1, y + u * -0.39465 + v * -0.58060);
498    *blue = _ALLEGRO_CLAMP(0, 1, y + u * 2.03211);
499 }
500 
501 
502 /* Function: al_color_rgb_to_yuv
503  */
al_color_rgb_to_yuv(float red,float green,float blue,float * y,float * u,float * v)504 void al_color_rgb_to_yuv(float red, float green, float blue,
505    float *y, float *u, float *v)
506 {
507    *y = red * 0.299 + green * 0.587 + blue * 0.114;
508    *u = red * -0.14713 + green * -0.28886 + blue * 0.436;
509    *v = red * 0.615 + green * -0.51499 + blue * -0.10001;
510    /* Normalize chroma components into 0..1 range. */
511    *u = (*u / 0.436 + 1) * 0.5;
512    *v = (*v / 0.615 + 1) * 0.5;
513 }
514 
515 
516 /* Function: al_color_yuv
517  */
al_color_yuv(float y,float u,float v)518 ALLEGRO_COLOR al_color_yuv(float y, float u, float v)
519 {
520    float r, g, b;
521    al_color_yuv_to_rgb(y, u, v, &r, &g, &b);
522    return al_map_rgb_f(r, g, b);
523 }
524 
525 
526 /* Function: al_color_rgb_to_html
527  */
al_color_rgb_to_html(float red,float green,float blue,char * string)528 void al_color_rgb_to_html(float red, float green, float blue,
529     char *string)
530 {
531    sprintf(string, "#%02x%02x%02x", (int)(red * 255),
532       (int)(green * 255), (int)(blue * 255));
533 }
534 
535 
536 /* Function: al_color_html_to_rgb
537  */
al_color_html_to_rgb(char const * string,float * red,float * green,float * blue)538 bool al_color_html_to_rgb(char const *string,
539    float *red, float *green, float *blue)
540 {
541    char const *ptr = string;
542    int ir, ig, ib;
543    ASSERT(ptr);
544    ASSERT(red);
545    ASSERT(green);
546    ASSERT(blue);
547 
548    *red = *green = *blue = 0.0f;
549 
550    if (*ptr == '#')
551       ptr++;
552 
553    if (strlen(ptr) != 6)
554       return false;
555 
556    if (sscanf(ptr, "%02x%02x%02x", &ir, &ig, &ib) != 3)
557       return false;
558 
559    *red = ir / 255.0;
560    *green = ig / 255.0;
561    *blue = ib / 255.0;
562    return true;
563 }
564 
565 
566 /* Function: al_color_html
567  */
al_color_html(char const * string)568 ALLEGRO_COLOR al_color_html(char const *string)
569 {
570    float r, g, b;
571 
572    if (al_color_html_to_rgb(string, &r, &g, &b))
573       return al_map_rgb_f(r, g, b);
574    else
575       return al_map_rgba(0, 0, 0, 0);
576 }
577 
578 
579 /* Function: al_get_allegro_color_version
580  */
al_get_allegro_color_version(void)581 uint32_t al_get_allegro_color_version(void)
582 {
583    return ALLEGRO_VERSION_INT;
584 }
585 
586 
587 /* Converts from an sRGB color component to the linear value.
588  */
srgba_gamma_to_linear(double x)589 static double srgba_gamma_to_linear(double x) {
590    double const a = 0.055;
591    if (x < 0.04045) return x / 12.92;
592    return pow((x + a) / (1 + a), 2.4);
593 }
594 
595 
596 /* Converts a linear color component back into sRGB.
597  */
srgba_linear_to_gamma(double x)598 static double srgba_linear_to_gamma(double x) {
599    double const a = 0.055;
600    if (x < 0.0031308) return x * 12.92;
601    return pow(x, 1 / 2.4) * (1 + a) - a;
602 }
603 
604 
605 /* Function: al_color_xyz_to_rgb
606  */
al_color_xyz_to_rgb(float x,float y,float z,float * red,float * green,float * blue)607 void al_color_xyz_to_rgb(float x, float y, float z,
608     float *red, float *green, float *blue)
609 {
610    double r = 3.2406 * x + (-1.5372 * y) + (-0.4986 * z);
611    double g = -0.9689 * x + 1.8758 * y + 0.0415 * z;
612    double b = 0.0557 * x + (-0.2040 * y) + 1.0570 * z;
613    *red = srgba_linear_to_gamma(r);
614    *green = srgba_linear_to_gamma(g);
615    *blue = srgba_linear_to_gamma(b);
616 }
617 
618 
619 /* Function: al_color_rgb_to_xyz
620  */
al_color_rgb_to_xyz(float red,float green,float blue,float * x,float * y,float * z)621 void al_color_rgb_to_xyz(float red, float green, float blue,
622    float *x, float *y, float *z)
623 {
624    double r = srgba_gamma_to_linear(red);
625    double g = srgba_gamma_to_linear(green);
626    double b = srgba_gamma_to_linear(blue);
627    *x = r * 0.4124 + g * 0.3576 + b * 0.1805;
628    *y = r * 0.2126 + g * 0.7152 + b * 0.0722;
629    *z = r * 0.0193 + g * 0.1192 + b * 0.9505;
630 }
631 
632 
633 /* Function: al_color_xyz
634  */
al_color_xyz(float x,float y,float z)635 ALLEGRO_COLOR al_color_xyz(float x, float y, float z)
636 {
637    float r, g, b;
638    al_color_xyz_to_rgb(x, y, z, &r, &g, &b);
639    return al_map_rgb_f(r, g, b);
640 }
641 
642 
cielab_f(double x)643 static double cielab_f(double x) {
644    if (x > delta3) return pow(x, 1.0 / 3);
645    return 4.0 / 29 + x / delta2 / 3;
646 }
647 
648 
cielab_f_inv(double x)649 static double cielab_f_inv(double x) {
650    if (x > delta) return pow(x, 3);
651    return (x - 4.0 / 29) * 3 * delta2;
652 }
653 
654 
655 /* Function: al_color_lab_to_rgb
656  */
al_color_lab_to_rgb(float l,float a,float b,float * red,float * green,float * blue)657 void al_color_lab_to_rgb(float l, float a, float b,
658     float *red, float *green, float *blue)
659 {
660    float x = Xn * cielab_f_inv((l + 0.16) / 1.16 + a / 5.00);
661    float y = Yn * cielab_f_inv((l + 0.16) / 1.16);
662    float z = Zn * cielab_f_inv((l + 0.16) / 1.16 - b / 2.00);
663    al_color_xyz_to_rgb(x, y, z, red, green, blue);
664 }
665 
666 
667 /* Function: al_color_rgb_to_lab
668  */
al_color_rgb_to_lab(float red,float green,float blue,float * l,float * a,float * b)669 void al_color_rgb_to_lab(float red, float green, float blue,
670    float *l, float *a, float *b)
671 {
672    float x, y, z;
673    al_color_rgb_to_xyz(red, green, blue, &x, &y, &z);
674    x /= Xn;
675    y /= Yn;
676    z /= Zn;
677    *l = 1.16 * cielab_f(y) - 0.16;
678    *a = 5.00 * (cielab_f(x) - cielab_f(y));
679    *b = 2.00 * (cielab_f(y) - cielab_f(z));
680 }
681 
682 
683 /* Function: al_color_lab
684  */
al_color_lab(float l,float a,float b)685 ALLEGRO_COLOR al_color_lab(float l, float a, float b)
686 {
687    float r2, g2, b2;
688    al_color_lab_to_rgb(l, a, b, &r2, &g2, &b2);
689    return al_map_rgb_f(r2, g2, b2);
690 }
691 
692 
693 /* Function: al_color_lch_to_rgb
694  */
al_color_lch_to_rgb(float l,float c,float h,float * red,float * green,float * blue)695 void al_color_lch_to_rgb(float l, float c, float h,
696     float *red, float *green, float *blue)
697 {
698    double a = c * cos(h);
699    double b = c * sin(h);
700    al_color_lab_to_rgb(l, a, b, red, green, blue);
701 }
702 
703 
704 /* Function: al_color_rgb_to_lch
705  */
al_color_rgb_to_lch(float red,float green,float blue,float * l,float * c,float * h)706 void al_color_rgb_to_lch(float red, float green, float blue,
707    float *l, float *c, float *h)
708 {
709    float a, b;
710    al_color_rgb_to_lab(red, green, blue, l, &a, &b);
711    *c = sqrt(a * a + b * b);
712    *h = fmod(ALLEGRO_PI * 2 + atan2(b, a), ALLEGRO_PI * 2);
713 }
714 
715 
716 /* Function: al_color_lch
717  */
al_color_lch(float l,float c,float h)718 ALLEGRO_COLOR al_color_lch(float l, float c, float h)
719 {
720    float r, g, b;
721    al_color_lch_to_rgb(l, c, h, &r, &g, &b);
722    return al_map_rgb_f(r, g, b);
723 }
724 
725 
726 /* Function: al_color_xyy_to_rgb
727  */
al_color_xyy_to_rgb(float x,float y,float y2,float * red,float * green,float * blue)728 void al_color_xyy_to_rgb(float x, float y, float y2,
729     float *red, float *green, float *blue)
730 {
731    double x2 = x * y / y2;
732    double z2 = (1 - y2 - x) * y / y2;
733    al_color_xyz_to_rgb(x2, y2, z2, red, green, blue);
734 }
735 
736 
737 /* Function: al_color_rgb_to_xyy
738  */
al_color_rgb_to_xyy(float red,float green,float blue,float * x,float * y,float * y2)739 void al_color_rgb_to_xyy(float red, float green, float blue,
740    float *x, float *y, float *y2)
741 {
742    float x2, z2;
743    al_color_rgb_to_xyz(red, green, blue, &x2, y2, &z2);
744    *x = x2 / (x2 + *y2 + z2);
745    *y = *y2 / (x2 + *y2 + z2);
746 }
747 
748 
749 /* Function: al_color_xyy
750  */
al_color_xyy(float x,float y,float y2)751 ALLEGRO_COLOR al_color_xyy(float x, float y, float y2)
752 {
753    float r, g, b;
754    al_color_xyy_to_rgb(x, y, y2, &r, &g, &b);
755    return al_map_rgb_f(r, g, b);
756 }
757 
758 
759 /* Function: al_color_distance_ciede2000
760  */
al_color_distance_ciede2000(ALLEGRO_COLOR color1,ALLEGRO_COLOR color2)761 double al_color_distance_ciede2000(ALLEGRO_COLOR color1,
762       ALLEGRO_COLOR color2) {
763    /* For implementation details refer to e.g.
764     * http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf
765     */
766    float l1, a1, b1, l2, a2, b2;
767    al_color_rgb_to_lab(color1.r, color1.g, color1.b, &l1, &a1, &b1);
768    al_color_rgb_to_lab(color2.r, color2.g, color2.b, &l2, &a2, &b2);
769    double pi = ALLEGRO_PI;
770    double dl = l1 - l2;
771    double ml = (l1 + l2) / 2;
772    double c1 = sqrt(a1 * a1 + b1 * b1);
773    double c2 = sqrt(a2 * a2 + b2 * b2);
774    double mc = (c1 + c2) / 2;
775    double fac = sqrt(pow(mc, 7) / (pow(mc, 7) + tf7));
776    double g = 0.5 * (1 - fac);
777    a1 *= 1 + g;
778    a2 *= 1 + g;
779    c1 = sqrt(a1 * a1 + b1 * b1);
780    c2 = sqrt(a2 * a2 + b2 * b2);
781    double dc = c2 - c1;
782    mc = (c1 + c2) / 2;
783    fac = sqrt(pow(mc, 7) / (pow(mc, 7) + tf7));
784    double h1 = fmod(2 * pi + atan2(b1, a1), 2 * pi);
785    double h2 = fmod(2 * pi + atan2(b2, a2), 2 * pi);
786    double dh = 0;
787    double mh = h1 + h2;
788    if (c1 * c2 != 0) {
789       dh = h2 - h1;
790       if (dh > pi) dh -= 2 * pi;
791       if (dh < -pi) dh += 2 * pi;
792       if (fabs(h1 - h2) <= pi) mh = (h1 + h2) / 2;
793       else if (h1 + h2 < 2 * pi) mh = (h1 + h2 + 2 * pi) / 2;
794       else mh = (h1 + h2 - 2 * pi) / 2;
795    }
796    dh = 2 * sqrt(c1 * c2) * sin(dh / 2);
797    double t = 1 - 0.17 * cos(mh - pi / 6) + 0.24 * cos(2 * mh) +
798          0.32 * cos(3 * mh + pi / 30) - 0.2 * cos(4 * mh - pi * 7 / 20);
799    double mls = pow(ml - 0.5, 2);
800    double sl = 1 + 1.5 * mls / sqrt(0.002 + mls);
801    double sc = 1 + 4.5 * mc;
802    double sh = 1 + 1.5 * mc * t;
803    double rt = -2 * fac * sin(pi / 3 *
804          exp(-pow(mh / pi * 36 / 5 - 11, 2)));
805    return sqrt(pow(dl / sl, 2) + pow(dc / sc, 2) +
806          pow(dh / sh, 2) + rt * dc / sc * dh / sh);
807 }
808 
809 /* Function al_color_is_valid
810  */
al_is_color_valid(ALLEGRO_COLOR color)811 bool al_is_color_valid(ALLEGRO_COLOR color)
812 {
813    return color.r >= 0 && color.g >= 0 && color.b >= 0 &&
814       color.r <= 1 && color.g <= 1 && color.b <= 1;
815 }
816 
817 /* vim: set sts=3 sw=3 et: */
818