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