Graphviz  2.29.20120523.0446
lib/common/colxlate.c
Go to the documentation of this file.
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