From 6b520d8f00f7a505c0ea3ef04d87374523169644 Mon Sep 17 00:00:00 2001 From: "Emden R. Gansner" Date: Tue, 6 Aug 2013 15:44:42 -0400 Subject: [PATCH] Update xdot library to handle gradient fill --- lib/xdot/xdot.3 | 66 +++++++++- lib/xdot/xdot.c | 321 +++++++++++++++++++++++++++++++++++++++++++++--- lib/xdot/xdot.h | 44 ++++++- 3 files changed, 411 insertions(+), 20 deletions(-) diff --git a/lib/xdot/xdot.3 b/lib/xdot/xdot.3 index 56b798995..fb2668375 100644 --- a/lib/xdot/xdot.3 +++ b/lib/xdot/xdot.3 @@ -8,6 +8,40 @@ \f5 #include +typedef enum { + xd_none, + xd_linear, + xd_radial +} xdot_grad_type; + +typedef struct { + float frac; + char* color; +} xdot_color_stop; + +typedef struct { + double x0, y0; + double x1, y1; + int n_stops; + xdot_color_stop* stops; +} xdot_linear_grad; + +typedef struct { + double x0, y0, r0; + double x1, y1, r1; + int n_stops; + xdot_color_stop* stops; +} xdot_radial_grad; + +typedef struct { + xdot_grad_type type; + union { + char* clr; + xdot_linear_grad ling; + xdot_radial_grad ring; + } u; +} xdot_color; + typedef enum { xd_left, xd_center, xd_right } xdot_align; @@ -47,7 +81,8 @@ typedef enum { xd_filled_polygon, xd_unfilled_polygon, xd_filled_bezier, xd_unfilled_bezier, xd_polyline, xd_text, - xd_fill_color, xd_pen_color, xd_font, xd_style, xd_image + xd_fill_color, xd_pen_color, xd_font, xd_style, xd_image, + xd_grad_fill_color, xd_grad_pen_color } xdot_kind; typedef enum { @@ -55,7 +90,8 @@ typedef enum { xop_polygon, xop_bezier, xop_polyline, xop_text, - xop_fill_color, xop_pen_color, xop_font, xop_style, xop_image + xop_fill_color, xop_pen_color, xop_font, xop_style, xop_image, + xop_grad_fill_color, xop_grad_pen_color } xop_kind; typedef struct _xdot_op xdot_op; @@ -72,6 +108,7 @@ struct _xdot_op { xdot_text text; /* xd_text */ xdot_image image; /* xd_image */ char* color; /* xd_fill_color, xd_pen_color */ + xdot_color grad_color; /* xd_grad_fill_color, xd_grad_pen_color */ xdot_font font; /* xd_font */ char* style; /* xd_style */ } u; @@ -93,6 +130,10 @@ xdot* parseXDot (char*); char* sprintXDot (xdot*); void fprintXDot (FILE*, xdot*); void freeXDot (xdot*); + +xdot_grad_type colorType (char*); +xdot_color* parseXDotColor (char*); +void freeXDotColor (xdot_color*); \fP .fi .SH DESCRIPTION @@ -119,7 +160,7 @@ a given \fIkind\fP indicated by the comments. The \fIdrawfunc\fP field allows the user to attach a drawing-specific function to the operation, providing an object-based interface. These functions can be automatically attached during parsing by providing a -non-NULL second argument to \fIparseXDotF\fP. +non-NULL second argument to \fBparseXDotF\fP. .PP .SS " xop_kind" This type provides an enumeration of the allowed xdot operations. @@ -193,6 +234,25 @@ These two functions deparse the argument xdot structure, producing a string representation. \fIfprintXDot\fP writes the output onto the open stream \fIfp\fP; \fIsprintXDot\fP returns a heap-allocated string. +.PP +The color string with fill and draw operations can encode linear +and radial gradients. These values are parsed automatically by +\fBparseXDotF\fP or \fBparseXDot\fP, +with \fIxdot_op\fP having kind \fIxd_grad_pen_color\fP or +\fIxd_grad_fill_color\fP and the value is stored in \fIgrad_color\fP. +.PP +For an application that handles its own parsing of xdot, the library +provides three helper functions. +.PP +.SS " xdot_grad_type colorTypeXDot (char *str)" +returns the color type described by the input string. +.PP +.SS " char* parseXDotColor (char *str, xdot_color* clr)" +attempts to parse the string \fIstr\fP as a color value, storing +the result in \fIclr\fP. It returns NULL on failure. +.PP +.SS " void freeXDotColor (xdot_color* cp)" +This frees the resources associated with a value of type \fIxdot_color\fP. .SH BUGS Although some small checking is done on the \fIsz\fP argument to diff --git a/lib/xdot/xdot.c b/lib/xdot/xdot.c index e0217943b..f7f7a5f10 100755 --- a/lib/xdot/xdot.c +++ b/lib/xdot/xdot.c @@ -1,4 +1,3 @@ -/* $Id$Revision: */ /* vim:set shiftwidth=4 ts=8: */ /************************************************************************* @@ -247,6 +246,9 @@ static char *parseAlign(char *s, xdot_align * ap) static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error) { + char* cs; + xdot_color clr; + *error = 0; while (isspace(*s)) s++; @@ -300,19 +302,41 @@ static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error) break; case 'c': - op->kind = xd_pen_color; - s = parseString(s, &op->u.color); + s = parseString(s, &cs); CHK(s); - if (ops) - op->drawfunc = ops[xop_pen_color]; + cs = parseXDotColor (cs, &clr); + CHK(cs); + if (clr.type == xd_none) { + op->kind = xd_pen_color; + op->u.color = clr.u.clr; + if (ops) + op->drawfunc = ops[xop_pen_color]; + } + else { + op->kind = xd_grad_pen_color; + op->u.grad_color = clr; + if (ops) + op->drawfunc = ops[xop_grad_pen_color]; + } break; case 'C': - op->kind = xd_fill_color; - s = parseString(s, &op->u.color); + s = parseString(s, &cs); CHK(s); - if (ops) - op->drawfunc = ops[xop_fill_color]; + cs = parseXDotColor (cs, &clr); + CHK(cs); + if (clr.type == xd_none) { + op->kind = xd_fill_color; + op->u.color = clr.u.clr; + if (ops) + op->drawfunc = ops[xop_fill_color]; + } + else { + op->kind = xd_grad_fill_color; + op->u.grad_color = clr; + if (ops) + op->drawfunc = ops[xop_grad_fill_color]; + } break; case 'L': @@ -456,11 +480,41 @@ xdot *parseXDot(char *s) typedef void (*pf) (char *, void *); +/* trim: + * Trailing zeros are removed and decimal point, if possible. + */ +static void trim (char* buf) +{ + char* dotp; + char* p; + + if ((dotp = strchr (buf,'.'))) { + p = dotp+1; + while (*p) p++; // find end of string + p--; + while (*p == '0') *p-- = '\0'; + if (*p == '.') // If all decimals were zeros, remove ".". + *p = '\0'; + else + p++; + } +} + static void printRect(xdot_rect * r, pf print, void *info) { char buf[128]; - sprintf(buf, " %.06f %.06f %.06f %.06f", r->x, r->y, r->w, r->h); + sprintf(buf, " %.02f", r->x); + trim(buf); + print(buf, info); + sprintf(buf, " %.02f", r->y); + trim(buf); + print(buf, info); + sprintf(buf, " %.02f", r->w); + trim(buf); + print(buf, info); + sprintf(buf, " %.02f", r->h); + trim(buf); print(buf, info); } @@ -472,7 +526,11 @@ static void printPolyline(xdot_polyline * p, pf print, void *info) sprintf(buf, " %d", p->cnt); print(buf, info); for (i = 0; i < p->cnt; i++) { - sprintf(buf, " %.06f %.06f", p->pts[i].x, p->pts[i].y); + sprintf(buf, " %.02f", p->pts[i].x); + trim(buf); + print(buf, info); + sprintf(buf, " %.02f", p->pts[i].y); + trim(buf); print(buf, info); } } @@ -494,11 +552,15 @@ static void printInt(int i, pf print, void *info) print(buf, info); } -static void printFloat(float f, pf print, void *info) +static void printFloat(float f, pf print, void *info, int space) { char buf[128]; - sprintf(buf, " %f", f); + if (space) + sprintf(buf, " %.02f", f); + else + sprintf(buf, "%.02f", f); + trim (buf); print(buf, info); } @@ -517,10 +579,58 @@ static void printAlign(xdot_align a, pf print, void *info) } } +static void +gradprint (char* s, void* v) +{ + agxbput(s, (agxbuf*)v); +} + +static void +toGradString (agxbuf* xb, xdot_color* cp) +{ + int i, n_stops; + xdot_color_stop* stops; + + if (cp->type == xd_linear) { + agxbputc (xb, '['); + printFloat (cp->u.ling.x0, gradprint, xb, 0); + printFloat (cp->u.ling.y0, gradprint, xb, 1); + printFloat (cp->u.ling.x1, gradprint, xb, 1); + printFloat (cp->u.ling.y1, gradprint, xb, 1); + n_stops = cp->u.ling.n_stops; + stops = cp->u.ling.stops; + } + else { + agxbputc (xb, '('); + printFloat (cp->u.ring.x0, gradprint, xb, 0); + printFloat (cp->u.ring.y0, gradprint, xb, 1); + printFloat (cp->u.ring.r0, gradprint, xb, 1); + printFloat (cp->u.ring.x1, gradprint, xb, 1); + printFloat (cp->u.ring.y1, gradprint, xb, 1); + printFloat (cp->u.ring.r1, gradprint, xb, 1); + n_stops = cp->u.ring.n_stops; + stops = cp->u.ring.stops; + } + printInt (n_stops, gradprint, xb); + for (i = 0; i < n_stops; i++) { + printFloat (stops[i].frac, gradprint, xb, 1); + printString (stops[i].color, gradprint, xb); + } + + if (cp->type == xd_linear) + agxbputc (xb, ']'); + else + agxbputc (xb, ')'); +} + typedef void (*print_op)(xdot_op * op, pf print, void *info, int more); static void printXDot_Op(xdot_op * op, pf print, void *info, int more) { + agxbuf xb; + unsigned char buf[BUFSIZ]; + + agxbinit (&xb, BUFSIZ, buf); switch (op->kind) { case xd_filled_ellipse: print("E", info); @@ -551,10 +661,20 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more) print("c", info); printString(op->u.color, print, info); break; + case xd_grad_pen_color: + print("c", info); + toGradString (&xb, &op->u.grad_color); + printString(agxbuse(&xb), print, info); + break; case xd_fill_color: print("C", info); printString(op->u.color, print, info); break; + case xd_grad_fill_color: + print("C", info); + toGradString (&xb, &op->u.grad_color); + printString(agxbuse(&xb), print, info); + break; case xd_polyline: print("L", info); printPolyline(&op->u.polyline, print, info); @@ -570,7 +690,7 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more) case xd_font: print("F", info); op->kind = xd_font; - printFloat(op->u.font.size, print, info); + printFloat(op->u.font.size, print, info, 1); printString(op->u.font.name, print, info); break; case xd_style: @@ -585,6 +705,7 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more) } if (more) print(" ", info); + agxbfree (&xb); } static void jsonRect(xdot_rect * r, pf print, void *info) @@ -629,6 +750,10 @@ static void jsonString(char *p, pf print, void *info) static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more) { + agxbuf xb; + unsigned char buf[BUFSIZ]; + + agxbinit (&xb, BUFSIZ, buf); switch (op->kind) { case xd_filled_ellipse: print("{E : ", info); @@ -658,10 +783,20 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more) print("{c : ", info); jsonString(op->u.color, print, info); break; + case xd_grad_pen_color: + print("{c : ", info); + toGradString (&xb, &op->u.grad_color); + jsonString(agxbuse(&xb), print, info); + break; case xd_fill_color: print("{C : ", info); jsonString(op->u.color, print, info); break; + case xd_grad_fill_color: + print("{C : ", info); + toGradString (&xb, &op->u.grad_color); + jsonString(agxbuse(&xb), print, info); + break; case xd_polyline: print("{L :", info); jsonPolyline(&op->u.polyline, print, info); @@ -682,7 +817,7 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more) case xd_font: print("{F : [", info); op->kind = xd_font; - printFloat(op->u.font.size, print, info); + printFloat(op->u.font.size, print, info, 1); print(",", info); jsonString(op->u.font.name, print, info); print("]", info); @@ -703,6 +838,7 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more) print("},\n", info); else print("}\n", info); + agxbfree (&xb); } static void _printXDot(xdot * x, pf print, void *info, print_op ofn) @@ -762,6 +898,10 @@ static void freeXOpData(xdot_op * x) case xd_pen_color: free(x->u.color); break; + case xd_grad_fill_color: + case xd_grad_pen_color: + freeXDotColor (&x->u.grad_color); + break; case xd_font: free(x->u.font.name); break; @@ -849,6 +989,157 @@ int statXDot (xdot* x, xdot_stats* sp) return 0; } +xdot_grad_type +colorType (char* cp) +{ + xdot_grad_type rv; + + switch (*cp) { + case '[' : + rv = xd_linear; + break; + case '(' : + rv = xd_radial; + break; + default : + rv = xd_none; + break; + } + return rv; +} + +#define CHK1(s) if(!s){free(stops);return NULL;} + +/* radGradient: + * Parse radial gradient spec + * Return NULL on failure. + */ +static char* +radGradient (char* cp, xdot_color* clr) +{ + char* s = cp; + int i; + double d; + xdot_color_stop* stops = NULL; + + clr->type = xd_radial; + s = parseReal(s, &clr->u.ring.x0); + CHK1(s); + s = parseReal(s, &clr->u.ring.y0); + CHK1(s); + s = parseReal(s, &clr->u.ring.r0); + CHK1(s); + s = parseReal(s, &clr->u.ring.x1); + CHK1(s); + s = parseReal(s, &clr->u.ring.y1); + CHK1(s); + s = parseReal(s, &clr->u.ring.r1); + CHK1(s); + s = parseInt(s, &clr->u.ring.n_stops); + CHK1(s); + + stops = N_NEW(clr->u.ring.n_stops,xdot_color_stop); + for (i = 0; i < clr->u.ring.n_stops; i++) { + s = parseReal(s, &d); + CHK1(s); + stops[i].frac = d; + s = parseString(s, &stops[i].color); + CHK1(s); + } + clr->u.ring.stops = stops; + + return cp; +} + +/* linGradient: + * Parse linear gradient spec + * Return NULL on failure. + */ +static char* +linGradient (char* cp, xdot_color* clr) +{ + char* s = cp; + int i; + double d; + xdot_color_stop* stops = NULL; + + clr->type = xd_linear; + s = parseReal(s, &clr->u.ling.x0); + CHK1(s); + s = parseReal(s, &clr->u.ling.y0); + CHK1(s); + s = parseReal(s, &clr->u.ling.x1); + CHK1(s); + s = parseReal(s, &clr->u.ling.y1); + CHK1(s); + s = parseInt(s, &clr->u.ling.n_stops); + CHK1(s); + + stops = N_NEW(clr->u.ling.n_stops,xdot_color_stop); + for (i = 0; i < clr->u.ling.n_stops; i++) { + s = parseReal(s, &d); + CHK1(s); + stops[i].frac = d; + s = parseString(s, &stops[i].color); + CHK1(s); + } + clr->u.ling.stops = stops; + + return cp; +} + +/* parseXDotColor: + * Parse xdot color spec: ordinary or gradient + * The result is stored in clr. + * Return NULL on failure. + */ +char* +parseXDotColor (char* cp, xdot_color* clr) +{ + char c = *cp; + + switch (c) { + case '[' : + return linGradient (cp+1, clr); + break; + case '(' : + return radGradient (cp+1, clr); + break; + case '#' : + case '/' : + clr->type = xd_none; + clr->u.clr = cp; + return cp; + break; + default : + if (isalnum(c)) { + clr->type = xd_none; + clr->u.clr = cp; + return cp; + } + else + return NULL; + } +} + +void freeXDotColor (xdot_color* cp) +{ + int i; + + if (cp->type == xd_linear) { + for (i = 0; i < cp->u.ling.n_stops; i++) { + free (cp->u.ling.stops[i].color); + } + free (cp->u.ling.stops); + } + else if (cp->type == xd_radial) { + for (i = 0; i < cp->u.ring.n_stops; i++) { + free (cp->u.ring.stops[i].color); + } + free (cp->u.ring.stops); + } +} + #if 0 static void execOp(xdot_op * op, int param) { diff --git a/lib/xdot/xdot.h b/lib/xdot/xdot.h index dec2c64a4..e9e89c2b9 100755 --- a/lib/xdot/xdot.h +++ b/lib/xdot/xdot.h @@ -18,6 +18,40 @@ #endif #define INITIAL_XDOT_CAPACITY 512 +typedef enum { + xd_none, + xd_linear, + xd_radial +} xdot_grad_type; + +typedef struct { + float frac; + char* color; +} xdot_color_stop; + +typedef struct { + double x0, y0; + double x1, y1; + int n_stops; + xdot_color_stop* stops; +} xdot_linear_grad; + +typedef struct { + double x0, y0, r0; + double x1, y1, r1; + int n_stops; + xdot_color_stop* stops; +} xdot_radial_grad; + +typedef struct { + xdot_grad_type type; + union { + char* clr; + xdot_linear_grad ling; + xdot_radial_grad ring; + } u; +} xdot_color; + typedef enum { xd_left, xd_center, xd_right } xdot_align; @@ -57,7 +91,8 @@ typedef enum { xd_filled_polygon, xd_unfilled_polygon, xd_filled_bezier, xd_unfilled_bezier, xd_polyline, xd_text, - xd_fill_color, xd_pen_color, xd_font, xd_style, xd_image + xd_fill_color, xd_pen_color, xd_font, xd_style, xd_image, + xd_grad_fill_color, xd_grad_pen_color } xdot_kind; typedef enum { @@ -65,7 +100,8 @@ typedef enum { xop_polygon, xop_bezier, xop_polyline, xop_text, - xop_fill_color, xop_pen_color, xop_font, xop_style, xop_image + xop_fill_color, xop_pen_color, xop_font, xop_style, xop_image, + xop_grad_fill_color, xop_grad_pen_color } xop_kind; typedef struct _xdot_op xdot_op; @@ -82,6 +118,7 @@ struct _xdot_op { xdot_text text; /* xd_text */ xdot_image image; /* xd_image */ char* color; /* xd_fill_color, xd_pen_color */ + xdot_color grad_color; /* xd_grad_fill_color, xd_grad_pen_color */ xdot_font font; /* xd_font */ char* style; /* xd_style */ } u; @@ -123,5 +160,8 @@ extern void fprintXDot (FILE*, xdot*); extern void jsonXDot (FILE*, xdot*); extern void freeXDot (xdot*); extern int statXDot (xdot*, xdot_stats*); +extern xdot_grad_type colorTypeXDot (char*); +extern char* parseXDotColor (char* cp, xdot_color* clr); +extern void freeXDotColor (xdot_color*); #endif -- 2.40.0