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