libgeda

s_color.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * libgeda - gEDA's library
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 #include <config.h>
00021 #include <missing.h>
00022 
00023 #include <stdio.h>
00024 #include <math.h>
00025 #ifdef HAVE_STRING_H
00026 #include <string.h>
00027 #endif
00028 
00029 #include "libgeda_priv.h"
00030 
00031 #ifdef HAVE_LIBDMALLOC
00032 #include <dmalloc.h>
00033 #endif
00034 
00035 COLOR print_colors[MAX_COLORS];
00036 
00037 #define NOCOLOR {0xff, 0xff, 0xff, 0xff, FALSE}
00038 #define WHITE   {0xff, 0xff, 0xff, 0xff, TRUE}
00039 #define GRAY    {0x88, 0x88, 0x88, 0xff, TRUE}
00040 #define BLACK   {0x00, 0x00, 0x00, 0xff, TRUE}
00041 #define ENDMAP  {0x00, 0x00, 0x00, 0x00, FALSE}
00042 
00043 static COLOR default_colors[] = {
00044   WHITE,           /*  0: background         */
00045   BLACK,           /*  1: pin                */
00046   BLACK,           /*  2: net-endpoint       */
00047   BLACK,           /*  3: graphic            */
00048   BLACK,           /*  4: net                */
00049   BLACK,           /*  5: attribute          */
00050   BLACK,           /*  6: logic-bubble       */
00051   BLACK,           /*  7: dots-grid          */
00052   BLACK,           /*  8: detached-attribute */
00053   BLACK,           /*  9: text               */
00054   BLACK,           /* 10: bus                */
00055   GRAY,            /* 11: select             */
00056   GRAY,            /* 12: bounding-box       */
00057   GRAY,            /* 13: zoom-box           */
00058   GRAY,            /* 14: stroke             */
00059   BLACK,           /* 15: lock               */
00060   NOCOLOR,         /* 16: output-background  */
00061   NOCOLOR,         /* 17: freestyle1         */
00062   NOCOLOR,         /* 18: freestyle2         */
00063   NOCOLOR,         /* 19: freestyle3         */
00064   NOCOLOR,         /* 20: freestyle4         */
00065   BLACK,           /* 21: junction           */
00066   GRAY,            /* 22: mesh-grid-major    */
00067   NOCOLOR,         /* 23: mesh-grid-minor    */
00068   ENDMAP
00069 };
00070 
00075 void
00076 s_color_init(void)
00077 {
00078   s_color_map_defaults (print_colors);
00079 }
00080 
00090 void
00091 s_color_map_defaults (COLOR *map)
00092 {
00093   int i;
00094   gboolean reached_end = FALSE;
00095   COLOR c;
00096   for (i = 0; i < MAX_COLORS; i++) {
00097     if (reached_end) {
00098       map[i].enabled = FALSE;
00099       continue;
00100     }
00101     c = default_colors[i];
00102     if (c.a == 0) { /* Check for end of default map */
00103       reached_end = TRUE;
00104       i--;
00105       continue;
00106     }
00107     map[i] = c;
00108   }
00109 }
00110 
00111 /* \brief Decode a hexadecimal RGB or RGBA color code.
00112  * \par Function Description
00113  * Accepts a hexadecimal color code \a rgba of either the form #RRGGBB
00114  * or #RRGGBBAA, and parses it to extract the numerical color values,
00115  * placing them in the the #guchar pointers passed as arguments. If
00116  * the six-digit form is used, the alpha channel is set to full
00117  * opacity. If an error occurs during parsing, the return values are
00118  * set to solid white.
00119  *
00120  * Note that this function implements similar functionality to
00121  * gdk_color_parse(). However, for consistency, <em>only</em> this
00122  * function should be used to parse color strings from gEDA
00123  * configuration files, as gdk_color_parse() does not support the
00124  * alpha channel.
00125  *
00126  * \todo Use GError mechanism to give more specific error messages.
00127  *
00128  * \param [in]  rgba Colour code to parse.
00129  * \param [out] r    Location to store red value.
00130  * \param [out] g    Location to store green value.
00131  * \param [out] b    Location to store blue value.
00132  *
00133  *  \returns #TRUE on success, #FALSE on failure.
00134  */
00135 gboolean
00136 s_color_rgba_decode (const gchar *rgba,
00137                      guint8 *r, guint8 *g, guint8 *b, guint8 *a)
00138 {
00139   gint len, i, ri, gi, bi, ai;
00140   gchar c;
00141 
00142   /* Default to solid white */
00143   *r = 0xff; *g = 0xff; *b = 0xff; *a = 0xff;
00144 
00145   /* Check that the string is a valid length and starts with a '#' */
00146   len = strlen (rgba);
00147   if ((len != 9 && len != 7) || rgba[0] != '#')
00148     return FALSE;
00149 
00150   /* Check we only have [0-9a-fA-F] */
00151   for (i = 1; i < len; i++) {
00152     c = rgba[i];
00153     if ((c < '0' || c > '9')
00154         && (c < 'a' || c > 'f')
00155         && (c < 'A' || c > 'F'))
00156       return FALSE;
00157   }
00158 
00159   /* Use sscanf to extract values */
00160   c = sscanf (rgba + 1, "%2x%2x%2x", &ri, &gi, &bi);
00161   if (c != 3)
00162     return FALSE;
00163   *r = (guint8) ri; *g = (guint8) gi; *b = (guint8) bi;
00164 
00165   if (len == 9) {
00166     c = sscanf (rgba + 7, "%2x", &ai);
00167     if (c != 1)
00168       return FALSE;
00169     *a = (guint8) ai;
00170   }
00171 
00172   return TRUE;
00173 }
00174 
00175 /* \brief Encode a hexadecimal RGB or RGBA color code.
00176  * \par Function Description
00177  * Encodes four colour components into either the form #RRGGBB or
00178  * #RRGGBBAA. The shorter form is used when the alpha component is
00179  * 0xff.
00180  *
00181  * \param [in] r Red component.
00182  * \param [in] g Green component.
00183  * \param [in] b Blue component.
00184  * \returns A newly allocated string containing the encoded string.
00185  */
00186 gchar *
00187 s_color_rgba_encode (guint8 r, guint8 g, guint8 b, guint8 a)
00188 {
00189   if (a < 0xff)
00190     return g_strdup_printf("#%02x%02x%02x%02x",
00191                            (gint) r, (gint) g, (gint) b, (gint) a);
00192   else
00193     return g_strdup_printf("#%02x%02x%02x",
00194                            (gint) r, (gint) g, (gint) b);
00195 }
00196 
00202 gchar *s_color_ps_string(gint color)
00203 {
00204   COLOR c;
00205 
00206   if (color >= MAX_COLORS) {
00207     g_warning (_("Color index out of range"));
00208     return NULL;
00209   }
00210 
00211   c = print_colors[color];
00212 
00213   if ((c.a == 0) || !c.enabled) {
00214     return NULL;
00215   } else {
00216     return g_strdup_printf ("%.3f %.3f %.3f",
00217                             (gdouble) c.r/255.0,
00218                             (gdouble) c.g/255.0,
00219                             (gdouble) c.b/255.0);
00220   }
00221 }
00222 
00223 SCM
00224 s_color_map_to_scm (const COLOR *map)
00225 {
00226   SCM result = SCM_EOL;
00227   int i;
00228   for (i = MAX_COLORS - 1; i >= 0; i--) {
00229     SCM color_val = SCM_BOOL_F;
00230     if (map[i].enabled) {
00231       COLOR c = map[i];
00232       gchar *rgba = s_color_rgba_encode (c.r, c.g, c.b, c.a);
00233       color_val = scm_from_utf8_string (rgba);
00234       g_free (rgba);
00235     }
00236     result = scm_cons (scm_list_2 (scm_from_int (i), color_val), result);
00237   }
00238   return result;
00239 }
00240 
00244 void
00245 s_color_map_from_scm (COLOR *map, SCM lst, const char *scheme_proc_name)
00246 {
00247   SCM curr = lst;
00248   SCM wrong_type_arg_sym = scm_from_utf8_symbol ("wrong-type-arg");
00249   SCM proc_name = scm_from_utf8_string (scheme_proc_name);
00250   while (curr != SCM_EOL) {
00251     int i;
00252     char *rgba;
00253     SCM s;
00254     COLOR c = {0x00, 0x00, 0x00, FALSE};
00255     gboolean result;
00256     SCM entry = scm_car (curr);
00257 
00258     /* Check map entry has correct type */
00259     if (!scm_is_true (scm_list_p (entry))
00260         || (scm_to_int (scm_length (entry)) != 2)) {
00261       scm_error_scm (wrong_type_arg_sym, proc_name,
00262                      scm_from_utf8_string (_("Color map entry must be a two-element list")),
00263                      SCM_EOL, scm_list_1 (entry));
00264     }
00265 
00266     /* Check color index has correct type, and extract it */
00267     s = scm_car (entry);
00268     if (!scm_is_integer (s)) {
00269       scm_error_scm (wrong_type_arg_sym, proc_name,
00270                      scm_from_utf8_string (_("Index in color map entry must be an integer")),
00271                      SCM_EOL, scm_list_1 (s));
00272     }
00273     i = scm_to_int (s);
00274 
00275     /* Check color index is within bounds. If it's out of bounds, it's
00276      * legal, but warn & ignore it.
00277      *
00278      * FIXME one day we will have dynamically-expanding colorspace.
00279      * One day. */
00280     if ((i < 0) || (i >= MAX_COLORS)) {
00281       g_critical ("Color map index out of bounds: %i\n", i);
00282       goto color_map_next;
00283     }
00284 
00285     /* If color value is #F, disable color */
00286     s = scm_cadr (entry);
00287     if (scm_is_false (s)) {
00288       map[i].enabled = FALSE;
00289       goto color_map_next;
00290     }
00291 
00292     /* Otherwise, we require a string */
00293     s = scm_cadr (entry);
00294     if (!scm_is_string (s)) {
00295       scm_error_scm (wrong_type_arg_sym, proc_name,
00296                      scm_from_utf8_string (_("Value in color map entry must be #f or a string")),
00297                      SCM_EOL, scm_list_1 (s));
00298     }
00299     rgba = scm_to_utf8_string (s);
00300 
00301     result = s_color_rgba_decode (rgba, &c.r, &c.g, &c.b, &c.a);
00302 
00303     /* FIXME should we generate a Guile error if there's a problem here? */
00304     if (!result) {
00305       g_critical ("Invalid color map value: %s\n", rgba);
00306     } else {
00307       map[i] = c;
00308       map[i].enabled = TRUE;
00309     }
00310 
00311   color_map_next:
00312     /* Go to next element in map */
00313     curr = scm_cdr (curr);
00314   }
00315   scm_remember_upto_here_2 (wrong_type_arg_sym, proc_name);
00316 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines