|
Graphviz
2.29.20120523.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 #include <stdio.h> 00015 00016 00017 #include <stdlib.h> 00018 #ifdef WIN32 00019 #include <string.h> 00020 #include <ctype.h> 00021 #include "compat.h" 00022 #endif 00023 #include <string.h> 00024 #include <ctype.h> 00025 00026 #include "arith.h" 00027 #include "color.h" 00028 #include "colorprocs.h" 00029 #include "colortbl.h" 00030 #include "memory.h" 00031 00032 static char* colorscheme; 00033 00034 #ifdef WIN32 00035 extern int strcasecmp(const char *s1, const char *s2); 00036 extern int strncasecmp(const char *s1, const char *s2, unsigned int n); 00037 #endif 00038 00039 00040 static void hsv2rgb(double h, double s, double v, 00041 double *r, double *g, double *b) 00042 { 00043 int i; 00044 double f, p, q, t; 00045 00046 if (s <= 0.0) { /* achromatic */ 00047 *r = v; 00048 *g = v; 00049 *b = v; 00050 } else { 00051 if (h >= 1.0) 00052 h = 0.0; 00053 h = 6.0 * h; 00054 i = (int) h; 00055 f = h - (double) i; 00056 p = v * (1 - s); 00057 q = v * (1 - (s * f)); 00058 t = v * (1 - (s * (1 - f))); 00059 switch (i) { 00060 case 0: 00061 *r = v; 00062 *g = t; 00063 *b = p; 00064 break; 00065 case 1: 00066 *r = q; 00067 *g = v; 00068 *b = p; 00069 break; 00070 case 2: 00071 *r = p; 00072 *g = v; 00073 *b = t; 00074 break; 00075 case 3: 00076 *r = p; 00077 *g = q; 00078 *b = v; 00079 break; 00080 case 4: 00081 *r = t; 00082 *g = p; 00083 *b = v; 00084 break; 00085 case 5: 00086 *r = v; 00087 *g = p; 00088 *b = q; 00089 break; 00090 } 00091 } 00092 } 00093 00094 static void rgb2hsv(double r, double g, double b, 00095 double *h, double *s, double *v) 00096 { 00097 00098 double rgbmin, rgbmax; 00099 double rc, bc, gc; 00100 double ht = 0.0, st = 0.0; 00101 00102 rgbmin = MIN(r, MIN(g, b)); 00103 rgbmax = MAX(r, MAX(g, b)); 00104 00105 if (rgbmax > 0.0) 00106 st = (rgbmax - rgbmin) / rgbmax; 00107 00108 if (st > 0.0) { 00109 rc = (rgbmax - r) / (rgbmax - rgbmin); 00110 gc = (rgbmax - g) / (rgbmax - rgbmin); 00111 bc = (rgbmax - b) / (rgbmax - rgbmin); 00112 if (r == rgbmax) 00113 ht = bc - gc; 00114 else if (g == rgbmax) 00115 ht = 2 + rc - bc; 00116 else if (b == rgbmax) 00117 ht = 4 + gc - rc; 00118 ht = ht * 60.0; 00119 if (ht < 0.0) 00120 ht += 360.0; 00121 } 00122 *h = ht / 360.0; 00123 *v = rgbmax; 00124 *s = st; 00125 } 00126 00127 static void rgb2cmyk(double r, double g, double b, double *c, double *m, 00128 double *y, double *k) 00129 { 00130 *c = 1.0 - r; 00131 *m = 1.0 - g; 00132 *y = 1.0 - b; 00133 *k = *c < *m ? *c : *m; 00134 *k = *y < *k ? *y : *k; 00135 *c -= *k; 00136 *m -= *k; 00137 *y -= *k; 00138 } 00139 00140 static int colorcmpf(const void *p0, const void *p1) 00141 { 00142 return strcasecmp(((hsvrgbacolor_t *) p0)->name, ((hsvrgbacolor_t *) p1)->name); 00143 } 00144 00145 char *canontoken(char *str) 00146 { 00147 static unsigned char *canon; 00148 static int allocated; 00149 unsigned char c, *p, *q; 00150 int len; 00151 00152 p = (unsigned char *) str; 00153 len = strlen(str); 00154 if (len >= allocated) { 00155 allocated = len + 1 + 10; 00156 canon = grealloc(canon, allocated); 00157 if (!canon) 00158 return NULL; 00159 } 00160 q = (unsigned char *) canon; 00161 while ((c = *p++)) { 00162 /* if (isalnum(c) == FALSE) */ 00163 /* continue; */ 00164 if (isupper(c)) 00165 c = tolower(c); 00166 *q++ = c; 00167 } 00168 *q = '\0'; 00169 return (char*)canon; 00170 } 00171 00172 /* fullColor: 00173 * Return "/prefix/str" 00174 */ 00175 static char* fullColor (char* prefix, char* str) 00176 { 00177 static char *fulls; 00178 static int allocated; 00179 int len = strlen (prefix) + strlen (str) + 3; 00180 00181 if (len >= allocated) { 00182 allocated = len + 10; 00183 fulls = grealloc(fulls, allocated); 00184 } 00185 sprintf (fulls, "/%s/%s", prefix, str); 00186 return fulls; 00187 } 00188 00189 /* resolveColor: 00190 * Resolve input color str allowing color scheme namespaces. 00191 * 0) "black" => "black" 00192 * "white" => "white" 00193 * "lightgrey" => "lightgrey" 00194 * NB: This is something of a hack due to the remaining codegen. 00195 * Once these are gone, this case could be removed and all references 00196 * to "black" could be replaced by "/X11/black". 00197 * 1) No initial / => 00198 * if colorscheme is defined and no "X11", return /colorscheme/str 00199 * else return str 00200 * 2) One initial / => return str+1 00201 * 3) Two initial /'s => 00202 * a) If colorscheme is defined and not "X11", return /colorscheme/(str+2) 00203 * b) else return (str+2) 00204 * 4) Two /'s, not both initial => return str. 00205 * 00206 * Note that 1), 2), and 3b) allow the default X11 color scheme. 00207 * 00208 * In other words, 00209 * xxx => /colorscheme/xxx if colorscheme is defined and not "X11" 00210 * xxx => xxx otherwise 00211 * /xxx => xxx 00212 * /X11/yyy => yyy 00213 * /xxx/yyy => /xxx/yyy 00214 * //yyy => /colorscheme/yyy if colorscheme is defined and not "X11" 00215 * //yyy => yyy otherwise 00216 * 00217 * At present, no other error checking is done. For example, 00218 * yyy could be "". This will be caught later. 00219 */ 00220 00221 #define DFLT_SCHEME "X11/" /* Must have final '/' */ 00222 #define DFLT_SCHEME_LEN ((sizeof(DFLT_SCHEME)-1)/sizeof(char)) 00223 #define ISNONDFLT(s) ((s) && *(s) && strncasecmp(DFLT_SCHEME, s, DFLT_SCHEME_LEN-1)) 00224 00225 static char* resolveColor (char* str) 00226 { 00227 char* s; 00228 char* ss; /* second slash */ 00229 char* c2; /* second char */ 00230 00231 if ((*str == 'b') || !strncmp(str+1,"lack",4)) return str; 00232 if ((*str == 'w') || !strncmp(str+1,"hite",4)) return str; 00233 if ((*str == 'l') || !strncmp(str+1,"ightgrey",8)) return str; 00234 if (*str == '/') { /* if begins with '/' */ 00235 c2 = str+1; 00236 if ((ss = strchr(c2, '/'))) { /* if has second '/' */ 00237 if (*c2 == '/') { /* if second '/' is second character */ 00238 /* Do not compare against final '/' */ 00239 if (ISNONDFLT(colorscheme)) 00240 s = fullColor (colorscheme, c2+1); 00241 else 00242 s = c2+1; 00243 } 00244 else if (strncasecmp(DFLT_SCHEME, c2, DFLT_SCHEME_LEN)) s = str; 00245 else s = ss + 1; 00246 } 00247 else s = c2; 00248 } 00249 else if (ISNONDFLT(colorscheme)) s = fullColor (colorscheme, str); 00250 else s = str; 00251 return canontoken(s); 00252 } 00253 00254 int colorxlate(char *str, gvcolor_t * color, color_type_t target_type) 00255 { 00256 static hsvrgbacolor_t *last; 00257 static unsigned char *canon; 00258 static int allocated; 00259 unsigned char *p, *q; 00260 hsvrgbacolor_t fake; 00261 unsigned char c; 00262 double H, S, V, A, R, G, B; 00263 double C, M, Y, K; 00264 unsigned int r, g, b, a; 00265 int len, rc; 00266 00267 color->type = target_type; 00268 00269 rc = COLOR_OK; 00270 for (; *str == ' '; str++); /* skip over any leading whitespace */ 00271 p = (unsigned char *) str; 00272 00273 /* test for rgb value such as: "#ff0000" 00274 or rgba value such as "#ff000080" */ 00275 a = 255; /* default alpha channel value=opaque in case not supplied */ 00276 if ((*p == '#') 00277 && (sscanf((char *) p, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3)) { 00278 switch (target_type) { 00279 case HSVA_DOUBLE: 00280 R = (double) r / 255.0; 00281 G = (double) g / 255.0; 00282 B = (double) b / 255.0; 00283 A = (double) a / 255.0; 00284 rgb2hsv(R, G, B, &H, &S, &V); 00285 color->u.HSVA[0] = H; 00286 color->u.HSVA[1] = S; 00287 color->u.HSVA[2] = V; 00288 color->u.HSVA[3] = A; 00289 break; 00290 case RGBA_BYTE: 00291 color->u.rgba[0] = r; 00292 color->u.rgba[1] = g; 00293 color->u.rgba[2] = b; 00294 color->u.rgba[3] = a; 00295 break; 00296 case CMYK_BYTE: 00297 R = (double) r / 255.0; 00298 G = (double) g / 255.0; 00299 B = (double) b / 255.0; 00300 rgb2cmyk(R, G, B, &C, &M, &Y, &K); 00301 color->u.cmyk[0] = (int) C *255; 00302 color->u.cmyk[1] = (int) M *255; 00303 color->u.cmyk[2] = (int) Y *255; 00304 color->u.cmyk[3] = (int) K *255; 00305 break; 00306 case RGBA_WORD: 00307 color->u.rrggbbaa[0] = r * 65535 / 255; 00308 color->u.rrggbbaa[1] = g * 65535 / 255; 00309 color->u.rrggbbaa[2] = b * 65535 / 255; 00310 color->u.rrggbbaa[3] = a * 65535 / 255; 00311 break; 00312 case RGBA_DOUBLE: 00313 color->u.RGBA[0] = (double) r / 255.0; 00314 color->u.RGBA[1] = (double) g / 255.0; 00315 color->u.RGBA[2] = (double) b / 255.0; 00316 color->u.RGBA[3] = (double) a / 255.0; 00317 break; 00318 case COLOR_STRING: 00319 break; 00320 case COLOR_INDEX: 00321 break; 00322 } 00323 return rc; 00324 } 00325 00326 /* test for hsv value such as: ".6,.5,.3" */ 00327 if (((c = *p) == '.') || isdigit(c)) { 00328 len = strlen((char*)p); 00329 if (len >= allocated) { 00330 allocated = len + 1 + 10; 00331 canon = grealloc(canon, allocated); 00332 if (! canon) { 00333 rc = COLOR_MALLOC_FAIL; 00334 return rc; 00335 } 00336 } 00337 q = canon; 00338 while ((c = *p++)) { 00339 if (c == ',') 00340 c = ' '; 00341 *q++ = c; 00342 } 00343 *q = '\0'; 00344 00345 if (sscanf((char *) canon, "%lf%lf%lf", &H, &S, &V) == 3) { 00346 /* clip to reasonable values */ 00347 H = MAX(MIN(H, 1.0), 0.0); 00348 S = MAX(MIN(S, 1.0), 0.0); 00349 V = MAX(MIN(V, 1.0), 0.0); 00350 switch (target_type) { 00351 case HSVA_DOUBLE: 00352 color->u.HSVA[0] = H; 00353 color->u.HSVA[1] = S; 00354 color->u.HSVA[2] = V; 00355 color->u.HSVA[3] = 1.0; /* opaque */ 00356 break; 00357 case RGBA_BYTE: 00358 hsv2rgb(H, S, V, &R, &G, &B); 00359 color->u.rgba[0] = (int) (R * 255); 00360 color->u.rgba[1] = (int) (G * 255); 00361 color->u.rgba[2] = (int) (B * 255); 00362 color->u.rgba[3] = 255; /* opaque */ 00363 break; 00364 case CMYK_BYTE: 00365 hsv2rgb(H, S, V, &R, &G, &B); 00366 rgb2cmyk(R, G, B, &C, &M, &Y, &K); 00367 color->u.cmyk[0] = (int) C *255; 00368 color->u.cmyk[1] = (int) M *255; 00369 color->u.cmyk[2] = (int) Y *255; 00370 color->u.cmyk[3] = (int) K *255; 00371 break; 00372 case RGBA_WORD: 00373 hsv2rgb(H, S, V, &R, &G, &B); 00374 color->u.rrggbbaa[0] = (int) (R * 65535); 00375 color->u.rrggbbaa[1] = (int) (G * 65535); 00376 color->u.rrggbbaa[2] = (int) (B * 65535); 00377 color->u.rrggbbaa[3] = 65535; /* opaque */ 00378 break; 00379 case RGBA_DOUBLE: 00380 hsv2rgb(H, S, V, &R, &G, &B); 00381 color->u.RGBA[0] = R; 00382 color->u.RGBA[1] = G; 00383 color->u.RGBA[2] = B; 00384 color->u.RGBA[3] = 1.0; /* opaque */ 00385 break; 00386 case COLOR_STRING: 00387 break; 00388 case COLOR_INDEX: 00389 break; 00390 } 00391 return rc; 00392 } 00393 } 00394 00395 /* test for known color name (generic, not renderer specific known names) */ 00396 fake.name = resolveColor(str); 00397 if (!fake.name) 00398 return COLOR_MALLOC_FAIL; 00399 if ((last == NULL) 00400 || (last->name[0] != fake.name[0]) 00401 || (strcmp(last->name, fake.name))) { 00402 last = (hsvrgbacolor_t *) bsearch((void *) &fake, 00403 (void *) color_lib, 00404 sizeof(color_lib) / 00405 sizeof(hsvrgbacolor_t), sizeof(fake), 00406 colorcmpf); 00407 } 00408 if (last != NULL) { 00409 switch (target_type) { 00410 case HSVA_DOUBLE: 00411 color->u.HSVA[0] = ((double) last->h) / 255.0; 00412 color->u.HSVA[1] = ((double) last->s) / 255.0; 00413 color->u.HSVA[2] = ((double) last->v) / 255.0; 00414 color->u.HSVA[3] = ((double) last->a) / 255.0; 00415 break; 00416 case RGBA_BYTE: 00417 color->u.rgba[0] = last->r; 00418 color->u.rgba[1] = last->g; 00419 color->u.rgba[2] = last->b; 00420 color->u.rgba[3] = last->a; 00421 break; 00422 case CMYK_BYTE: 00423 R = (last->r) / 255.0; 00424 G = (last->g) / 255.0; 00425 B = (last->b) / 255.0; 00426 rgb2cmyk(R, G, B, &C, &M, &Y, &K); 00427 color->u.cmyk[0] = (int) C * 255; 00428 color->u.cmyk[1] = (int) M * 255; 00429 color->u.cmyk[2] = (int) Y * 255; 00430 color->u.cmyk[3] = (int) K * 255; 00431 break; 00432 case RGBA_WORD: 00433 color->u.rrggbbaa[0] = last->r * 65535 / 255; 00434 color->u.rrggbbaa[1] = last->g * 65535 / 255; 00435 color->u.rrggbbaa[2] = last->b * 65535 / 255; 00436 color->u.rrggbbaa[3] = last->a * 65535 / 255; 00437 break; 00438 case RGBA_DOUBLE: 00439 color->u.RGBA[0] = last->r / 255.0; 00440 color->u.RGBA[1] = last->g / 255.0; 00441 color->u.RGBA[2] = last->b / 255.0; 00442 color->u.RGBA[3] = last->a / 255.0; 00443 break; 00444 case COLOR_STRING: 00445 break; 00446 case COLOR_INDEX: 00447 break; 00448 } 00449 return rc; 00450 } 00451 00452 /* if we're still here then we failed to find a valid color spec */ 00453 rc = COLOR_UNKNOWN; 00454 switch (target_type) { 00455 case HSVA_DOUBLE: 00456 color->u.HSVA[0] = color->u.HSVA[1] = color->u.HSVA[2] = 0.0; 00457 color->u.HSVA[3] = 1.0; /* opaque */ 00458 break; 00459 case RGBA_BYTE: 00460 color->u.rgba[0] = color->u.rgba[1] = color->u.rgba[2] = 0; 00461 color->u.rgba[3] = 255; /* opaque */ 00462 break; 00463 case CMYK_BYTE: 00464 color->u.cmyk[0] = 00465 color->u.cmyk[1] = color->u.cmyk[2] = color->u.cmyk[3] = 0; 00466 break; 00467 case RGBA_WORD: 00468 color->u.rrggbbaa[0] = color->u.rrggbbaa[1] = color->u.rrggbbaa[2] = 0; 00469 color->u.rrggbbaa[3] = 65535; /* opaque */ 00470 break; 00471 case RGBA_DOUBLE: 00472 color->u.RGBA[0] = color->u.RGBA[1] = color->u.RGBA[2] = 0.0; 00473 color->u.RGBA[3] = 1.0; /* opaque */ 00474 break; 00475 case COLOR_STRING: 00476 break; 00477 case COLOR_INDEX: 00478 break; 00479 } 00480 return rc; 00481 } 00482 00483 /* setColorScheme: 00484 * Set current color scheme for resolving names. 00485 */ 00486 void setColorScheme (char* s) 00487 { 00488 colorscheme = s; 00489 } 00490 00491 00492
1.7.5