1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include <stdio.h>
15
16
17#include <stdlib.h>
18#ifdef _WIN32
19#include <string.h>
20#include <ctype.h>
21#include "compat.h"
22#endif
23#include <string.h>
24#include <ctype.h>
25
26#include "arith.h"
27#include "color.h"
28#include "colorprocs.h"
29#include "colortbl.h"
30#include "memory.h"
31
32static char* colorscheme;
33
34#ifdef _MSC_VER
35extern int strcasecmp(const char *s1, const char *s2);
36extern int strncasecmp(const char *s1, const char *s2, unsigned int n);
37#endif
38
39
40static void hsv2rgb(double h, double s, double v,
41 double *r, double *g, double *b)
42{
43 int i;
44 double f, p, q, t;
45
46 if (s <= 0.0) { /* achromatic */
47 *r = v;
48 *g = v;
49 *b = v;
50 } else {
51 if (h >= 1.0)
52 h = 0.0;
53 h = 6.0 * h;
54 i = (int) h;
55 f = h - (double) i;
56 p = v * (1 - s);
57 q = v * (1 - (s * f));
58 t = v * (1 - (s * (1 - f)));
59 switch (i) {
60 case 0:
61 *r = v;
62 *g = t;
63 *b = p;
64 break;
65 case 1:
66 *r = q;
67 *g = v;
68 *b = p;
69 break;
70 case 2:
71 *r = p;
72 *g = v;
73 *b = t;
74 break;
75 case 3:
76 *r = p;
77 *g = q;
78 *b = v;
79 break;
80 case 4:
81 *r = t;
82 *g = p;
83 *b = v;
84 break;
85 case 5:
86 *r = v;
87 *g = p;
88 *b = q;
89 break;
90 }
91 }
92}
93
94static void rgb2hsv(double r, double g, double b,
95 double *h, double *s, double *v)
96{
97
98 double rgbmin, rgbmax;
99 double rc, bc, gc;
100 double ht = 0.0, st = 0.0;
101
102 rgbmin = MIN(r, MIN(g, b));
103 rgbmax = MAX(r, MAX(g, b));
104
105 if (rgbmax > 0.0)
106 st = (rgbmax - rgbmin) / rgbmax;
107
108 if (st > 0.0) {
109 rc = (rgbmax - r) / (rgbmax - rgbmin);
110 gc = (rgbmax - g) / (rgbmax - rgbmin);
111 bc = (rgbmax - b) / (rgbmax - rgbmin);
112 if (r == rgbmax)
113 ht = bc - gc;
114 else if (g == rgbmax)
115 ht = 2 + rc - bc;
116 else if (b == rgbmax)
117 ht = 4 + gc - rc;
118 ht = ht * 60.0;
119 if (ht < 0.0)
120 ht += 360.0;
121 }
122 *h = ht / 360.0;
123 *v = rgbmax;
124 *s = st;
125}
126
127static void rgb2cmyk(double r, double g, double b, double *c, double *m,
128 double *y, double *k)
129{
130 *c = 1.0 - r;
131 *m = 1.0 - g;
132 *y = 1.0 - b;
133 *k = *c < *m ? *c : *m;
134 *k = *y < *k ? *y : *k;
135 *c -= *k;
136 *m -= *k;
137 *y -= *k;
138}
139
140static int colorcmpf(const void *p0, const void *p1)
141{
142 return strcasecmp(((hsvrgbacolor_t *) p0)->name, ((hsvrgbacolor_t *) p1)->name);
143}
144
145char *canontoken(char *str)
146{
147 static unsigned char *canon;
148 static size_t allocated;
149 unsigned char c, *p, *q;
150 size_t len;
151
152 p = (unsigned char *) str;
153 len = strlen(str);
154 if (len >= allocated) {
155 allocated = len + 1 + 10;
156 canon = grealloc(canon, allocated);
157 if (!canon)
158 return NULL;
159 }
160 q = (unsigned char *) canon;
161 while ((c = *p++)) {
162 /* if (isalnum(c) == FALSE) */
163 /* continue; */
164 if (isupper(c))
165 c = (unsigned char) tolower(c);
166 *q++ = c;
167 }
168 *q = '\0';
169 return (char*)canon;
170}
171
172/* fullColor:
173 * Return "/prefix/str"
174 */
175static char* fullColor (char* prefix, char* str)
176{
177 static char *fulls;
178 static size_t allocated;
179 size_t len = strlen(prefix) + strlen(str) + 3;
180
181 if (len >= allocated) {
182 allocated = len + 10;
183 fulls = grealloc(fulls, allocated);
184 }
185 sprintf (fulls, "/%s/%s", prefix, str);
186 return fulls;
187}
188
189/* resolveColor:
190 * Resolve input color str allowing color scheme namespaces.
191 * 0) "black" => "black"
192 * "white" => "white"
193 * "lightgrey" => "lightgrey"
194 * NB: This is something of a hack due to the remaining codegen.
195 * Once these are gone, this case could be removed and all references
196 * to "black" could be replaced by "/X11/black".
197 * 1) No initial / =>
198 * if colorscheme is defined and no "X11", return /colorscheme/str
199 * else return str
200 * 2) One initial / => return str+1
201 * 3) Two initial /'s =>
202 * a) If colorscheme is defined and not "X11", return /colorscheme/(str+2)
203 * b) else return (str+2)
204 * 4) Two /'s, not both initial => return str.
205 *
206 * Note that 1), 2), and 3b) allow the default X11 color scheme.
207 *
208 * In other words,
209 * xxx => /colorscheme/xxx if colorscheme is defined and not "X11"
210 * xxx => xxx otherwise
211 * /xxx => xxx
212 * /X11/yyy => yyy
213 * /xxx/yyy => /xxx/yyy
214 * //yyy => /colorscheme/yyy if colorscheme is defined and not "X11"
215 * //yyy => yyy otherwise
216 *
217 * At present, no other error checking is done. For example,
218 * yyy could be "". This will be caught later.
219 */
220
221#define DFLT_SCHEME "X11/" /* Must have final '/' */
222#define DFLT_SCHEME_LEN ((sizeof(DFLT_SCHEME)-1)/sizeof(char))
223#define ISNONDFLT(s) ((s) && *(s) && strncasecmp(DFLT_SCHEME, s, DFLT_SCHEME_LEN-1))
224
225static char* resolveColor (char* str)
226{
227 char* s;
228 char* ss; /* second slash */
229 char* c2; /* second char */
230
231 if (!strcmp(str, "black")) return str;
232 if (!strcmp(str, "white")) return str;
233 if (!strcmp(str, "lightgrey")) return str;
234 if (*str == '/') { /* if begins with '/' */
235 c2 = str+1;
236 if ((ss = strchr(c2, '/'))) { /* if has second '/' */
237 if (*c2 == '/') { /* if second '/' is second character */
238 /* Do not compare against final '/' */
239 if (ISNONDFLT(colorscheme))
240 s = fullColor (colorscheme, c2+1);
241 else
242 s = c2+1;
243 }
244 else if (strncasecmp(DFLT_SCHEME, c2, DFLT_SCHEME_LEN)) s = str;
245 else s = ss + 1;
246 }
247 else s = c2;
248 }
249 else if (ISNONDFLT(colorscheme)) s = fullColor (colorscheme, str);
250 else s = str;
251 return canontoken(s);
252}
253
254int colorxlate(char *str, gvcolor_t * color, color_type_t target_type)
255{
256 static hsvrgbacolor_t *last;
257 static unsigned char *canon;
258 static size_t allocated;
259 unsigned char *p, *q;
260 hsvrgbacolor_t fake;
261 unsigned char c;
262 double H, S, V, A, R, G, B;
263 double C, M, Y, K;
264 unsigned int r, g, b, a;
265 size_t len;
266 int rc;
267
268 color->type = target_type;
269
270 rc = COLOR_OK;
271 for (; *str == ' '; str++); /* skip over any leading whitespace */
272 p = (unsigned char *) str;
273
274 /* test for rgb value such as: "#ff0000"
275 or rgba value such as "#ff000080" */
276 a = 255; /* default alpha channel value=opaque in case not supplied */
277 if ((*p == '#')
278 && (sscanf((char *) p, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3)) {
279 switch (target_type) {
280 case HSVA_DOUBLE:
281 R = (double) r / 255.0;
282 G = (double) g / 255.0;
283 B = (double) b / 255.0;
284 A = (double) a / 255.0;
285 rgb2hsv(R, G, B, &H, &S, &V);
286 color->u.HSVA[0] = H;
287 color->u.HSVA[1] = S;
288 color->u.HSVA[2] = V;
289 color->u.HSVA[3] = A;
290 break;
291 case RGBA_BYTE:
292 color->u.rgba[0] = r;
293 color->u.rgba[1] = g;
294 color->u.rgba[2] = b;
295 color->u.rgba[3] = a;
296 break;
297 case CMYK_BYTE:
298 R = (double) r / 255.0;
299 G = (double) g / 255.0;
300 B = (double) b / 255.0;
301 rgb2cmyk(R, G, B, &C, &M, &Y, &K);
302 color->u.cmyk[0] = (int) C *255;
303 color->u.cmyk[1] = (int) M *255;
304 color->u.cmyk[2] = (int) Y *255;
305 color->u.cmyk[3] = (int) K *255;
306 break;
307 case RGBA_WORD:
308 color->u.rrggbbaa[0] = r * 65535 / 255;
309 color->u.rrggbbaa[1] = g * 65535 / 255;
310 color->u.rrggbbaa[2] = b * 65535 / 255;
311 color->u.rrggbbaa[3] = a * 65535 / 255;
312 break;
313 case RGBA_DOUBLE:
314 color->u.RGBA[0] = (double) r / 255.0;
315 color->u.RGBA[1] = (double) g / 255.0;
316 color->u.RGBA[2] = (double) b / 255.0;
317 color->u.RGBA[3] = (double) a / 255.0;
318 break;
319 case COLOR_STRING:
320 break;
321 case COLOR_INDEX:
322 break;
323 }
324 return rc;
325 }
326
327 /* test for hsv value such as: ".6,.5,.3" */
328 if (((c = *p) == '.') || isdigit(c)) {
329 len = strlen((char*)p);
330 if (len >= allocated) {
331 allocated = len + 1 + 10;
332 canon = grealloc(canon, allocated);
333 if (! canon) {
334 rc = COLOR_MALLOC_FAIL;
335 return rc;
336 }
337 }
338 q = canon;
339 while ((c = *p++)) {
340 if (c == ',')
341 c = ' ';
342 *q++ = c;
343 }
344 *q = '\0';
345
346 if (sscanf((char *) canon, "%lf%lf%lf", &H, &S, &V) == 3) {
347 /* clip to reasonable values */
348 H = MAX(MIN(H, 1.0), 0.0);
349 S = MAX(MIN(S, 1.0), 0.0);
350 V = MAX(MIN(V, 1.0), 0.0);
351 switch (target_type) {
352 case HSVA_DOUBLE:
353 color->u.HSVA[0] = H;
354 color->u.HSVA[1] = S;
355 color->u.HSVA[2] = V;
356 color->u.HSVA[3] = 1.0; /* opaque */
357 break;
358 case RGBA_BYTE:
359 hsv2rgb(H, S, V, &R, &G, &B);
360 color->u.rgba[0] = (int) (R * 255);
361 color->u.rgba[1] = (int) (G * 255);
362 color->u.rgba[2] = (int) (B * 255);
363 color->u.rgba[3] = 255; /* opaque */
364 break;
365 case CMYK_BYTE:
366 hsv2rgb(H, S, V, &R, &G, &B);
367 rgb2cmyk(R, G, B, &C, &M, &Y, &K);
368 color->u.cmyk[0] = (int) C *255;
369 color->u.cmyk[1] = (int) M *255;
370 color->u.cmyk[2] = (int) Y *255;
371 color->u.cmyk[3] = (int) K *255;
372 break;
373 case RGBA_WORD:
374 hsv2rgb(H, S, V, &R, &G, &B);
375 color->u.rrggbbaa[0] = (int) (R * 65535);
376 color->u.rrggbbaa[1] = (int) (G * 65535);
377 color->u.rrggbbaa[2] = (int) (B * 65535);
378 color->u.rrggbbaa[3] = 65535; /* opaque */
379 break;
380 case RGBA_DOUBLE:
381 hsv2rgb(H, S, V, &R, &G, &B);
382 color->u.RGBA[0] = R;
383 color->u.RGBA[1] = G;
384 color->u.RGBA[2] = B;
385 color->u.RGBA[3] = 1.0; /* opaque */
386 break;
387 case COLOR_STRING:
388 break;
389 case COLOR_INDEX:
390 break;
391 }
392 return rc;
393 }
394 }
395
396 /* test for known color name (generic, not renderer specific known names) */
397 fake.name = resolveColor(str);
398 if (!fake.name)
399 return COLOR_MALLOC_FAIL;
400 if ((last == NULL)
401 || (last->name[0] != fake.name[0])
402 || (strcmp(last->name, fake.name))) {
403 last = (hsvrgbacolor_t *) bsearch((void *) &fake,
404 (void *) color_lib,
405 sizeof(color_lib) /
406 sizeof(hsvrgbacolor_t), sizeof(fake),
407 colorcmpf);
408 }
409 if (last != NULL) {
410 switch (target_type) {
411 case HSVA_DOUBLE:
412 color->u.HSVA[0] = ((double) last->h) / 255.0;
413 color->u.HSVA[1] = ((double) last->s) / 255.0;
414 color->u.HSVA[2] = ((double) last->v) / 255.0;
415 color->u.HSVA[3] = ((double) last->a) / 255.0;
416 break;
417 case RGBA_BYTE:
418 color->u.rgba[0] = last->r;
419 color->u.rgba[1] = last->g;
420 color->u.rgba[2] = last->b;
421 color->u.rgba[3] = last->a;
422 break;
423 case CMYK_BYTE:
424 R = (last->r) / 255.0;
425 G = (last->g) / 255.0;
426 B = (last->b) / 255.0;
427 rgb2cmyk(R, G, B, &C, &M, &Y, &K);
428 color->u.cmyk[0] = (int) C * 255;
429 color->u.cmyk[1] = (int) M * 255;
430 color->u.cmyk[2] = (int) Y * 255;
431 color->u.cmyk[3] = (int) K * 255;
432 break;
433 case RGBA_WORD:
434 color->u.rrggbbaa[0] = last->r * 65535 / 255;
435 color->u.rrggbbaa[1] = last->g * 65535 / 255;
436 color->u.rrggbbaa[2] = last->b * 65535 / 255;
437 color->u.rrggbbaa[3] = last->a * 65535 / 255;
438 break;
439 case RGBA_DOUBLE:
440 color->u.RGBA[0] = last->r / 255.0;
441 color->u.RGBA[1] = last->g / 255.0;
442 color->u.RGBA[2] = last->b / 255.0;
443 color->u.RGBA[3] = last->a / 255.0;
444 break;
445 case COLOR_STRING:
446 break;
447 case COLOR_INDEX:
448 break;
449 }
450 return rc;
451 }
452
453 /* if we're still here then we failed to find a valid color spec */
454 rc = COLOR_UNKNOWN;
455 switch (target_type) {
456 case HSVA_DOUBLE:
457 color->u.HSVA[0] = color->u.HSVA[1] = color->u.HSVA[2] = 0.0;
458 color->u.HSVA[3] = 1.0; /* opaque */
459 break;
460 case RGBA_BYTE:
461 color->u.rgba[0] = color->u.rgba[1] = color->u.rgba[2] = 0;
462 color->u.rgba[3] = 255; /* opaque */
463 break;
464 case CMYK_BYTE:
465 color->u.cmyk[0] =
466 color->u.cmyk[1] = color->u.cmyk[2] = color->u.cmyk[3] = 0;
467 break;
468 case RGBA_WORD:
469 color->u.rrggbbaa[0] = color->u.rrggbbaa[1] = color->u.rrggbbaa[2] = 0;
470 color->u.rrggbbaa[3] = 65535; /* opaque */
471 break;
472 case RGBA_DOUBLE:
473 color->u.RGBA[0] = color->u.RGBA[1] = color->u.RGBA[2] = 0.0;
474 color->u.RGBA[3] = 1.0; /* opaque */
475 break;
476 case COLOR_STRING:
477 break;
478 case COLOR_INDEX:
479 break;
480 }
481 return rc;
482}
483
484static void rgba_wordToByte (int* rrggbbaa, unsigned char* rgba)
485{
486 int i;
487
488 for (i = 0; i < 4; i++) {
489 rgba[i] = rrggbbaa[i] * 255 / 65535;
490 }
491}
492
493static void rgba_dblToByte (double* RGBA, unsigned char* rgba)
494{
495 int i;
496
497 for (i = 0; i < 4; i++) {
498 rgba[i] = (unsigned char)(RGBA[i] * 255);
499 }
500}
501
502/* colorCvt:
503 * Color format converter.
504 * Except for the trivial case, it converts the input color to a string
505 * representation and then calls colorxlate.
506 * ncolor must point to a gvcolor_t struct with type specifying the desired
507 * output type.
508 */
509int colorCvt(gvcolor_t *ocolor, gvcolor_t *ncolor)
510{
511 int rc;
512 char buf[BUFSIZ];
513 char* s;
514 unsigned char rgba[4];
515
516 if (ocolor->type == ncolor->type) {
517 memcpy (&ncolor->u, &ocolor->u, sizeof(ocolor->u));
518 return COLOR_OK;
519 }
520 s = buf;
521 switch (ocolor->type) {
522 case HSVA_DOUBLE :
523 sprintf (buf, "%.03f %.03f %.03f %.03f",
524 ocolor->u.HSVA[0], ocolor->u.HSVA[1], ocolor->u.HSVA[2], ocolor->u.HSVA[3]);
525 break;
526 case RGBA_BYTE :
527 sprintf (buf, "#%02x%02x%02x%02x",
528 ocolor->u.rgba[0], ocolor->u.rgba[1], ocolor->u.rgba[2], ocolor->u.rgba[3]);
529 break;
530 case RGBA_WORD:
531 rgba_wordToByte (ocolor->u.rrggbbaa, rgba);
532 sprintf (buf, "#%02x%02x%02x%02x", rgba[0], rgba[1], rgba[2], rgba[3]);
533 break;
534 case RGBA_DOUBLE:
535 rgba_dblToByte (ocolor->u.RGBA, rgba);
536 sprintf (buf, "#%02x%02x%02x%02x", rgba[0], rgba[1], rgba[2], rgba[3]);
537 break;
538 case COLOR_STRING:
539 s = ocolor->u.string;
540 break;
541 case CMYK_BYTE :
542 /* agerr (AGWARN, "Input color type 'CMYK_BYTE' not supported for conversion\n"); */
543 return COLOR_UNKNOWN;
544 break;
545 case COLOR_INDEX:
546 /* agerr (AGWARN, "Input color type 'COLOR_INDEX' not supported for conversion\n"); */
547 return COLOR_UNKNOWN;
548 break;
549 default:
550 /* agerr (AGWARN, "Unknown input color type value '%u'\n", ncolor->type); */
551 return COLOR_UNKNOWN;
552 break;
553 }
554 rc = colorxlate (s, ncolor, ncolor->type);
555 return rc;
556}
557
558/* setColorScheme:
559 * Set current color scheme for resolving names.
560 */
561void setColorScheme (char* s)
562{
563 colorscheme = s;
564}
565
566
567
568