1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This file contains color methods that are GNU extensions to libplot:
20 pencolor, fillcolor, bgcolor, and the convenience function, color.
21
22 It also contains _grayscale_approx(), which computes grayscale
23 approximations to RGB colors. */
24
25 /* It also includes the pencolorname, fillcolorname, and bgcolorname
26 methods, which are GNU extensions to libplot; also the convenience
27 function colorname. They search a database of known names (stored in
28 g_colorname.h) for a specified color name. If the name is found, its
29 interpretation as a 48-bit RGB color is determined, and pencolor,
30 fillcolor, or bgcolor is called to set the color. If the name is not
31 found, a default color (black for pen and fill, white for bg) is
32 substituted. */
33
34 #include "sys-defines.h"
35 #include "extern.h"
36 #include "g_colorname.h"
37
38 /* forward references */
39 static bool string_to_precise_color (const char *name, plColor *color_p);
40
41 /* The pencolor method, which is a GNU extension to libplot. It sets a
42 drawing attribute: the pen color (``foreground color'') of objects
43 created in the drawing operations that follow. The fill color may be
44 set separately, by invoking fillcolor() and filltype().
45
46 We use the RGB color model. In principle we support 48-bit color (16
47 bits, i.e. 0x0000 through 0xffff, for each of red, green, and blue). */
48
49 int
_API_pencolor(R___ (Plotter * _plotter)int red,int green,int blue)50 _API_pencolor (R___(Plotter *_plotter) int red, int green, int blue)
51 {
52 if (!_plotter->data->open)
53 {
54 _plotter->error (R___(_plotter)
55 "pencolor: invalid operation");
56 return -1;
57 }
58
59 _API_endpath (S___(_plotter)); /* flush path if any */
60
61 if ((red > 0xffff) || (green > 0xffff) || (blue > 0xffff))
62 /* OOB switches to default */
63 {
64 red = _default_drawstate.fgcolor.red;
65 green = _default_drawstate.fgcolor.green;
66 blue = _default_drawstate.fgcolor.blue;
67 }
68
69 if (_plotter->data->emulate_color)
70 /* replace by grayscale approximation */
71 red = green = blue = _grayscale_approx (red, green, blue);
72
73 /* save our notion of foreground color */
74 _plotter->drawstate->fgcolor.red = red;
75 _plotter->drawstate->fgcolor.green = green;
76 _plotter->drawstate->fgcolor.blue = blue;
77
78 return 0;
79 }
80
81 /* The fillcolor method, which is a GNU extension to libplot. It sets a
82 drawing attribute: the fill color of objects created in the following
83 drawing operations. Actually the true fill color (if filling is not
84 disabled) will be a desaturated version of the user-specified fill
85 color. The desaturation level is set by invoking filltype().
86
87 In principle we support 48-bit color (16 bits, i.e. 0x0000 through
88 0xffff, for each of red, green, and blue). */
89
90 int
_API_fillcolor(R___ (Plotter * _plotter)int red,int green,int blue)91 _API_fillcolor (R___(Plotter *_plotter) int red, int green, int blue)
92 {
93 double red_d, green_d, blue_d;
94 double desaturate;
95 plColor new_rgb;
96
97 if (!_plotter->data->open)
98 {
99 _plotter->error (R___(_plotter)
100 "fillcolor: invalid operation");
101 return -1;
102 }
103
104 _API_endpath (S___(_plotter)); /* flush path if any */
105
106 if ((red > 0xffff) || (green > 0xffff) || (blue > 0xffff))
107 /* OOB switches to default */
108 {
109 red = _default_drawstate.fillcolor.red;
110 green = _default_drawstate.fillcolor.green;
111 blue = _default_drawstate.fillcolor.blue;
112 }
113
114 if (_plotter->data->emulate_color)
115 /* replace by grayscale approximation */
116 red = green = blue = _grayscale_approx (red, green, blue);
117
118 /* save the basic fillcolor (i.e. the base fillcolor, unaffected by the
119 fill type) */
120 _plotter->drawstate->fillcolor_base.red = red;
121 _plotter->drawstate->fillcolor_base.green = green;
122 _plotter->drawstate->fillcolor_base.blue = blue;
123
124 if (_plotter->drawstate->fill_type == 0)
125 /* won't be doing filling, so stop right here */
126 return 0;
127
128 /* update fillcolor, taking fill type into account */
129
130 /* scale each RGB from a 16-bit quantity to range [0.0,1.0] */
131 red_d = ((double)red)/0xFFFF;
132 green_d = ((double)green)/0xFFFF;
133 blue_d = ((double)blue)/0xFFFF;
134
135 /* fill_type, if nonzero, specifies the extent to which the nominal fill
136 color should be desaturated. 1 means no desaturation, 0xffff means
137 complete desaturation (white). */
138 desaturate = ((double)_plotter->drawstate->fill_type - 1.)/0xFFFE;
139 red_d = red_d + desaturate * (1.0 - red_d);
140 green_d = green_d + desaturate * (1.0 - green_d);
141 blue_d = blue_d + desaturate * (1.0 - blue_d);
142
143 /* restore each RGB to a 16-bit quantity (48 bits in all) */
144 new_rgb.red = IROUND(0xFFFF * red_d);
145 new_rgb.green = IROUND(0xFFFF * green_d);
146 new_rgb.blue = IROUND(0xFFFF * blue_d);
147
148 /* store actual fill color in drawing state */
149 _plotter->drawstate->fillcolor = new_rgb;
150
151 return 0;
152 }
153
154 /* convenience function */
155
156 int
_API_color(R___ (Plotter * _plotter)int red,int green,int blue)157 _API_color (R___(Plotter *_plotter) int red, int green, int blue)
158 {
159 if (!_plotter->data->open)
160 {
161 _plotter->error (R___(_plotter)
162 "color: invalid operation");
163 return -1;
164 }
165
166 _API_pencolor (R___(_plotter) red, green, blue);
167 _API_fillcolor (R___(_plotter) red, green, blue);
168
169 return 0;
170 }
171
172 /* The bgcolor method, which is a GNU extension to libplot. It sets a
173 drawing attribute: the background color.
174
175 We use the RGB color model. In principle we support 48-bit color (16
176 bits, i.e. 0x0000 through 0xffff, for each of red, green, and blue). */
177
178 int
_API_bgcolor(R___ (Plotter * _plotter)int red,int green,int blue)179 _API_bgcolor (R___(Plotter *_plotter) int red, int green, int blue)
180 {
181 if (!_plotter->data->open)
182 {
183 _plotter->error (R___(_plotter)
184 "bgcolor: invalid operation");
185 return -1;
186 }
187
188 if ((red > 0xffff) || (green > 0xffff) || (blue > 0xffff))
189 /* OOB switches to default */
190 {
191 red = _default_drawstate.bgcolor.red;
192 green = _default_drawstate.bgcolor.green;
193 blue = _default_drawstate.bgcolor.blue;
194 }
195
196 if (_plotter->data->emulate_color)
197 /* replace by grayscale approximation */
198 red = green = blue = _grayscale_approx (red, green, blue);
199
200 /* save our notion of background color */
201 _plotter->drawstate->bgcolor.red = red;
202 _plotter->drawstate->bgcolor.green = green;
203 _plotter->drawstate->bgcolor.blue = blue;
204
205 return 0;
206 }
207
208 /* compute a 16-bit grayscale approximation to a 48-bit RGB; optionally
209 used by pencolorname, fillcolorname, bgcolorname methods */
210 int
_grayscale_approx(int red,int green,int blue)211 _grayscale_approx (int red, int green, int blue)
212 {
213 double gray;
214
215 /* compute CIE luminance according to Rec. 709 */
216 gray = 0.212671 * red + 0.715160 * green + 0.072169 * blue;
217 return IROUND(gray);
218 }
219
220 /* Below are the pencolorname, fillcolorname, and bgcolorname methods,
221 which are GNU extensions to libplot. They search a database of known
222 names (stored in g_colorname.h) for a specified color name. If the name
223 is found, its interpretation as a 48-bit RGB color is determined, and
224 pencolor, fillcolor, or bgcolor is called to set the color. If the name
225 is not found, a default color (black for pen and fill, white for bg) is
226 substituted.
227
228 The lowest-level routine is _string_to_color(). */
229
230 int
_API_pencolorname(R___ (Plotter * _plotter)const char * name)231 _API_pencolorname (R___(Plotter *_plotter) const char *name)
232 {
233 plColor color;
234 int intred, intgreen, intblue;
235
236 if (!_plotter->data->open)
237 {
238 _plotter->error (R___(_plotter)
239 "pencolorname: invalid operation");
240 return -1;
241 }
242
243 /* null pointer ignored */
244 if (!name)
245 return 0;
246
247 /* RGB values for default pen color */
248 intred = _default_drawstate.fgcolor.red;
249 intgreen = _default_drawstate.fgcolor.green;
250 intblue = _default_drawstate.fgcolor.blue;
251
252 if (_string_to_color (name, &color, _plotter->data->color_name_cache))
253 {
254 unsigned int red, green, blue;
255
256 red = color.red;
257 green = color.green;
258 blue = color.blue;
259 /* to convert from 24-bit to 48-bit color, double bytes */
260 intred = (red << 8) | red;
261 intgreen = (green << 8) | green;
262 intblue = (blue << 8) | blue;
263 }
264 else if (_plotter->data->pen_color_warning_issued == false)
265 {
266 char *buf;
267
268 buf = (char *)_pl_xmalloc (strlen (name) + 100);
269 sprintf (buf, "substituting \"black\" for undefined pen color \"%s\"",
270 name);
271 _plotter->warning (R___(_plotter) buf);
272 free (buf);
273 _plotter->data->pen_color_warning_issued = true;
274 }
275
276 _API_pencolor (R___(_plotter) intred, intgreen, intblue);
277
278 return 0;
279 }
280
281 int
_API_fillcolorname(R___ (Plotter * _plotter)const char * name)282 _API_fillcolorname (R___(Plotter *_plotter) const char *name)
283 {
284 plColor color;
285 int intred, intgreen, intblue;
286
287 if (!_plotter->data->open)
288 {
289 _plotter->error (R___(_plotter)
290 "fillcolorname: invalid operation");
291 return -1;
292 }
293
294 /* null pointer ignored */
295 if (!name)
296 return 0;
297
298 /* RGB values for default fill color */
299 intred = _default_drawstate.fillcolor.red;
300 intgreen = _default_drawstate.fillcolor.green;
301 intblue = _default_drawstate.fillcolor.blue;
302
303 if (_string_to_color (name, &color, _plotter->data->color_name_cache))
304 {
305 unsigned int red, green, blue;
306
307 red = color.red;
308 green = color.green;
309 blue = color.blue;
310 /* to convert from 24-bit to 48-bit color, double bytes */
311 intred = (red << 8) | red;
312 intgreen = (green << 8) | green;
313 intblue = (blue << 8) | blue;
314 }
315 else if (_plotter->data->fill_color_warning_issued == false)
316 {
317 char *buf;
318
319 buf = (char *)_pl_xmalloc (strlen (name) + 100);
320 sprintf (buf, "substituting \"black\" for undefined fill color \"%s\"",
321 name);
322 _plotter->warning (R___(_plotter) buf);
323 free (buf);
324 _plotter->data->fill_color_warning_issued = true;
325 }
326
327 _API_fillcolor (R___(_plotter) intred, intgreen, intblue);
328
329 return 0;
330 }
331
332 /* convenience function */
333
334 int
_API_colorname(R___ (Plotter * _plotter)const char * name)335 _API_colorname (R___(Plotter *_plotter) const char *name)
336 {
337 if (!_plotter->data->open)
338 {
339 _plotter->error (R___(_plotter)
340 "colorname: invalid operation");
341 return -1;
342 }
343
344 _API_pencolorname (R___(_plotter) name);
345 _API_fillcolorname (R___(_plotter) name);
346
347 return 0;
348 }
349
350 int
_API_bgcolorname(R___ (Plotter * _plotter)const char * name)351 _API_bgcolorname (R___(Plotter *_plotter) const char *name)
352 {
353 plColor color;
354 int intred, intgreen, intblue;
355
356 if (!_plotter->data->open)
357 {
358 _plotter->error (R___(_plotter)
359 "bgcolorname: invalid operation");
360 return -1;
361 }
362
363 /* null pointer ignored */
364 if (!name)
365 return 0;
366
367 if (strcmp (name, "none") == 0)
368 /* turn off background (some Plotters can implement this) */
369 {
370 _plotter->drawstate->bgcolor_suppressed = true;
371 /* treat as default, for benefit of Plotters that can't */
372 name = "white";
373 }
374 else
375 _plotter->drawstate->bgcolor_suppressed = false;
376
377 /* RGB values for default color [white, presumably] */
378 intred = _default_drawstate.bgcolor.red;
379 intgreen = _default_drawstate.bgcolor.green;
380 intblue = _default_drawstate.bgcolor.blue;
381
382 if (_string_to_color (name, &color, _plotter->data->color_name_cache))
383 {
384 unsigned int red, green, blue;
385
386 red = color.red;
387 green = color.green;
388 blue = color.blue;
389 /* to convert from 24-bit to 48-bit color, double bytes */
390 intred = (red << 8) | red;
391 intgreen = (green << 8) | green;
392 intblue = (blue << 8) | blue;
393 }
394 else if (_plotter->data->bg_color_warning_issued == false)
395 {
396 char *buf;
397
398 buf = (char *)_pl_xmalloc (strlen (name) + 100);
399 sprintf (buf, "substituting \"white\" for undefined background color \"%s\"",
400 name);
401 _plotter->warning (R___(_plotter) buf);
402 free (buf);
403 _plotter->data->bg_color_warning_issued = true;
404 }
405
406 _API_bgcolor (R___(_plotter) intred, intgreen, intblue);
407
408 return 0;
409 }
410
411 /* _string_to_color() searches a database of known color names, in
412 g_colorname.h, for a specified string. Matches are case-insensitive and
413 ignore spaces. The retrieved RGB components are returned via a pointer.
414
415 We don't wish to search through the entire (long) color database. (It
416 contains 600+ color name strings.) So any Plotter maintains a cache
417 of previously found colors. */
418
419 bool
_string_to_color(const char * name,plColor * color_p,plColorNameCache * color_name_cache)420 _string_to_color (const char *name, plColor *color_p, plColorNameCache *color_name_cache)
421 {
422 plColor color;
423 plCachedColorNameInfo **cached_colors_p;
424 bool found = false;
425 char *squeezed_name, *nptr;
426 const plColorNameInfo *info, *found_info = NULL;
427 const char *optr;
428 plCachedColorNameInfo *cached_info;
429
430 if (name == NULL) /* avoid core dumps */
431 return false;
432
433 if (color_name_cache == NULL) /* avoid core dumps */
434 return false;
435
436 /* first check whether string is of the form "#ffffff" */
437 if (string_to_precise_color (name, &color))
438 {
439 *color_p = color;
440 return true;
441 }
442
443 /* copy string, removing spaces */
444 squeezed_name = (char *)_pl_xmalloc (strlen (name) + 1);
445 optr = name, nptr = squeezed_name;
446 while (*optr)
447 {
448 if (*optr == '\0')
449 break;
450 if (*optr != ' ')
451 *nptr++ = *optr;
452 optr++;
453 }
454 *nptr = '\0';
455
456 /* Search our list of cached, previously used color names, doing string
457 comparison. If this were only for use by the X11 driver, we'd use
458 XrmPermStringToQuark to get a faster-compared representation. */
459
460 cached_colors_p = &color_name_cache->cached_colors;
461 cached_info = *cached_colors_p;
462 while (cached_info)
463 {
464 if (strcasecmp (cached_info->info->name, squeezed_name) == 0)
465 {
466 found = true;
467 found_info = cached_info->info;
468 break;
469 }
470 cached_info = cached_info->next;
471 }
472
473 if (!found)
474 /* not previously used, so search master colorname table (this is slower) */
475 {
476 info = _pl_g_colornames; /* start at head of list in g_colorname.h */
477 while (info->name)
478 {
479 if (strcasecmp (info->name, squeezed_name) == 0)
480 {
481 found = true;
482 found_info = info;
483 break;
484 }
485 info++;
486 }
487
488 if (found)
489 /* copy to head of cached color list */
490 {
491 plCachedColorNameInfo *old_cached_colors, *cached_colors;
492
493 old_cached_colors = *cached_colors_p;
494 cached_colors =
495 (plCachedColorNameInfo *)_pl_xmalloc (sizeof (plCachedColorNameInfo));
496 cached_colors->next = old_cached_colors;
497 cached_colors->info = found_info;
498 *cached_colors_p = cached_colors;
499 }
500 }
501
502 free (squeezed_name);
503 if (found)
504 {
505 color_p->red = found_info->red;
506 color_p->green = found_info->green;
507 color_p->blue = found_info->blue;
508 }
509
510 return found;
511 }
512
513 /* Attempt to map a string to a 24-bit RGB; this will work if the string is
514 of the form "#ffffff". */
515
516 static bool
string_to_precise_color(const char * name,plColor * color_p)517 string_to_precise_color (const char *name, plColor *color_p)
518 {
519 const char *good_hex_digits = "0123456789abcdefABCDEF";
520 int i, num_assigned;
521
522 if (name == (const char *)NULL || *name != '#')
523 return false;
524
525 for (i = 1; i <= 8 ; i++)
526 {
527 bool found;
528 const char *cp;
529
530 if (name[i] == '\0')
531 break;
532 cp = good_hex_digits;
533 found = false;
534 while (*cp)
535 {
536 if (name[i] == *cp)
537 {
538 found = true;
539 break;
540 }
541 cp++;
542 }
543 if (found == false)
544 return false;
545 }
546 if (i != 7)
547 return false;
548
549 /* okay, have something like "#ffffff"; can now safely use scanf() */
550
551 num_assigned = sscanf (name, "#%2x%2x%2x",
552 &(color_p->red), &(color_p->green), &(color_p->blue));
553
554 return (num_assigned == 3 ? true : false);
555 }
556
557
558 /* The cache of color names is currently implemented as a linked list. */
559
560 plColorNameCache *
_create_color_name_cache(void)561 _create_color_name_cache (void)
562 {
563 plColorNameCache *new_cache;
564
565 new_cache = (plColorNameCache *)_pl_xmalloc(sizeof(plColorNameCache));
566 new_cache->cached_colors = NULL;
567 return new_cache;
568 }
569
570 void
_delete_color_name_cache(plColorNameCache * color_name_cache)571 _delete_color_name_cache (plColorNameCache *color_name_cache)
572 {
573 plCachedColorNameInfo *colorptr;
574
575 if (color_name_cache == (plColorNameCache *)NULL)
576 return;
577
578 colorptr = color_name_cache->cached_colors;
579 while (colorptr != NULL) /* free linked list */
580 {
581 plCachedColorNameInfo *next_colorptr;
582
583 next_colorptr = colorptr->next;
584 free (colorptr);
585 colorptr = next_colorptr;
586 }
587
588 free (color_name_cache); /* free structure itself */
589 }
590