Graphviz 13.0.0~dev.20241220.2304
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 <util/agxbuf.h>
27#include <util/alloc.h>
28#include <util/gv_ctype.h>
29#include <util/gv_math.h>
30#include <util/strcasecmp.h>
31#include <util/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
129/* fullColor:
130 * Return "/prefix/str"
131 */
132static char *fullColor(agxbuf *xb, const char *prefix, const char *str) {
133 agxbprint(xb, "/%s/%s", prefix, str);
134 return agxbuse(xb);
135}
136
137/* resolveColor:
138 * Resolve input color str allowing color scheme namespaces.
139 * 0) "black" => "black"
140 * "white" => "white"
141 * "lightgrey" => "lightgrey"
142 * NB: This is something of a hack due to the remaining codegen.
143 * Once these are gone, this case could be removed and all references
144 * to "black" could be replaced by "/X11/black".
145 * 1) No initial / =>
146 * if colorscheme is defined and no "X11", return /colorscheme/str
147 * else return str
148 * 2) One initial / => return str+1
149 * 3) Two initial /'s =>
150 * a) If colorscheme is defined and not "X11", return /colorscheme/(str+2)
151 * b) else return (str+2)
152 * 4) Two /'s, not both initial => return str.
153 *
154 * Note that 1), 2), and 3b) allow the default X11 color scheme.
155 *
156 * In other words,
157 * xxx => /colorscheme/xxx if colorscheme is defined and not "X11"
158 * xxx => xxx otherwise
159 * /xxx => xxx
160 * /X11/yyy => yyy
161 * /xxx/yyy => /xxx/yyy
162 * //yyy => /colorscheme/yyy if colorscheme is defined and not "X11"
163 * //yyy => yyy otherwise
164 *
165 * At present, no other error checking is done. For example,
166 * yyy could be "". This will be caught later.
167 */
168
169#define DFLT_SCHEME "X11/" /* Must have final '/' */
170#define DFLT_SCHEME_LEN ((sizeof(DFLT_SCHEME)-1)/sizeof(char))
171#define ISNONDFLT(s) ((s) && *(s) && strncasecmp(DFLT_SCHEME, s, DFLT_SCHEME_LEN-1))
172
173static char *resolveColor(const char *str) {
174 const char *s;
175
176 if (!strcmp(str, "black")) return strdup(str);
177 if (!strcmp(str, "white")) return strdup(str);
178 if (!strcmp(str, "lightgrey")) return strdup(str);
179 agxbuf xb = {0};
180 if (*str == '/') { /* if begins with '/' */
181 const char *const c2 = str + 1; // second char
182 const char *const ss = strchr(c2, '/'); // second slash
183 if (ss != NULL) { // if has second '/'
184 if (*c2 == '/') { /* if second '/' is second character */
185 /* Do not compare against final '/' */
187 s = fullColor(&xb, colorscheme, c2+1);
188 else
189 s = c2+1;
190 }
191 else if (strncasecmp(DFLT_SCHEME, c2, DFLT_SCHEME_LEN)) s = str;
192 else s = ss + 1;
193 }
194 else s = c2;
195 }
196 else if (ISNONDFLT(colorscheme)) s = fullColor(&xb, colorscheme, str);
197 else s = str;
198 char *on_heap = strdup(s);
199 agxbfree(&xb);
200 return on_heap;
201}
202
203int colorxlate(const char *str, gvcolor_t *color, color_type_t target_type) {
204 char c;
205 double H, S, V, A, R, G, B;
206 unsigned int r, g, b;
207
208 color->type = target_type;
209
210 int rc = COLOR_OK;
211 for (; *str == ' '; str++); /* skip over any leading whitespace */
212 const char *p = str;
213
214 /* test for rgb value such as: "#ff0000"
215 or rgba value such as "#ff000080" */
216 unsigned a = 255; // default alpha channel value=opaque in case not supplied
217 bool is_rgb = sscanf(p, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3;
218 if (!is_rgb) { // try 3 letter form
219 is_rgb = strlen(p) == 4 && sscanf(p, "#%1x%1x%1x", &r, &g, &b) == 3;
220 if (is_rgb) {
221 r |= r << 4;
222 g |= g << 4;
223 b |= b << 4;
224 }
225 }
226 if (is_rgb) {
227 switch (target_type) {
228 case HSVA_DOUBLE:
229 R = (double) r / 255.0;
230 G = (double) g / 255.0;
231 B = (double) b / 255.0;
232 A = (double) a / 255.0;
233 rgb2hsv(R, G, B, &H, &S, &V);
234 color->u.HSVA[0] = H;
235 color->u.HSVA[1] = S;
236 color->u.HSVA[2] = V;
237 color->u.HSVA[3] = A;
238 break;
239 case RGBA_BYTE:
240 color->u.rgba[0] = (unsigned char)r;
241 color->u.rgba[1] = (unsigned char)g;
242 color->u.rgba[2] = (unsigned char)b;
243 color->u.rgba[3] = (unsigned char)a;
244 break;
245 case RGBA_WORD:
246 color->u.rrggbbaa[0] = (int)(r * 65535 / 255);
247 color->u.rrggbbaa[1] = (int)(g * 65535 / 255);
248 color->u.rrggbbaa[2] = (int)(b * 65535 / 255);
249 color->u.rrggbbaa[3] = (int)(a * 65535 / 255);
250 break;
251 case RGBA_DOUBLE:
252 color->u.RGBA[0] = (double) r / 255.0;
253 color->u.RGBA[1] = (double) g / 255.0;
254 color->u.RGBA[2] = (double) b / 255.0;
255 color->u.RGBA[3] = (double) a / 255.0;
256 break;
257 case COLOR_STRING:
258 break;
259 case COLOR_INDEX:
260 break;
261 default:
262 UNREACHABLE();
263 }
264 return rc;
265 }
266
267 /* test for hsv value such as: ".6,.5,.3" */
268 if ((c = *p) == '.' || gv_isdigit(c)) {
269 agxbuf canon = {0};
270 while ((c = *p++)) {
271 agxbputc(&canon, c == ',' ? ' ' : c);
272 }
273
274 A = 1.0; // default
275 if (sscanf(agxbuse(&canon), "%lf%lf%lf%lf", &H, &S, &V, &A) >= 3) {
276 /* clip to reasonable values */
277 H = fmax(fmin(H, 1.0), 0.0);
278 S = fmax(fmin(S, 1.0), 0.0);
279 V = fmax(fmin(V, 1.0), 0.0);
280 A = fmax(fmin(A, 1.0), 0.0);
281 switch (target_type) {
282 case HSVA_DOUBLE:
283 color->u.HSVA[0] = H;
284 color->u.HSVA[1] = S;
285 color->u.HSVA[2] = V;
286 color->u.HSVA[3] = A;
287 break;
288 case RGBA_BYTE:
289 hsv2rgb(H, S, V, &R, &G, &B);
290 color->u.rgba[0] = (unsigned char)(R * 255);
291 color->u.rgba[1] = (unsigned char)(G * 255);
292 color->u.rgba[2] = (unsigned char)(B * 255);
293 color->u.rgba[3] = (unsigned char)(A * 255);
294 break;
295 case RGBA_WORD:
296 hsv2rgb(H, S, V, &R, &G, &B);
297 color->u.rrggbbaa[0] = (int) (R * 65535);
298 color->u.rrggbbaa[1] = (int) (G * 65535);
299 color->u.rrggbbaa[2] = (int) (B * 65535);
300 color->u.rrggbbaa[3] = (int) (A * 65535);
301 break;
302 case RGBA_DOUBLE:
303 hsv2rgb(H, S, V, &R, &G, &B);
304 color->u.RGBA[0] = R;
305 color->u.RGBA[1] = G;
306 color->u.RGBA[2] = B;
307 color->u.RGBA[3] = A;
308 break;
309 case COLOR_STRING:
310 break;
311 case COLOR_INDEX:
312 break;
313 default:
314 UNREACHABLE();
315 }
316 agxbfree(&canon);
317 return rc;
318 }
319 agxbfree(&canon);
320 }
321
322 /* test for known color name (generic, not renderer specific known names) */
323 char *name = resolveColor(str);
324 if (!name)
325 return COLOR_MALLOC_FAIL;
326 const hsvrgbacolor_t *known = bsearch(name, color_lib,
327 sizeof(color_lib) / sizeof(hsvrgbacolor_t),
328 sizeof(color_lib[0]), colorcmpf);
329 free(name);
330 if (known != NULL) {
331 switch (target_type) {
332 case HSVA_DOUBLE:
333 color->u.HSVA[0] = (double)known->h / 255.0;
334 color->u.HSVA[1] = (double)known->s / 255.0;
335 color->u.HSVA[2] = (double)known->v / 255.0;
336 color->u.HSVA[3] = (double)known->a / 255.0;
337 break;
338 case RGBA_BYTE:
339 color->u.rgba[0] = known->r;
340 color->u.rgba[1] = known->g;
341 color->u.rgba[2] = known->b;
342 color->u.rgba[3] = known->a;
343 break;
344 case RGBA_WORD:
345 color->u.rrggbbaa[0] = known->r * 65535 / 255;
346 color->u.rrggbbaa[1] = known->g * 65535 / 255;
347 color->u.rrggbbaa[2] = known->b * 65535 / 255;
348 color->u.rrggbbaa[3] = known->a * 65535 / 255;
349 break;
350 case RGBA_DOUBLE:
351 color->u.RGBA[0] = known->r / 255.0;
352 color->u.RGBA[1] = known->g / 255.0;
353 color->u.RGBA[2] = known->b / 255.0;
354 color->u.RGBA[3] = known->a / 255.0;
355 break;
356 case COLOR_STRING:
357 break;
358 case COLOR_INDEX:
359 break;
360 default:
361 UNREACHABLE();
362 }
363 return rc;
364 }
365
366 /* if we're still here then we failed to find a valid color spec */
367 rc = COLOR_UNKNOWN;
368 switch (target_type) {
369 case HSVA_DOUBLE:
370 color->u.HSVA[0] = color->u.HSVA[1] = color->u.HSVA[2] = 0.0;
371 color->u.HSVA[3] = 1.0; /* opaque */
372 break;
373 case RGBA_BYTE:
374 color->u.rgba[0] = color->u.rgba[1] = color->u.rgba[2] = 0;
375 color->u.rgba[3] = 255; /* opaque */
376 break;
377 case RGBA_WORD:
378 color->u.rrggbbaa[0] = color->u.rrggbbaa[1] = color->u.rrggbbaa[2] = 0;
379 color->u.rrggbbaa[3] = 65535; /* opaque */
380 break;
381 case RGBA_DOUBLE:
382 color->u.RGBA[0] = color->u.RGBA[1] = color->u.RGBA[2] = 0.0;
383 color->u.RGBA[3] = 1.0; /* opaque */
384 break;
385 case COLOR_STRING:
386 break;
387 case COLOR_INDEX:
388 break;
389 default:
390 UNREACHABLE();
391 }
392 return rc;
393}
394
395char *setColorScheme(const char *s) {
396 char *previous = colorscheme;
397 colorscheme = s == NULL ? NULL : gv_strdup(s);
398 return previous;
399}
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:277
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:40
void colorxlate(char *str, agxbuf *buf)
Definition colxlate.c:46
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:163
replacements for ctype.h functions
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:129
#define B
Definition hierarchy.c:117
textitem scanner parser str
Definition htmlparse.y:224
static char * resolveColor(const char *str)
Definition colxlate.c:173
#define DFLT_SCHEME
Definition colxlate.c:169
#define ISNONDFLT(s)
Definition colxlate.c:171
static char * fullColor(agxbuf *xb, const char *prefix, const char *str)
Definition colxlate.c:132
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:170
char * setColorScheme(const char *s)
Definition colxlate.c:395
static void rgb2hsv(double r, double g, double b, double *h, double *s, double *v)
Definition colxlate.c:91
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:310
static char * canon(graph_t *g, char *s)
Definition output.c:97
platform abstraction for case-insensitive string functions
unsigned char h
Definition color.h:21
unsigned char a
Definition color.h:22
unsigned char b
Definition color.h:22
unsigned char g
Definition color.h:22
unsigned char s
Definition color.h:21
unsigned char v
Definition color.h:21
unsigned char r
Definition color.h:22
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30