colxlate.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /**
  2. * @file
  3. * @brief API colorprocs.h
  4. * @ingroup common_utils
  5. */
  6. /*************************************************************************
  7. * Copyright (c) 2011 AT&T Intellectual Property
  8. * All rights reserved. This program and the accompanying materials
  9. * are made available under the terms of the Eclipse Public License v1.0
  10. * which accompanies this distribution, and is available at
  11. * https://www.eclipse.org/legal/epl-v10.html
  12. *
  13. * Contributors: Details at https://graphviz.org
  14. *************************************************************************/
  15. #include <stdio.h>
  16. #include <math.h>
  17. #include <stdbool.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <common/arith.h>
  21. #include <common/color.h>
  22. #include <common/colorprocs.h>
  23. #include <common/colortbl.h>
  24. #include <cgraph/gv_ctype.h>
  25. #include <cgraph/gv_math.h>
  26. #include <util/agxbuf.h>
  27. #include <util/alloc.h>
  28. #include <util/strcasecmp.h>
  29. #include <util/unreachable.h>
  30. static char* colorscheme;
  31. static void hsv2rgb(double h, double s, double v,
  32. double *r, double *g, double *b)
  33. {
  34. int i;
  35. double f, p, q, t;
  36. if (s <= 0.0) { /* achromatic */
  37. *r = v;
  38. *g = v;
  39. *b = v;
  40. } else {
  41. if (h >= 1.0)
  42. h = 0.0;
  43. h = 6.0 * h;
  44. i = (int) h;
  45. f = h - i;
  46. p = v * (1 - s);
  47. q = v * (1 - s * f);
  48. t = v * (1 - s * (1 - f));
  49. switch (i) {
  50. case 0:
  51. *r = v;
  52. *g = t;
  53. *b = p;
  54. break;
  55. case 1:
  56. *r = q;
  57. *g = v;
  58. *b = p;
  59. break;
  60. case 2:
  61. *r = p;
  62. *g = v;
  63. *b = t;
  64. break;
  65. case 3:
  66. *r = p;
  67. *g = q;
  68. *b = v;
  69. break;
  70. case 4:
  71. *r = t;
  72. *g = p;
  73. *b = v;
  74. break;
  75. case 5:
  76. *r = v;
  77. *g = p;
  78. *b = q;
  79. break;
  80. default:
  81. UNREACHABLE();
  82. }
  83. }
  84. }
  85. static void rgb2hsv(double r, double g, double b,
  86. double *h, double *s, double *v)
  87. {
  88. double rgbmin, rgbmax;
  89. double rc, bc, gc;
  90. double ht = 0.0, st = 0.0;
  91. rgbmin = fmin(r, fmin(g, b));
  92. rgbmax = fmax(r, fmax(g, b));
  93. if (rgbmax > 0.0)
  94. st = (rgbmax - rgbmin) / rgbmax;
  95. if (st > 0.0) {
  96. rc = (rgbmax - r) / (rgbmax - rgbmin);
  97. gc = (rgbmax - g) / (rgbmax - rgbmin);
  98. bc = (rgbmax - b) / (rgbmax - rgbmin);
  99. if (is_exactly_equal(r, rgbmax))
  100. ht = bc - gc;
  101. else if (is_exactly_equal(g, rgbmax))
  102. ht = 2 + rc - bc;
  103. else if (is_exactly_equal(b, rgbmax))
  104. ht = 4 + gc - rc;
  105. ht = ht * 60.0;
  106. if (ht < 0.0)
  107. ht += 360.0;
  108. }
  109. *h = ht / 360.0;
  110. *v = rgbmax;
  111. *s = st;
  112. }
  113. static int colorcmpf(const void *p0, const void *p1)
  114. {
  115. return strcasecmp(p0, ((const hsvrgbacolor_t *)p1)->name);
  116. }
  117. /* fullColor:
  118. * Return "/prefix/str"
  119. */
  120. static char *fullColor(agxbuf *xb, const char *prefix, const char *str) {
  121. agxbprint(xb, "/%s/%s", prefix, str);
  122. return agxbuse(xb);
  123. }
  124. /* resolveColor:
  125. * Resolve input color str allowing color scheme namespaces.
  126. * 0) "black" => "black"
  127. * "white" => "white"
  128. * "lightgrey" => "lightgrey"
  129. * NB: This is something of a hack due to the remaining codegen.
  130. * Once these are gone, this case could be removed and all references
  131. * to "black" could be replaced by "/X11/black".
  132. * 1) No initial / =>
  133. * if colorscheme is defined and no "X11", return /colorscheme/str
  134. * else return str
  135. * 2) One initial / => return str+1
  136. * 3) Two initial /'s =>
  137. * a) If colorscheme is defined and not "X11", return /colorscheme/(str+2)
  138. * b) else return (str+2)
  139. * 4) Two /'s, not both initial => return str.
  140. *
  141. * Note that 1), 2), and 3b) allow the default X11 color scheme.
  142. *
  143. * In other words,
  144. * xxx => /colorscheme/xxx if colorscheme is defined and not "X11"
  145. * xxx => xxx otherwise
  146. * /xxx => xxx
  147. * /X11/yyy => yyy
  148. * /xxx/yyy => /xxx/yyy
  149. * //yyy => /colorscheme/yyy if colorscheme is defined and not "X11"
  150. * //yyy => yyy otherwise
  151. *
  152. * At present, no other error checking is done. For example,
  153. * yyy could be "". This will be caught later.
  154. */
  155. #define DFLT_SCHEME "X11/" /* Must have final '/' */
  156. #define DFLT_SCHEME_LEN ((sizeof(DFLT_SCHEME)-1)/sizeof(char))
  157. #define ISNONDFLT(s) ((s) && *(s) && strncasecmp(DFLT_SCHEME, s, DFLT_SCHEME_LEN-1))
  158. static char *resolveColor(const char *str) {
  159. const char *s;
  160. if (!strcmp(str, "black")) return strdup(str);
  161. if (!strcmp(str, "white")) return strdup(str);
  162. if (!strcmp(str, "lightgrey")) return strdup(str);
  163. agxbuf xb = {0};
  164. if (*str == '/') { /* if begins with '/' */
  165. const char *const c2 = str + 1; // second char
  166. const char *const ss = strchr(c2, '/'); // second slash
  167. if (ss != NULL) { // if has second '/'
  168. if (*c2 == '/') { /* if second '/' is second character */
  169. /* Do not compare against final '/' */
  170. if (ISNONDFLT(colorscheme))
  171. s = fullColor(&xb, colorscheme, c2+1);
  172. else
  173. s = c2+1;
  174. }
  175. else if (strncasecmp(DFLT_SCHEME, c2, DFLT_SCHEME_LEN)) s = str;
  176. else s = ss + 1;
  177. }
  178. else s = c2;
  179. }
  180. else if (ISNONDFLT(colorscheme)) s = fullColor(&xb, colorscheme, str);
  181. else s = str;
  182. char *on_heap = strdup(s);
  183. agxbfree(&xb);
  184. return on_heap;
  185. }
  186. int colorxlate(const char *str, gvcolor_t *color, color_type_t target_type) {
  187. char c;
  188. double H, S, V, A, R, G, B;
  189. unsigned int r, g, b;
  190. color->type = target_type;
  191. int rc = COLOR_OK;
  192. for (; *str == ' '; str++); /* skip over any leading whitespace */
  193. const char *p = str;
  194. /* test for rgb value such as: "#ff0000"
  195. or rgba value such as "#ff000080" */
  196. unsigned a = 255; // default alpha channel value=opaque in case not supplied
  197. bool is_rgb = sscanf(p, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3;
  198. if (!is_rgb) { // try 3 letter form
  199. is_rgb = strlen(p) == 4 && sscanf(p, "#%1x%1x%1x", &r, &g, &b) == 3;
  200. if (is_rgb) {
  201. r |= r << 4;
  202. g |= g << 4;
  203. b |= b << 4;
  204. }
  205. }
  206. if (is_rgb) {
  207. switch (target_type) {
  208. case HSVA_DOUBLE:
  209. R = (double) r / 255.0;
  210. G = (double) g / 255.0;
  211. B = (double) b / 255.0;
  212. A = (double) a / 255.0;
  213. rgb2hsv(R, G, B, &H, &S, &V);
  214. color->u.HSVA[0] = H;
  215. color->u.HSVA[1] = S;
  216. color->u.HSVA[2] = V;
  217. color->u.HSVA[3] = A;
  218. break;
  219. case RGBA_BYTE:
  220. color->u.rgba[0] = (unsigned char)r;
  221. color->u.rgba[1] = (unsigned char)g;
  222. color->u.rgba[2] = (unsigned char)b;
  223. color->u.rgba[3] = (unsigned char)a;
  224. break;
  225. case RGBA_WORD:
  226. color->u.rrggbbaa[0] = (int)(r * 65535 / 255);
  227. color->u.rrggbbaa[1] = (int)(g * 65535 / 255);
  228. color->u.rrggbbaa[2] = (int)(b * 65535 / 255);
  229. color->u.rrggbbaa[3] = (int)(a * 65535 / 255);
  230. break;
  231. case RGBA_DOUBLE:
  232. color->u.RGBA[0] = (double) r / 255.0;
  233. color->u.RGBA[1] = (double) g / 255.0;
  234. color->u.RGBA[2] = (double) b / 255.0;
  235. color->u.RGBA[3] = (double) a / 255.0;
  236. break;
  237. case COLOR_STRING:
  238. break;
  239. case COLOR_INDEX:
  240. break;
  241. default:
  242. UNREACHABLE();
  243. }
  244. return rc;
  245. }
  246. /* test for hsv value such as: ".6,.5,.3" */
  247. if ((c = *p) == '.' || gv_isdigit(c)) {
  248. agxbuf canon = {0};
  249. while ((c = *p++)) {
  250. agxbputc(&canon, c == ',' ? ' ' : c);
  251. }
  252. A = 1.0; // default
  253. if (sscanf(agxbuse(&canon), "%lf%lf%lf%lf", &H, &S, &V, &A) >= 3) {
  254. /* clip to reasonable values */
  255. H = fmax(fmin(H, 1.0), 0.0);
  256. S = fmax(fmin(S, 1.0), 0.0);
  257. V = fmax(fmin(V, 1.0), 0.0);
  258. A = fmax(fmin(A, 1.0), 0.0);
  259. switch (target_type) {
  260. case HSVA_DOUBLE:
  261. color->u.HSVA[0] = H;
  262. color->u.HSVA[1] = S;
  263. color->u.HSVA[2] = V;
  264. color->u.HSVA[3] = A;
  265. break;
  266. case RGBA_BYTE:
  267. hsv2rgb(H, S, V, &R, &G, &B);
  268. color->u.rgba[0] = (unsigned char)(R * 255);
  269. color->u.rgba[1] = (unsigned char)(G * 255);
  270. color->u.rgba[2] = (unsigned char)(B * 255);
  271. color->u.rgba[3] = (unsigned char)(A * 255);
  272. break;
  273. case RGBA_WORD:
  274. hsv2rgb(H, S, V, &R, &G, &B);
  275. color->u.rrggbbaa[0] = (int) (R * 65535);
  276. color->u.rrggbbaa[1] = (int) (G * 65535);
  277. color->u.rrggbbaa[2] = (int) (B * 65535);
  278. color->u.rrggbbaa[3] = (int) (A * 65535);
  279. break;
  280. case RGBA_DOUBLE:
  281. hsv2rgb(H, S, V, &R, &G, &B);
  282. color->u.RGBA[0] = R;
  283. color->u.RGBA[1] = G;
  284. color->u.RGBA[2] = B;
  285. color->u.RGBA[3] = A;
  286. break;
  287. case COLOR_STRING:
  288. break;
  289. case COLOR_INDEX:
  290. break;
  291. default:
  292. UNREACHABLE();
  293. }
  294. agxbfree(&canon);
  295. return rc;
  296. }
  297. agxbfree(&canon);
  298. }
  299. /* test for known color name (generic, not renderer specific known names) */
  300. char *name = resolveColor(str);
  301. if (!name)
  302. return COLOR_MALLOC_FAIL;
  303. const hsvrgbacolor_t *known = bsearch(name, color_lib,
  304. sizeof(color_lib) / sizeof(hsvrgbacolor_t),
  305. sizeof(color_lib[0]), colorcmpf);
  306. free(name);
  307. if (known != NULL) {
  308. switch (target_type) {
  309. case HSVA_DOUBLE:
  310. color->u.HSVA[0] = (double)known->h / 255.0;
  311. color->u.HSVA[1] = (double)known->s / 255.0;
  312. color->u.HSVA[2] = (double)known->v / 255.0;
  313. color->u.HSVA[3] = (double)known->a / 255.0;
  314. break;
  315. case RGBA_BYTE:
  316. color->u.rgba[0] = known->r;
  317. color->u.rgba[1] = known->g;
  318. color->u.rgba[2] = known->b;
  319. color->u.rgba[3] = known->a;
  320. break;
  321. case RGBA_WORD:
  322. color->u.rrggbbaa[0] = known->r * 65535 / 255;
  323. color->u.rrggbbaa[1] = known->g * 65535 / 255;
  324. color->u.rrggbbaa[2] = known->b * 65535 / 255;
  325. color->u.rrggbbaa[3] = known->a * 65535 / 255;
  326. break;
  327. case RGBA_DOUBLE:
  328. color->u.RGBA[0] = known->r / 255.0;
  329. color->u.RGBA[1] = known->g / 255.0;
  330. color->u.RGBA[2] = known->b / 255.0;
  331. color->u.RGBA[3] = known->a / 255.0;
  332. break;
  333. case COLOR_STRING:
  334. break;
  335. case COLOR_INDEX:
  336. break;
  337. default:
  338. UNREACHABLE();
  339. }
  340. return rc;
  341. }
  342. /* if we're still here then we failed to find a valid color spec */
  343. rc = COLOR_UNKNOWN;
  344. switch (target_type) {
  345. case HSVA_DOUBLE:
  346. color->u.HSVA[0] = color->u.HSVA[1] = color->u.HSVA[2] = 0.0;
  347. color->u.HSVA[3] = 1.0; /* opaque */
  348. break;
  349. case RGBA_BYTE:
  350. color->u.rgba[0] = color->u.rgba[1] = color->u.rgba[2] = 0;
  351. color->u.rgba[3] = 255; /* opaque */
  352. break;
  353. case RGBA_WORD:
  354. color->u.rrggbbaa[0] = color->u.rrggbbaa[1] = color->u.rrggbbaa[2] = 0;
  355. color->u.rrggbbaa[3] = 65535; /* opaque */
  356. break;
  357. case RGBA_DOUBLE:
  358. color->u.RGBA[0] = color->u.RGBA[1] = color->u.RGBA[2] = 0.0;
  359. color->u.RGBA[3] = 1.0; /* opaque */
  360. break;
  361. case COLOR_STRING:
  362. break;
  363. case COLOR_INDEX:
  364. break;
  365. default:
  366. UNREACHABLE();
  367. }
  368. return rc;
  369. }
  370. char *setColorScheme(const char *s) {
  371. char *previous = colorscheme;
  372. colorscheme = s == NULL ? NULL : gv_strdup(s);
  373. return previous;
  374. }