libgeda
|
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 }