g_colors.c 17 KB

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