Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
colxlate.c
Go to the documentation of this file.
1
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
16#include <stdio.h>
17
18#include <math.h>
19#include <stdbool.h>
20#include <stdlib.h>
21#include <string.h>
22#include <common/arith.h>
23#include <common/color.h>
24#include <common/colorprocs.h>
25#include <common/colortbl.h>
26#include <cgraph/agxbuf.h>
27#include <cgraph/alloc.h>
28#include <cgraph/gv_ctype.h>
29#include <cgraph/gv_math.h>
30#include <cgraph/strcasecmp.h>
31#include <cgraph/unreachable.h>
32
33static char* colorscheme;
34
35static void hsv2rgb(double h, double s, double v,
36 double *r, double *g, double *b)
37{
38 int i;
39 double f, p, q, t;
40
41 if (s <= 0.0) { /* achromatic */
42 *r = v;
43 *g = v;
44 *b = v;
45 } else {
46 if (h >= 1.0)
47 h = 0.0;
48 h = 6.0 * h;
49 i = (int) h;
50 f = h - i;
51 p = v * (1 - s);
52 q = v * (1 - s * f);
53 t = v * (1 - s * (1 - f));
54 switch (i) {
55 case 0:
56 *r = v;
57 *g = t;
58 *b = p;
59 break;
60 case 1:
61 *r = q;
62 *g = v;
63 *b = p;
64 break;
65 case 2:
66 *r = p;
67 *g = v;
68 *b = t;
69 break;
70 case 3:
71 *r = p;
72 *g = q;
73 *b = v;
74 break;
75 case 4:
76 *r = t;
77 *g = p;
78 *b = v;
79 break;
80 case 5:
81 *r = v;
82 *g = p;
83 *b = q;
84 break;
85 default:
87 }
88 }
89}
90
91static void rgb2hsv(double r, double g, double b,
92 double *h, double *s, double *v)
93{
94
95 double rgbmin, rgbmax;
96 double rc, bc, gc;
97 double ht = 0.0, st = 0.0;
98
99 rgbmin = fmin(r, fmin(g, b));
100 rgbmax = fmax(r, fmax(g, b));
101
102 if (rgbmax > 0.0)
103 st = (rgbmax - rgbmin) / rgbmax;
104
105 if (st > 0.0) {
106 rc = (rgbmax - r) / (rgbmax - rgbmin);
107 gc = (rgbmax - g) / (rgbmax - rgbmin);
108 bc = (rgbmax - b) / (rgbmax - rgbmin);
109 if (is_exactly_equal(r, rgbmax))
110 ht = bc - gc;
111 else if (is_exactly_equal(g, rgbmax))
112 ht = 2 + rc - bc;
113 else if (is_exactly_equal(b, rgbmax))
114 ht = 4 + gc - rc;
115 ht = ht * 60.0;
116 if (ht < 0.0)
117 ht += 360.0;
118 }
119 *h = ht / 360.0;
120 *v = rgbmax;
121 *s = st;
122}
123
124static int colorcmpf(const void *p0, const void *p1)
125{
126 return strcasecmp(p0, ((const hsvrgbacolor_t *)p1)->name);
127}
128
129char *canontoken(char *str)
130{
131 agxbuf canon = {0};
132 char c, *p;
133
134 p = str;
135 while ((c = *p++)) {
137 }
138 return agxbdisown(&canon);
139}
140
141/* fullColor:
142 * Return "/prefix/str"
143 */
144static char *fullColor(agxbuf *xb, const char *prefix, const char *str) {
145 agxbprint(xb, "/%s/%s", prefix, str);
146 return agxbuse(xb);
147}
148
149/* resolveColor:
150 * Resolve input color str allowing color scheme namespaces.
151 * 0) "black" => "black"
152 * "white" => "white"
153 * "lightgrey" => "lightgrey"
154 * NB: This is something of a hack due to the remaining codegen.
155 * Once these are gone, this case could be removed and all references
156 * to "black" could be replaced by "/X11/black".
157 * 1) No initial / =>
158 * if colorscheme is defined and no "X11", return /colorscheme/str
159 * else return str
160 * 2) One initial / => return str+1
161 * 3) Two initial /'s =>
162 * a) If colorscheme is defined and not "X11", return /colorscheme/(str+2)
163 * b) else return (str+2)
164 * 4) Two /'s, not both initial => return str.
165 *
166 * Note that 1), 2), and 3b) allow the default X11 color scheme.
167 *
168 * In other words,
169 * xxx => /colorscheme/xxx if colorscheme is defined and not "X11"
170 * xxx => xxx otherwise
171 * /xxx => xxx
172 * /X11/yyy => yyy
173 * /xxx/yyy => /xxx/yyy
174 * //yyy => /colorscheme/yyy if colorscheme is defined and not "X11"
175 * //yyy => yyy otherwise
176 *
177 * At present, no other error checking is done. For example,
178 * yyy could be "". This will be caught later.
179 */
180
181#define DFLT_SCHEME "X11/" /* Must have final '/' */
182#define DFLT_SCHEME_LEN ((sizeof(DFLT_SCHEME)-1)/sizeof(char))
183#define ISNONDFLT(s) ((s) && *(s) && strncasecmp(DFLT_SCHEME, s, DFLT_SCHEME_LEN-1))
184
185static char* resolveColor (char* str)
186{
187 char* s;
188 char* ss; /* second slash */
189 char* c2; /* second char */
190
191 if (!strcmp(str, "black")) return strdup(str);
192 if (!strcmp(str, "white")) return strdup(str);
193 if (!strcmp(str, "lightgrey")) return strdup(str);
194 agxbuf xb = {0};
195 if (*str == '/') { /* if begins with '/' */
196 c2 = str+1;
197 if ((ss = strchr(c2, '/'))) { /* if has second '/' */
198 if (*c2 == '/') { /* if second '/' is second character */
199 /* Do not compare against final '/' */
201 s = fullColor(&xb, colorscheme, c2+1);
202 else
203 s = c2+1;
204 }
205 else if (strncasecmp(DFLT_SCHEME, c2, DFLT_SCHEME_LEN)) s = str;
206 else s = ss + 1;
207 }
208 else s = c2;
209 }
210 else if (ISNONDFLT(colorscheme)) s = fullColor(&xb, colorscheme, str);
211 else s = str;
212 char *on_heap = strdup(s);
213 agxbfree(&xb);
214 return on_heap;
215}
216
217int colorxlate(char *str, gvcolor_t * color, color_type_t target_type)
218{
219 static hsvrgbacolor_t *last;
220 char *p;
221 char c;
222 double H, S, V, A, R, G, B;
223 unsigned int r, g, b, a;
224 int rc;
225
226 color->type = target_type;
227
228 rc = COLOR_OK;
229 for (; *str == ' '; str++); /* skip over any leading whitespace */
230 p = str;
231
232 /* test for rgb value such as: "#ff0000"
233 or rgba value such as "#ff000080" */
234 a = 255; /* default alpha channel value=opaque in case not supplied */
235 bool is_rgb = *p == '#' && sscanf(p, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3;
236 if (!is_rgb) { // try 3 letter form
237 is_rgb = *p == '#' && strlen(p) == 4 && sscanf(p, "#%1x%1x%1x", &r, &g, &b) == 3;
238 if (is_rgb) {
239 r |= r << 4;
240 g |= g << 4;
241 b |= b << 4;
242 }
243 }
244 if (is_rgb) {
245 switch (target_type) {
246 case HSVA_DOUBLE:
247 R = (double) r / 255.0;
248 G = (double) g / 255.0;
249 B = (double) b / 255.0;
250 A = (double) a / 255.0;
251 rgb2hsv(R, G, B, &H, &S, &V);
252 color->u.HSVA[0] = H;
253 color->u.HSVA[1] = S;
254 color->u.HSVA[2] = V;
255 color->u.HSVA[3] = A;
256 break;
257 case RGBA_BYTE:
258 color->u.rgba[0] = (unsigned char)r;
259 color->u.rgba[1] = (unsigned char)g;
260 color->u.rgba[2] = (unsigned char)b;
261 color->u.rgba[3] = (unsigned char)a;
262 break;
263 case RGBA_WORD:
264 color->u.rrggbbaa[0] = (int)(r * 65535 / 255);
265 color->u.rrggbbaa[1] = (int)(g * 65535 / 255);
266 color->u.rrggbbaa[2] = (int)(b * 65535 / 255);
267 color->u.rrggbbaa[3] = (int)(a * 65535 / 255);
268 break;
269 case RGBA_DOUBLE:
270 color->u.RGBA[0] = (double) r / 255.0;
271 color->u.RGBA[1] = (double) g / 255.0;
272 color->u.RGBA[2] = (double) b / 255.0;
273 color->u.RGBA[3] = (double) a / 255.0;
274 break;
275 case COLOR_STRING:
276 break;
277 case COLOR_INDEX:
278 break;
279 default:
280 UNREACHABLE();
281 }
282 return rc;
283 }
284
285 /* test for hsv value such as: ".6,.5,.3" */
286 if ((c = *p) == '.' || gv_isdigit(c)) {
287 agxbuf canon = {0};
288 while ((c = *p++)) {
289 agxbputc(&canon, c == ',' ? ' ' : c);
290 }
291
292 A = 1.0; // default
293 if (sscanf(agxbuse(&canon), "%lf%lf%lf%lf", &H, &S, &V, &A) >= 3) {
294 /* clip to reasonable values */
295 H = fmax(fmin(H, 1.0), 0.0);
296 S = fmax(fmin(S, 1.0), 0.0);
297 V = fmax(fmin(V, 1.0), 0.0);
298 A = fmax(fmin(A, 1.0), 0.0);
299 switch (target_type) {
300 case HSVA_DOUBLE:
301 color->u.HSVA[0] = H;
302 color->u.HSVA[1] = S;
303 color->u.HSVA[2] = V;
304 color->u.HSVA[3] = A;
305 break;
306 case RGBA_BYTE:
307 hsv2rgb(H, S, V, &R, &G, &B);
308 color->u.rgba[0] = (unsigned char)(R * 255);
309 color->u.rgba[1] = (unsigned char)(G * 255);
310 color->u.rgba[2] = (unsigned char)(B * 255);
311 color->u.rgba[3] = (unsigned char)(A * 255);
312 break;
313 case RGBA_WORD:
314 hsv2rgb(H, S, V, &R, &G, &B);
315 color->u.rrggbbaa[0] = (int) (R * 65535);
316 color->u.rrggbbaa[1] = (int) (G * 65535);
317 color->u.rrggbbaa[2] = (int) (B * 65535);
318 color->u.rrggbbaa[3] = (int) (A * 65535);
319 break;
320 case RGBA_DOUBLE:
321 hsv2rgb(H, S, V, &R, &G, &B);
322 color->u.RGBA[0] = R;
323 color->u.RGBA[1] = G;
324 color->u.RGBA[2] = B;
325 color->u.RGBA[3] = A;
326 break;
327 case COLOR_STRING:
328 break;
329 case COLOR_INDEX:
330 break;
331 default:
332 UNREACHABLE();
333 }
334 agxbfree(&canon);
335 return rc;
336 }
337 agxbfree(&canon);
338 }
339
340 /* test for known color name (generic, not renderer specific known names) */
341 char *name = resolveColor(str);
342 if (!name)
343 return COLOR_MALLOC_FAIL;
344 if (last == NULL || strcasecmp(last->name, name)) {
345 last = bsearch(name, color_lib, sizeof(color_lib) / sizeof(hsvrgbacolor_t),
346 sizeof(color_lib[0]), colorcmpf);
347 }
348 free(name);
349 if (last != NULL) {
350 switch (target_type) {
351 case HSVA_DOUBLE:
352 color->u.HSVA[0] = ((double) last->h) / 255.0;
353 color->u.HSVA[1] = ((double) last->s) / 255.0;
354 color->u.HSVA[2] = ((double) last->v) / 255.0;
355 color->u.HSVA[3] = ((double) last->a) / 255.0;
356 break;
357 case RGBA_BYTE:
358 color->u.rgba[0] = last->r;
359 color->u.rgba[1] = last->g;
360 color->u.rgba[2] = last->b;
361 color->u.rgba[3] = last->a;
362 break;
363 case RGBA_WORD:
364 color->u.rrggbbaa[0] = last->r * 65535 / 255;
365 color->u.rrggbbaa[1] = last->g * 65535 / 255;
366 color->u.rrggbbaa[2] = last->b * 65535 / 255;
367 color->u.rrggbbaa[3] = last->a * 65535 / 255;
368 break;
369 case RGBA_DOUBLE:
370 color->u.RGBA[0] = last->r / 255.0;
371 color->u.RGBA[1] = last->g / 255.0;
372 color->u.RGBA[2] = last->b / 255.0;
373 color->u.RGBA[3] = last->a / 255.0;
374 break;
375 case COLOR_STRING:
376 break;
377 case COLOR_INDEX:
378 break;
379 default:
380 UNREACHABLE();
381 }
382 return rc;
383 }
384
385 /* if we're still here then we failed to find a valid color spec */
386 rc = COLOR_UNKNOWN;
387 switch (target_type) {
388 case HSVA_DOUBLE:
389 color->u.HSVA[0] = color->u.HSVA[1] = color->u.HSVA[2] = 0.0;
390 color->u.HSVA[3] = 1.0; /* opaque */
391 break;
392 case RGBA_BYTE:
393 color->u.rgba[0] = color->u.rgba[1] = color->u.rgba[2] = 0;
394 color->u.rgba[3] = 255; /* opaque */
395 break;
396 case RGBA_WORD:
397 color->u.rrggbbaa[0] = color->u.rrggbbaa[1] = color->u.rrggbbaa[2] = 0;
398 color->u.rrggbbaa[3] = 65535; /* opaque */
399 break;
400 case RGBA_DOUBLE:
401 color->u.RGBA[0] = color->u.RGBA[1] = color->u.RGBA[2] = 0.0;
402 color->u.RGBA[3] = 1.0; /* opaque */
403 break;
404 case COLOR_STRING:
405 break;
406 case COLOR_INDEX:
407 break;
408 default:
409 UNREACHABLE();
410 }
411 return rc;
412}
413
414char *setColorScheme(const char *s) {
415 char *previous = colorscheme;
416 colorscheme = s == NULL ? NULL : gv_strdup(s);
417 return previous;
418}
static agxbuf last
last message
Definition agerror.c:29
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
hsbcolor_t color_lib[]
Definition colortbl.h:11
static int colorcmpf(const void *a0, const void *a1)
Definition colxlate.c:42
void colorxlate(char *str, agxbuf *buf)
Definition colxlate.c:48
color_type_t
Definition color.h:26
@ HSVA_DOUBLE
Definition color.h:26
@ RGBA_DOUBLE
Definition color.h:27
@ RGBA_WORD
Definition color.h:26
@ RGBA_BYTE
Definition color.h:26
@ COLOR_INDEX
Definition color.h:27
@ COLOR_STRING
Definition color.h:27
#define COLOR_OK
Definition color.h:44
#define COLOR_MALLOC_FAIL
Definition color.h:42
#define COLOR_UNKNOWN
Definition color.h:43
#define A(n, t)
Definition expr.h:76
#define S
Definition expr.h:72
#define G
Definition gdefs.h:7
#define V
Definition gdefs.h:5
void free(void *)
require define api prefix
Definition gmlparse.y:17
node NULL
Definition grammar.y:149
replacements for ctype.h functions
static char gv_tolower(int c)
Definition gv_ctype.h:81
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
Arithmetic helper functions.
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:43
static void color(Agraph_t *g)
Definition gvcolor.c:128
#define B
Definition hierarchy.c:117
agxbuf * str
Definition htmlparse.c:97
#define DFLT_SCHEME
Definition colxlate.c:181
#define ISNONDFLT(s)
Definition colxlate.c:183
static char * fullColor(agxbuf *xb, const char *prefix, const char *str)
Definition colxlate.c:144
static void hsv2rgb(double h, double s, double v, double *r, double *g, double *b)
Definition colxlate.c:35
#define DFLT_SCHEME_LEN
Definition colxlate.c:182
char * canontoken(char *str)
Definition colxlate.c:129
char * setColorScheme(const char *s)
Definition colxlate.c:414
static void rgb2hsv(double r, double g, double b, double *h, double *s, double *v)
Definition colxlate.c:91
static char * resolveColor(char *str)
Definition colxlate.c:185
static char * colorscheme
Definition colxlate.c:33
static bool on_heap(const subtree_t *tree)
is this subtree stored in an STheap?
Definition ns.c:306
static char * canon(graph_t *g, char *s)
Definition output.c:94
platform abstraction for case-insensitive string functions
struct agxbuf::@59::@60 s
union agxbuf::@59 u
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30