Line data Source code
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 :
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 :
200 : static int compare(const void *va, const void *vb)
201 : {
202 3124 : char const *ca = va;
203 3124 : ColorName const *cb = vb;
204 3124 : return strcmp(ca, cb->name);
205 : }
206 :
207 :
208 : /* Function: al_color_name_to_rgb
209 : */
210 518 : bool al_color_name_to_rgb(char const *name, float *r, float *g, float *b)
211 : {
212 : void *result;
213 : assert_sorted_names();
214 518 : result = bsearch(name, _al_color_names, NUM_COLORS, sizeof(ColorName),
215 : compare);
216 518 : if (result) {
217 518 : ColorName *c = result;
218 518 : *r = c->r / 255.0;
219 518 : *g = c->g / 255.0;
220 518 : *b = c->b / 255.0;
221 518 : return true;
222 : }
223 : return false;
224 : }
225 :
226 :
227 : /* Function: al_color_rgb_to_name
228 : */
229 0 : char const *al_color_rgb_to_name(float r, float g, float b)
230 : {
231 : int i;
232 0 : int ir = r * 255;
233 0 : int ig = g * 255;
234 0 : int ib = b * 255;
235 0 : int n = NUM_COLORS;
236 0 : int min = n, mind = 0;
237 : /* Could optimize this, right now it does linear search. */
238 0 : for (i = 0; i < n; i++) {
239 0 : int dr = _al_color_names[i].r - ir;
240 0 : int dg = _al_color_names[i].g - ig;
241 0 : int db = _al_color_names[i].b - ib;
242 0 : int d = dr * dr + dg * dg + db * db;
243 0 : if (min == n || d < mind) {
244 0 : min = i;
245 0 : mind = d;
246 : }
247 : }
248 0 : return _al_color_names[min].name;
249 : }
250 :
251 :
252 : /* Function: al_color_name
253 : */
254 518 : ALLEGRO_COLOR al_color_name(char const *name)
255 : {
256 : float r, g, b;
257 518 : if (al_color_name_to_rgb(name, &r, &g, &b))
258 518 : return al_map_rgb_f(r, g, b);
259 : else
260 0 : return al_map_rgb_f(0, 0, 0);
261 : }
262 :
263 :
264 : /* Function: al_color_hsv_to_rgb
265 : */
266 0 : 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 0 : hue = fmodf(hue, 360);
273 0 : if (hue < 0)
274 0 : hue += 360;
275 0 : d = hue / 60;
276 0 : e = hue / 60 - d;
277 0 : a = value * (1 - saturation);
278 0 : b = value * (1 - e * saturation);
279 0 : c = value * (1 - (1 - e) * saturation);
280 0 : switch (d) {
281 0 : default:
282 0 : case 0: *red = value, *green = c, *blue = a; return;
283 0 : case 1: *red = b, *green = value, *blue = a; return;
284 0 : case 2: *red = a, *green = value, *blue = c; return;
285 0 : case 3: *red = a, *green = b, *blue = value; return;
286 0 : case 4: *red = c, *green = a, *blue = value; return;
287 0 : case 5: *red = value, *green = a, *blue = b; return;
288 : }
289 : }
290 :
291 :
292 : /* Function: al_color_rgb_to_hsv
293 : */
294 0 : 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 0 : if (red > green) {
299 0 : if (red > blue) {
300 0 : if (green > blue)
301 0 : a = red, b = green - blue, c = blue, d = 0;
302 : else
303 0 : a = red, b = green - blue, c = green, d = 0;
304 : }
305 : else {
306 0 : a = blue, b = red - green, c = green, d = 4;
307 : }
308 : }
309 : else {
310 0 : if (red > blue)
311 0 : a = green, b = blue - red, c = blue, d = 2;
312 0 : else if (green > blue)
313 0 : a = green, b = blue - red, c = red, d = 2;
314 : else
315 0 : a = blue, b = red - green, c = red, d = 4;
316 : }
317 :
318 0 : if (a == c) {
319 0 : *hue = 0;
320 : }
321 : else {
322 0 : *hue = 60 * (d + b / (a - c));
323 0 : if (*hue < 0)
324 0 : *hue += 360;
325 0 : if (*hue > 360)
326 0 : *hue -= 360;
327 : }
328 :
329 0 : if (a == 0)
330 0 : *saturation = 0;
331 : else
332 0 : *saturation = (a - c) / a;
333 0 : *value = a;
334 0 : }
335 :
336 :
337 : /* Function: al_color_hsv
338 : */
339 0 : ALLEGRO_COLOR al_color_hsv(float h, float s, float v)
340 : {
341 : float r, g, b;
342 :
343 0 : al_color_hsv_to_rgb(h, s, v, &r, &g, &b);
344 0 : return al_map_rgb_f(r, g, b);
345 : }
346 :
347 :
348 0 : static float hsl_to_rgb_helper(float x, float a, float b)
349 : {
350 0 : if (x < 0)
351 0 : x += 1;
352 0 : if (x > 1)
353 0 : x -= 1;
354 :
355 0 : if (x * 6 < 1)
356 0 : return b + (a - b) * 6 * x;
357 0 : if (x * 6 < 3)
358 : return a;
359 0 : if (x * 6 < 4)
360 0 : return b + (a - b) * (4.0 - 6.0 * x);
361 : return b;
362 : }
363 :
364 :
365 : /* Function: al_color_hsl_to_rgb
366 : */
367 0 : 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 0 : hue = fmodf(hue, 360);
373 0 : if (hue < 0)
374 0 : hue += 360;
375 0 : h = hue / 360;
376 0 : if (lightness < 0.5)
377 0 : a = lightness + lightness * saturation;
378 : else
379 0 : a = lightness + saturation - lightness * saturation;
380 0 : b = lightness * 2 - a;
381 0 : *red = hsl_to_rgb_helper(h + 1.0 / 3.0, a, b);
382 0 : *green = hsl_to_rgb_helper(h, a, b);
383 0 : *blue = hsl_to_rgb_helper(h - 1.0 / 3.0, a, b);
384 0 : }
385 :
386 :
387 : /* Function: al_color_rgb_to_hsl
388 : */
389 0 : 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 0 : if (red > green) {
395 0 : if (red > blue) {
396 0 : if (green > blue)
397 0 : a = red, b = green - blue, c = blue, d = 0;
398 : else
399 0 : a = red, b = green - blue, c = green, d = 0;
400 : }
401 : else {
402 0 : a = blue, b = red - green, c = green, d = 4;
403 : }
404 : }
405 : else {
406 0 : if (red > blue)
407 0 : a = green, b = blue - red, c = blue, d = 2;
408 0 : else if (green > blue)
409 0 : a = green, b = blue - red, c = red, d = 2;
410 : else
411 0 : a = blue, b = red - green, c = red, d = 4;
412 : }
413 :
414 0 : if (a == c) {
415 0 : *hue = 0;
416 : }
417 : else {
418 0 : *hue = 60 * (d + b / (a - c));
419 0 : if (*hue < 0)
420 0 : *hue += 360;
421 : }
422 :
423 0 : if (a == c)
424 0 : *saturation = 0;
425 0 : else if (a + c < 1)
426 0 : *saturation = (a - c) / (a + c);
427 : else
428 0 : *saturation = (a - c) / (2 - a - c);
429 :
430 0 : *lightness = (a + c) / 2;
431 0 : }
432 :
433 :
434 : /* Function: al_color_hsl
435 : */
436 0 : ALLEGRO_COLOR al_color_hsl(float h, float s, float l)
437 : {
438 : float r, g, b;
439 0 : al_color_hsl_to_rgb(h, s, l, &r, &g, &b);
440 0 : return al_map_rgb_f(r, g, b);
441 : }
442 :
443 :
444 : /* Function: al_color_cmyk_to_rgb
445 : */
446 0 : void al_color_cmyk_to_rgb(float cyan, float magenta, float yellow,
447 : float key, float *red, float *green, float *blue)
448 : {
449 0 : float max = 1 - key;
450 0 : *red = max - cyan * max;
451 0 : *green = max - magenta * max;
452 0 : *blue = max - yellow * max;
453 0 : }
454 :
455 :
456 : /* Function: al_color_rgb_to_cmyk
457 : */
458 0 : void al_color_rgb_to_cmyk(float red, float green, float blue,
459 : float *cyan, float *magenta, float *yellow, float *key)
460 : {
461 0 : float max = red;
462 0 : if (green > max)
463 0 : max = green;
464 0 : if (blue > max)
465 0 : max = blue;
466 0 : *key = 1 - max;
467 0 : if (max > 0) {
468 0 : *cyan = (max - red) / max;
469 0 : *magenta = (max - green) / max;
470 0 : *yellow = (max - blue) / max;
471 : }
472 : else {
473 0 : *cyan = *magenta = *yellow = 0;
474 : }
475 0 : }
476 :
477 :
478 : /* Function: al_color_cmyk
479 : */
480 0 : ALLEGRO_COLOR al_color_cmyk(float c, float m, float y, float k)
481 : {
482 : float r, g, b;
483 0 : al_color_cmyk_to_rgb(c, m, y, k, &r, &g, &b);
484 0 : return al_map_rgb_f(r, g, b);
485 : }
486 :
487 :
488 : /* Function: al_color_yuv_to_rgb
489 : */
490 0 : 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 0 : u = 0.436 * (u * 2 - 1);
495 0 : v = 0.615 * (v * 2 - 1);
496 0 : *red = _ALLEGRO_CLAMP(0, 1, y + v * 1.13983);
497 0 : *green = _ALLEGRO_CLAMP(0, 1, y + u * -0.39465 + v * -0.58060);
498 0 : *blue = _ALLEGRO_CLAMP(0, 1, y + u * 2.03211);
499 0 : }
500 :
501 :
502 : /* Function: al_color_rgb_to_yuv
503 : */
504 0 : void al_color_rgb_to_yuv(float red, float green, float blue,
505 : float *y, float *u, float *v)
506 : {
507 0 : *y = red * 0.299 + green * 0.587 + blue * 0.114;
508 0 : *u = red * -0.14713 + green * -0.28886 + blue * 0.436;
509 0 : *v = red * 0.615 + green * -0.51499 + blue * -0.10001;
510 : /* Normalize chroma components into 0..1 range. */
511 0 : *u = (*u / 0.436 + 1) * 0.5;
512 0 : *v = (*v / 0.615 + 1) * 0.5;
513 0 : }
514 :
515 :
516 : /* Function: al_color_yuv
517 : */
518 0 : ALLEGRO_COLOR al_color_yuv(float y, float u, float v)
519 : {
520 : float r, g, b;
521 0 : al_color_yuv_to_rgb(y, u, v, &r, &g, &b);
522 0 : return al_map_rgb_f(r, g, b);
523 : }
524 :
525 :
526 : /* Function: al_color_rgb_to_html
527 : */
528 0 : void al_color_rgb_to_html(float red, float green, float blue,
529 : char *string)
530 : {
531 0 : sprintf(string, "#%02x%02x%02x", (int)(red * 255),
532 0 : (int)(green * 255), (int)(blue * 255));
533 0 : }
534 :
535 :
536 : /* Function: al_color_html_to_rgb
537 : */
538 0 : bool al_color_html_to_rgb(char const *string,
539 : float *red, float *green, float *blue)
540 : {
541 0 : char const *ptr = string;
542 : int ir, ig, ib;
543 : ASSERT(ptr);
544 : ASSERT(red);
545 : ASSERT(green);
546 : ASSERT(blue);
547 :
548 0 : *red = *green = *blue = 0.0f;
549 :
550 0 : if (*ptr == '#')
551 0 : ptr++;
552 :
553 0 : if (strlen(ptr) != 6)
554 : return false;
555 :
556 0 : if (sscanf(ptr, "%02x%02x%02x", &ir, &ig, &ib) != 3)
557 : return false;
558 :
559 0 : *red = ir / 255.0;
560 0 : *green = ig / 255.0;
561 0 : *blue = ib / 255.0;
562 0 : return true;
563 : }
564 :
565 :
566 : /* Function: al_color_html
567 : */
568 0 : ALLEGRO_COLOR al_color_html(char const *string)
569 : {
570 : float r, g, b;
571 :
572 0 : if (al_color_html_to_rgb(string, &r, &g, &b))
573 0 : return al_map_rgb_f(r, g, b);
574 : else
575 0 : return al_map_rgba(0, 0, 0, 0);
576 : }
577 :
578 :
579 : /* Function: al_get_allegro_color_version
580 : */
581 0 : uint32_t al_get_allegro_color_version(void)
582 : {
583 0 : return ALLEGRO_VERSION_INT;
584 : }
585 :
586 :
587 : /* Converts from an sRGB color component to the linear value.
588 : */
589 : static double srgba_gamma_to_linear(double x) {
590 204 : double const a = 0.055;
591 204 : if (x < 0.04045) return x / 12.92;
592 184 : return pow((x + a) / (1 + a), 2.4);
593 : }
594 :
595 :
596 : /* Converts a linear color component back into sRGB.
597 : */
598 : static double srgba_linear_to_gamma(double x) {
599 204 : double const a = 0.055;
600 204 : if (x < 0.0031308) return x * 12.92;
601 184 : return pow(x, 1 / 2.4) * (1 + a) - a;
602 : }
603 :
604 :
605 : /* Function: al_color_xyz_to_rgb
606 : */
607 68 : void al_color_xyz_to_rgb(float x, float y, float z,
608 : float *red, float *green, float *blue)
609 : {
610 68 : double r = 3.2406 * x + (-1.5372 * y) + (-0.4986 * z);
611 68 : double g = -0.9689 * x + 1.8758 * y + 0.0415 * z;
612 68 : double b = 0.0557 * x + (-0.2040 * y) + 1.0570 * z;
613 68 : *red = srgba_linear_to_gamma(r);
614 68 : *green = srgba_linear_to_gamma(g);
615 68 : *blue = srgba_linear_to_gamma(b);
616 68 : }
617 :
618 :
619 : /* Function: al_color_rgb_to_xyz
620 : */
621 68 : void al_color_rgb_to_xyz(float red, float green, float blue,
622 : float *x, float *y, float *z)
623 : {
624 136 : double r = srgba_gamma_to_linear(red);
625 136 : double g = srgba_gamma_to_linear(green);
626 136 : double b = srgba_gamma_to_linear(blue);
627 68 : *x = r * 0.4124 + g * 0.3576 + b * 0.1805;
628 68 : *y = r * 0.2126 + g * 0.7152 + b * 0.0722;
629 68 : *z = r * 0.0193 + g * 0.1192 + b * 0.9505;
630 68 : }
631 :
632 :
633 : /* Function: al_color_xyz
634 : */
635 0 : ALLEGRO_COLOR al_color_xyz(float x, float y, float z)
636 : {
637 : float r, g, b;
638 0 : al_color_xyz_to_rgb(x, y, z, &r, &g, &b);
639 0 : return al_map_rgb_f(r, g, b);
640 : }
641 :
642 :
643 : static double cielab_f(double x) {
644 340 : if (x > delta3) return pow(x, 1.0 / 3);
645 19 : return 4.0 / 29 + x / delta2 / 3;
646 : }
647 :
648 :
649 : static double cielab_f_inv(double x) {
650 204 : if (x > delta) return pow(x, 3);
651 11 : return (x - 4.0 / 29) * 3 * delta2;
652 : }
653 :
654 :
655 : /* Function: al_color_lab_to_rgb
656 : */
657 68 : void al_color_lab_to_rgb(float l, float a, float b,
658 : float *red, float *green, float *blue)
659 : {
660 136 : float x = Xn * cielab_f_inv((l + 0.16) / 1.16 + a / 5.00);
661 136 : float y = Yn * cielab_f_inv((l + 0.16) / 1.16);
662 136 : float z = Zn * cielab_f_inv((l + 0.16) / 1.16 - b / 2.00);
663 68 : al_color_xyz_to_rgb(x, y, z, red, green, blue);
664 68 : }
665 :
666 :
667 : /* Function: al_color_rgb_to_lab
668 : */
669 68 : 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 68 : al_color_rgb_to_xyz(red, green, blue, &x, &y, &z);
674 68 : x /= Xn;
675 68 : y /= Yn;
676 68 : z /= Zn;
677 136 : *l = 1.16 * cielab_f(y) - 0.16;
678 204 : *a = 5.00 * (cielab_f(x) - cielab_f(y));
679 204 : *b = 2.00 * (cielab_f(y) - cielab_f(z));
680 68 : }
681 :
682 :
683 : /* Function: al_color_lab
684 : */
685 68 : ALLEGRO_COLOR al_color_lab(float l, float a, float b)
686 : {
687 : float r2, g2, b2;
688 68 : al_color_lab_to_rgb(l, a, b, &r2, &g2, &b2);
689 68 : return al_map_rgb_f(r2, g2, b2);
690 : }
691 :
692 :
693 : /* Function: al_color_lch_to_rgb
694 : */
695 0 : void al_color_lch_to_rgb(float l, float c, float h,
696 : float *red, float *green, float *blue)
697 : {
698 0 : double a = c * cos(h);
699 0 : double b = c * sin(h);
700 0 : al_color_lab_to_rgb(l, a, b, red, green, blue);
701 0 : }
702 :
703 :
704 : /* Function: al_color_rgb_to_lch
705 : */
706 0 : 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 0 : al_color_rgb_to_lab(red, green, blue, l, &a, &b);
711 0 : *c = sqrt(a * a + b * b);
712 0 : *h = fmod(ALLEGRO_PI * 2 + atan2(b, a), ALLEGRO_PI * 2);
713 0 : }
714 :
715 :
716 : /* Function: al_color_lch
717 : */
718 0 : ALLEGRO_COLOR al_color_lch(float l, float c, float h)
719 : {
720 : float r, g, b;
721 0 : al_color_lch_to_rgb(l, c, h, &r, &g, &b);
722 0 : return al_map_rgb_f(r, g, b);
723 : }
724 :
725 :
726 : /* Function: al_color_xyy_to_rgb
727 : */
728 0 : void al_color_xyy_to_rgb(float x, float y, float y2,
729 : float *red, float *green, float *blue)
730 : {
731 0 : double x2 = x * y / y2;
732 0 : double z2 = (1 - y2 - x) * y / y2;
733 0 : al_color_xyz_to_rgb(x2, y2, z2, red, green, blue);
734 0 : }
735 :
736 :
737 : /* Function: al_color_rgb_to_xyy
738 : */
739 0 : 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 0 : al_color_rgb_to_xyz(red, green, blue, &x2, y2, &z2);
744 0 : *x = x2 / (x2 + *y2 + z2);
745 0 : *y = *y2 / (x2 + *y2 + z2);
746 0 : }
747 :
748 :
749 : /* Function: al_color_xyy
750 : */
751 0 : ALLEGRO_COLOR al_color_xyy(float x, float y, float y2)
752 : {
753 : float r, g, b;
754 0 : al_color_xyy_to_rgb(x, y, y2, &r, &g, &b);
755 0 : return al_map_rgb_f(r, g, b);
756 : }
757 :
758 :
759 : /* Function: al_color_distance_ciede2000
760 : */
761 34 : 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 34 : al_color_rgb_to_lab(color1.r, color1.g, color1.b, &l1, &a1, &b1);
768 34 : al_color_rgb_to_lab(color2.r, color2.g, color2.b, &l2, &a2, &b2);
769 34 : double pi = ALLEGRO_PI;
770 34 : double dl = l1 - l2;
771 34 : double ml = (l1 + l2) / 2;
772 34 : double c1 = sqrt(a1 * a1 + b1 * b1);
773 34 : double c2 = sqrt(a2 * a2 + b2 * b2);
774 34 : double mc = (c1 + c2) / 2;
775 34 : double fac = sqrt(pow(mc, 7) / (pow(mc, 7) + tf7));
776 34 : double g = 0.5 * (1 - fac);
777 34 : a1 *= 1 + g;
778 34 : a2 *= 1 + g;
779 34 : c1 = sqrt(a1 * a1 + b1 * b1);
780 34 : c2 = sqrt(a2 * a2 + b2 * b2);
781 34 : double dc = c2 - c1;
782 34 : mc = (c1 + c2) / 2;
783 34 : fac = sqrt(pow(mc, 7) / (pow(mc, 7) + tf7));
784 34 : double h1 = fmod(2 * pi + atan2(b1, a1), 2 * pi);
785 34 : double h2 = fmod(2 * pi + atan2(b2, a2), 2 * pi);
786 34 : double dh = 0;
787 34 : double mh = h1 + h2;
788 34 : if (c1 * c2 != 0) {
789 34 : dh = h2 - h1;
790 34 : if (dh > pi) dh -= 2 * pi;
791 34 : if (dh < -pi) dh += 2 * pi;
792 34 : if (fabs(h1 - h2) <= pi) mh = (h1 + h2) / 2;
793 7 : else if (h1 + h2 < 2 * pi) mh = (h1 + h2 + 2 * pi) / 2;
794 4 : else mh = (h1 + h2 - 2 * pi) / 2;
795 : }
796 34 : dh = 2 * sqrt(c1 * c2) * sin(dh / 2);
797 102 : double t = 1 - 0.17 * cos(mh - pi / 6) + 0.24 * cos(2 * mh) +
798 68 : 0.32 * cos(3 * mh + pi / 30) - 0.2 * cos(4 * mh - pi * 7 / 20);
799 34 : double mls = pow(ml - 0.5, 2);
800 34 : double sl = 1 + 1.5 * mls / sqrt(0.002 + mls);
801 34 : double sc = 1 + 4.5 * mc;
802 34 : double sh = 1 + 1.5 * mc * t;
803 68 : double rt = -2 * fac * sin(pi / 3 *
804 34 : exp(-pow(mh / pi * 36 / 5 - 11, 2)));
805 102 : return sqrt(pow(dl / sl, 2) + pow(dc / sc, 2) +
806 68 : pow(dh / sh, 2) + rt * dc / sc * dh / sh);
807 : }
808 :
809 : /* Function al_color_is_valid
810 : */
811 0 : bool al_is_color_valid(ALLEGRO_COLOR color)
812 : {
813 0 : return color.r >= 0 && color.g >= 0 && color.b >= 0 &&
814 0 : color.r <= 1 && color.g <= 1 && color.b <= 1;
815 : }
816 :
817 : /* vim: set sts=3 sw=3 et: */
|