From: Dwight Perry Date: Tue, 10 Jan 2012 15:38:11 +0000 (-0500) Subject: Added gradient fill feature X-Git-Tag: LAST_LIBGRAPH~32^2~574 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2f6d5b337fb2032e2dc2bfd64e33e87ce307c88c;p=graphviz Added gradient fill feature --- diff --git a/lib/common/const.h b/lib/common/const.h index befff90e9..cdf45fba4 100644 --- a/lib/common/const.h +++ b/lib/common/const.h @@ -83,6 +83,7 @@ #define DEFAULT_RANKSEP 0.5 #define MIN_RANKSEP 0.02 + /* default margin for paged formats such as PostScript - in points = 0.5in */ #define DEFAULT_PRINT_MARGIN 36 /* default margin for embedded formats such as PNG - in points */ @@ -210,6 +211,8 @@ #define FOLDER (1 << 7) #define BOX3D (1 << 8) #define COMPONENT (1 << 9) +#define GRADIENT (1 << 10) +#define RGRADIENT GRADIENT + 1 /*radial gradient */ /* label types */ #define LT_NONE (0 << 1) diff --git a/lib/common/emit.c b/lib/common/emit.c index 1eed99757..a77ac0eab 100644 --- a/lib/common/emit.c +++ b/lib/common/emit.c @@ -31,6 +31,7 @@ #define P2RECT(p, pr, sx, sy) (pr[0].x = p.x - sx, pr[0].y = p.y - sy, pr[1].x = p.x + sx, pr[1].y = p.y + sy) #define FUZZ 3 #define EPSILON .0001 +#define GR_SZ 50 typedef struct { xdot_op op; @@ -90,6 +91,9 @@ obj_state_t* push_obj_state(GVJ_t *job) obj->pen = parent->pen; obj->fill = parent->fill; obj->penwidth = parent->penwidth; + obj->gradient.angle = parent->gradient.angle; + obj->gradient.startcolor = parent->gradient.startcolor; + obj->gradient.stopcolor = parent->gradient.stopcolor; } else { /* obj->pencolor = NULL */ @@ -1013,8 +1017,8 @@ static void emit_xdot (GVJ_t * job, xdot* xd) static void emit_background(GVJ_t * job, graph_t *g) { xdot* xd; - char *str; - int dfltColor; + char *str,*gcolor,*style; + int dfltColor,filltype; /* if no bgcolor specified - first assume default of "white" */ if (! ((str = agget(g, "bgcolor")) && str[0])) { @@ -1023,6 +1027,19 @@ static void emit_background(GVJ_t * job, graph_t *g) } else dfltColor = 0; + + if (((style = agget(g, "style")) && str[0])) { + if(strcmp(style,"linear") == 0) + filltype = GRADIENT; + else if(strcmp(style,"radial") == 0) + filltype = RGRADIENT; + else + filltype = 0; + } + + if ( filltype > 0 && ((gcolor = agget(g, "gradientcolor")) && str[0])) { + gvrender_set_gradient(job,g,G_gradientcolor,G_gradientangle); + } /* if device has no truecolor support, change "transparent" to "white" */ if (! (job->flags & GVDEVICE_DOES_TRUECOLOR) && (streq(str, "transparent"))) @@ -1033,7 +1050,10 @@ static void emit_background(GVJ_t * job, graph_t *g) || ((job->flags & GVRENDER_NO_WHITE_BG) && dfltColor))) { gvrender_set_fillcolor(job, str); gvrender_set_pencolor(job, str); - gvrender_box(job, job->clip, TRUE); /* filled */ + if(filltype > 0) + gvrender_box(job, job->clip,filltype); /* linear or radial gradient */ + else + gvrender_box(job, job->clip, TRUE); /* filled */ } if ((xd = (xdot*)GD_drawing(g)->xdots)) @@ -2813,6 +2833,7 @@ static void emit_colors(GVJ_t * job, graph_t * g) gvrender_set_fillcolor(job, str); if (((str = agget(g, "fontcolor")) != 0) && str[0]) gvrender_set_pencolor(job, str); + emit_cluster_colors(job, g); for (n = agfstnode(g); n; n = agnxtnode(g, n)) { if (((str = agget(n, "color")) != 0) && str[0]) @@ -3098,7 +3119,15 @@ static char **checkClusterStyle(graph_t* sg, int *flagp) if (strcmp(p, "filled") == 0) { istyle |= FILLED; pp++; - } else if (strcmp(p, "rounded") == 0) { + } + else if (strcmp(p, "linear") == 0) { + istyle |= GRADIENT; + pp++; + } + if (strcmp(p, "radial") == 0) { + istyle |= (RGRADIENT); + pp++; + }else if (strcmp(p, "rounded") == 0) { istyle |= ROUNDED; qp = pp; /* remove rounded from list passed to renderer */ do { @@ -3204,6 +3233,11 @@ void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) fillcolor = color; filled = TRUE; } + if (istyle & GRADIENT) { //handles both linear and radial gradients + gvrender_set_gradient(job,sg,G_gradientcolor,G_gradientangle); + filled = FALSE; + } + } if (!pencolor) pencolor = DEFAULT_COLOR; if (!fillcolor) fillcolor = DEFAULT_FILL; @@ -3231,8 +3265,14 @@ void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) else { gvrender_set_pencolor(job, pencolor); gvrender_set_fillcolor(job, fillcolor); - if (late_int(sg, G_peripheries, 1, 0)) + if (late_int(sg, G_peripheries, 1, 0)){ + if (istyle & GRADIENT) + gvrender_box(job, GD_bb(sg), istyle); + else gvrender_box(job, GD_bb(sg), filled); + } + else if (istyle & GRADIENT) + gvrender_box(job, GD_bb(sg), istyle); else if (filled) { if (fillcolor && fillcolor != pencolor) gvrender_set_pencolor(job, fillcolor); diff --git a/lib/common/globals.h b/lib/common/globals.h index af4f05405..121539cdb 100644 --- a/lib/common/globals.h +++ b/lib/common/globals.h @@ -98,7 +98,8 @@ extern "C" { *G_selectedpencolor, *G_selectedfillcolor, *G_visitedpencolor, *G_visitedfillcolor, *G_deletedpencolor, *G_deletedfillcolor, - *G_ordering, *G_peripheries, *G_penwidth; + *G_ordering, *G_peripheries, *G_penwidth, + *G_gradientcolor,*G_gradientangle; EXTERN attrsym_t *N_height, *N_width, *N_shape, *N_color, *N_fillcolor, *N_activepencolor, *N_activefillcolor, @@ -110,7 +111,7 @@ extern "C" { *N_sides, *N_peripheries, *N_ordering, *N_orientation, *N_skew, *N_distortion, *N_fixed, *N_imagescale, *N_layer, *N_group, *N_comment, *N_vertices, *N_z, - *N_penwidth; + *N_penwidth,*N_gradientcolor, *N_gradientangle; EXTERN attrsym_t *E_weight, *E_minlen, *E_color, *E_activepencolor, *E_activefillcolor, diff --git a/lib/common/htmllex.c b/lib/common/htmllex.c index c044137ee..1d6a63f07 100644 --- a/lib/common/htmllex.c +++ b/lib/common/htmllex.c @@ -160,6 +160,19 @@ static int idfn(htmldata_t * p, char *v) return 0; } +static int gradientfn(htmldata_t * p, char *v) +{ + p->gradient = strdup(v); + return 0; +} + +static int gradientcolorfn(htmldata_t * p, char *v) +{ + p->gradientcolor = strdup(v); + return 0; +} + + /* doInt: * Scan v for integral value. Check that * the value is >= min and <= max. Return value in ul. @@ -434,6 +447,8 @@ static attr_item tbl_items[] = { {"color", (attrFn) pencolorfn}, {"columns", (attrFn) columnsfn}, {"fixedsize", (attrFn) fixedsizefn}, + {"gradient", (attrFn) gradientfn}, + {"gradientcolor", (attrFn) gradientcolorfn}, {"height", (attrFn) heightfn}, {"href", (attrFn) hreffn}, {"id", (attrFn) idfn}, @@ -457,6 +472,8 @@ static attr_item cell_items[] = { {"color", (attrFn) pencolorfn}, {"colspan", (attrFn) colspanfn}, {"fixedsize", (attrFn) fixedsizefn}, + {"gradient", (attrFn) gradientfn}, + {"gradientcolor", (attrFn) gradientcolorfn}, {"height", (attrFn) heightfn}, {"href", (attrFn) hreffn}, {"id", (attrFn) idfn}, diff --git a/lib/common/htmltable.c b/lib/common/htmltable.c index 7f8015c6d..de878f5aa 100644 --- a/lib/common/htmltable.c +++ b/lib/common/htmltable.c @@ -259,6 +259,13 @@ static void doFill(GVJ_t * job, char *color, boxf BF) gvrender_box(job, BF, 1); } +static void doGrdtFill(GVJ_t * job, char *color, boxf BF, int gradient) +{ + //gvrender_set_fillcolor(job, color); + gvrender_set_pencolor(job, color); + gvrender_box(job, BF, gradient); +} + /* initAnchor: * Save current map values * Initialize fields in job->obj pertaining to anchors. @@ -432,6 +439,8 @@ emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env) int anchor; /* if true, we need to undo anchor settings. */ int doAnchor = (tbl->data.href || tbl->data.target); pointf AF[4]; + int gradient; + char *bordercolor; if (tbl->font) pushFontInfo(env, tbl->font, &savef); @@ -445,7 +454,6 @@ emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env) anchor = initAnchor(job, env, &tbl->data, pts, &saved, 1); else anchor = 0; - /* Set up rounded style */ if (tbl->style & ROUNDED) { AF[0] = pts.LL; @@ -465,11 +473,29 @@ emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env) /* Fill first */ if (tbl->data.bgcolor) { - if (tbl->style & ROUNDED) + if (tbl->style & ROUNDED){ round_corners (job, tbl->data.bgcolor, NULL, AF, 4, tbl->style, 1); + } else doFill(job, tbl->data.bgcolor, pts); + } + + if (tbl->data.gradient && tbl->data.gradientcolor) { + if (strcmp(tbl->data.gradient,"linear") == 0) + gradient = GRADIENT; + else if (strcmp(tbl->data.gradient,"radial") == 0) + gradient = RGRADIENT; + else + gradient = 0; + bordercolor = gvrender_set_gradient_values(job, tbl->data.gradientcolor, 0); + + if (tbl->style & ROUNDED){ + round_corners (job, bordercolor, NULL, AF, 4, tbl->style, gradient); + } + else + doGrdtFill(job, bordercolor, pts,gradient); } + while (*cells) { emit_html_cell(job, *cells, env); @@ -547,6 +573,8 @@ emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env) boxf pts = cp->data.box; pointf pos = env->pos; int inAnchor, doAnchor = (cp->data.href || cp->data.target); + int gradient; + char *bordercolor; pts.LL.x += pos.x; pts.UR.x += pos.x; @@ -558,12 +586,22 @@ emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env) else inAnchor = 0; - if (cp->data.bgcolor) { + if (cp->data.bgcolor) doFill(job, cp->data.bgcolor, pts); - } if (cp->data.border) doBorder(job, cp->data.pencolor, cp->data.border, pts); + + if(cp->data.gradient && cp->data.gradientcolor){ + if(strcmp(cp->data.gradient,"linear")==0) + gradient = GRADIENT; + else if (strcmp(cp->data.gradient,"radial")==0) + gradient = RGRADIENT; + else + gradient = 0; + bordercolor = gvrender_set_gradient_values(job, cp->data.gradientcolor, 0); + doGrdtFill(job,bordercolor, pts, gradient); + } if (cp->child.kind == HTML_TBL) emit_html_tbl(job, cp->child.u.tbl, env); diff --git a/lib/common/htmltable.h b/lib/common/htmltable.h index f0d3cc9d3..33edfa1ce 100644 --- a/lib/common/htmltable.h +++ b/lib/common/htmltable.h @@ -69,6 +69,8 @@ extern "C" { char *id; char *bgcolor; char *pencolor; + char *gradient; + char *gradientcolor; signed char space; unsigned char border; unsigned char pad; diff --git a/lib/common/input.c b/lib/common/input.c index 33f30aebd..789e166da 100644 --- a/lib/common/input.c +++ b/lib/common/input.c @@ -794,6 +794,8 @@ void graph_init(graph_t * g, boolean use_rankdir) Initial_dist = MYHUGE; G_ordering = agfindgraphattr(g, "ordering"); + G_gradientcolor = agfindgraphattr(g,"gradientcolor"); + G_gradientangle = agfindgraphattr(g,"gradientangle"); /* initialize nodes */ N_height = agfindnodeattr(g, "height"); @@ -824,6 +826,8 @@ void graph_init(graph_t * g, boolean use_rankdir) N_comment = agfindnodeattr(g, "comment"); N_vertices = agfindnodeattr(g, "vertices"); N_z = agfindnodeattr(g, "z"); + N_gradientcolor = agfindnodeattr(g,"gradientcolor"); + N_gradientangle = agfindnodeattr(g,"gradientangle"); /* initialize edges */ E_weight = agfindedgeattr(g, "weight"); diff --git a/lib/common/render.h b/lib/common/render.h index f39c72ddf..9167d9819 100644 --- a/lib/common/render.h +++ b/lib/common/render.h @@ -160,6 +160,9 @@ extern "C" { extern void write_attributed_dot(graph_t *g, FILE *f); extern void write_canonical_dot(graph_t *g, FILE *f); extern boxf xdotBB (graph_t* g); + extern char *findStartColor(void * n, attrsym_t * attr, char *); + extern char *findStopColor(void * n, attrsym_t * attr, char *); + extern int findGradientAngle(void * n, attrsym_t * attr); #undef extern diff --git a/lib/common/shapes.c b/lib/common/shapes.c index 77e7847e3..284a333d5 100644 --- a/lib/common/shapes.c +++ b/lib/common/shapes.c @@ -17,6 +17,7 @@ #define RBCONST 12 #define RBCURVE .5 +#define GR_SZ 50 static port Center = { {0, 0}, -1, 0, 0, 0, 1, 0, 0, 0 }; @@ -280,6 +281,50 @@ char *findFill(node_t * n) return (findFillDflt(n, DEFAULT_FILL)); } +char *findStartColor(void * n, attrsym_t * attr, char *dflt) +{ + char *color; + + color = late_nnstring(n, attr, dflt); + if (!color[0]) { + color = late_nnstring(n, N_color, ""); + if (!color[0]) { + color = DEFAULT_FILL; + } + } + return color; +} + + +char *findStopColor(void * n, attrsym_t * attr, char *dflt) +{ + char *color,*str; + + str = late_nnstring(n, attr, dflt); + color = strchr(str, ':'); + if ((color == NULL) || !color[0]) { + color = str; + if (!color[0]) { + color = DEFAULT_FILL; + } + } + else { + color++; + if (!color[0]) { + color = DEFAULT_FILL; + } + } + + return color; +} + +int findGradientAngle(void * n, attrsym_t * attr) +{ + int angle; + angle = late_int(n, attr, 0, 0); + return (angle); +} + static char **checkStyle(node_t * n, int *flagp) { char *style; @@ -297,6 +342,12 @@ static char **checkStyle(node_t * n, int *flagp) if (streq(p, "filled")) { istyle |= FILLED; pp++; + } else if (streq(p, "linear")) { + istyle |= GRADIENT; + pp++; + }else if (streq(p, "radial")) { + istyle |= (RGRADIENT); + pp++; } else if (streq(p, "rounded")) { istyle |= ROUNDED; qp = pp; /* remove rounded from list passed to renderer */ @@ -434,7 +485,10 @@ void round_corners(GVJ_t * job, char *fillc, char *penc, pointf * AF, pts[j++] = B[4 * seg + 1]; pts[j++] = B[4 * seg + 2]; } - gvrender_polygon(job, pts, 2 * sides, TRUE); + if (filled & GRADIENT) + gvrender_polygon(job, pts, 2 * sides, filled); + else + gvrender_polygon(job, pts, 2 * sides, TRUE); free(pts); for (seg = 0; seg < sides; seg++) { gvrender_beziercurve(job, B + 4 * seg + 2, 4, FALSE, FALSE, @@ -1623,10 +1677,15 @@ static void poly_gencode(GVJ_t * job, node_t * n) gvrender_set_fillcolor(job, color); filled = TRUE; } else { - if (style & FILLED) { + if (style & GRADIENT) { + gvrender_set_gradient(job,n,N_gradientcolor,N_gradientangle); + filled = FALSE; + } + else if (style & FILLED) { gvrender_set_fillcolor(job, findFill(n)); /* emit fill color */ filled = TRUE; - } else { + } + else { filled = FALSE; } pencolor(job, n); /* emit pen color */ @@ -1661,14 +1720,26 @@ static void poly_gencode(GVJ_t * job, node_t * n) /* lay down fill first */ if (filled && pfilled) { if (sides <= 2) { - gvrender_ellipse(job, AF, sides, filled); + if (style & GRADIENT) + gvrender_ellipse(job, AF, sides, style); + else + gvrender_ellipse(job, AF, sides, filled); + if (style & DIAGONALS) { Mcircle_hack(job, n); } } else if (style & (ROUNDED | DIAGONALS)) { - node_round_corners(job, n, AF, sides, style, filled); + if (style & GRADIENT) { + gvrender_set_gradient(job,n,N_gradientcolor,N_gradientangle); + node_round_corners(job, n, AF, sides, style, style); + } + else + node_round_corners(job, n, AF, sides, style, filled); } else { - gvrender_polygon(job, AF, sides, filled); + if (style & GRADIENT) + gvrender_polygon(job, AF, sides, style); + else + gvrender_polygon(job, AF, sides, filled); } } gvrender_usershape(job, name, AF, sides, filled, @@ -1683,14 +1754,25 @@ static void poly_gencode(GVJ_t * job, node_t * n) AF[i].y = P.y * ysize + ND_coord(n).y; } if (sides <= 2) { - gvrender_ellipse(job, AF, sides, filled); + if (style & GRADIENT) + gvrender_ellipse(job, AF, sides, style); + else + gvrender_ellipse(job, AF, sides, filled); if (style & DIAGONALS) { Mcircle_hack(job, n); } } else if (SPECIAL_CORNERS(style)) { + if (style & GRADIENT) { + gvrender_set_gradient(job,n,N_gradientcolor,N_gradientangle); + node_round_corners(job, n, AF, sides, style, style); + } + else node_round_corners(job, n, AF, sides, style, filled); } else { - gvrender_polygon(job, AF, sides, filled); + if (style & GRADIENT) + gvrender_polygon(job, AF, sides, style); + else + gvrender_polygon(job, AF, sides, filled); } /* fill innermost periphery only */ filled = FALSE; diff --git a/lib/common/utils.c b/lib/common/utils.c index 9448701f5..701c669f0 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -1831,6 +1831,87 @@ void setEdgeType (graph_t* g, int dflt) GD_flags(g) |= et; } + +/* cairogen_get_gradient_points + * Evaluates the extreme points of an ellipse or polygon + * Determines the point at the center of the extreme points + * Uses the angle parameter to identify two points on a line that defines the gradient direction + * + */ +void cairogen_get_gradient_points(pointf * A, pointf * G, int n, float angle) +{ + int i; + double rx, ry; + pointf min,max,center; + + if ( n == 2) { + rx = A[1].x - A[0].x; + ry = A[1].y - A[0].y; + min.x = A[0].x - rx; + max.x = A[0].x + rx; + min.y = A[0].y - ry; + max.y = A[0].y + ry; + } + else { + min.x = max.x = A[0].x; + min.y = max.y = A[0].y; + for (i = 0; i < n; i++){ + min.x = (A[i].x < min.x ? A[i].x : min.x); + min.y = (A[i].y < min.y ? A[i].y : min.y); + max.x = (A[i].x > max.x ? A[i].x : max.x); + max.y = (A[i].y > max.y ? A[i].y : max.y); + } + } + center.x = min.x + (max.x - min.x)/2; + center.y = min.y + (max.y - min.y)/2; + G[0].x = center.x - (max.x - center.x) * cos(angle); + G[0].y = -center.y + (max.y - center.y) * sin(angle); + G[1].x = center.x + (center.x - min.x) * cos(angle); + G[1].y = -center.y - (center.y - min.y) * sin(angle); +} + +/* cairogen_get_rgradient_points + * Evaluates the extreme points of an ellipse or polygon + * Determines the point at the center of the extreme points + * Sets the inner radius to half the distance to the min point + * + */ +void cairogen_get_rgradient_points(pointf * A, pointf * G, int n) +{ + int i; + double rx, ry,inner_r,outer_r; + pointf min,max,center; + + if ( n == 2) { + rx = A[1].x - A[0].x; + ry = A[1].y - A[0].y; + min.x = A[0].x - rx; + max.x = A[0].x + rx; + min.y = A[0].y - ry; + max.y = A[0].y + ry; + } + else { + min.x = max.x = A[0].x; + min.y = max.y = A[0].y; + for (i = 0; i < n; i++){ + min.x = (A[i].x < min.x ? A[i].x : min.x); + min.y = (A[i].y < min.y ? A[i].y : min.y); + max.x = (A[i].x > max.x ? A[i].x : max.x); + max.y = (A[i].y > max.y ? A[i].y : max.y); + } + } + center.x = min.x + (max.x - min.x)/2; + center.y = min.y + (max.y - min.y)/2; + outer_r = sqrt((center.x - min.x)*(center.x - min.x) + + (center.y - min.y)*(center.y - min.y)); + inner_r = outer_r /4.; + G[0].x = center.x; + G[0].y = -center.y; + G[1].x = inner_r; + G[1].y = outer_r; + +} + #ifndef WIN32_STATIC #ifndef HAVE_STRCASECMP diff --git a/lib/gvc/gvcjob.h b/lib/gvc/gvcjob.h index b8acce5c2..1944f8538 100644 --- a/lib/gvc/gvcjob.h +++ b/lib/gvc/gvcjob.h @@ -33,7 +33,7 @@ extern "C" { typedef struct gvloadimage_engine_s gvloadimage_engine_t; typedef enum { PEN_NONE, PEN_DASHED, PEN_DOTTED, PEN_SOLID } pen_type; - typedef enum { FILL_NONE, FILL_SOLID } fill_type; + typedef enum { FILL_NONE, FILL_SOLID, FILL_LINEAR, FILL_RADIAL } fill_type; typedef enum { FONT_REGULAR, FONT_BOLD, FONT_ITALIC } font_type; typedef enum { LABEL_PLAIN, LABEL_HTML } label_type; @@ -152,7 +152,13 @@ extern "C" { int argc; int alloc; } gv_argvlist_t; - + + typedef struct gv_gradient_s { + int angle; + gvcolor_t startcolor, stopcolor; + int id; + } gv_gradient_t; + typedef struct gvdevice_callbacks_s { void (*refresh) (GVJ_t * job); void (*button_press) (GVJ_t * job, int button, pointf pointer); @@ -201,6 +207,7 @@ extern "C" { emit_state_t emit_state; gvcolor_t pencolor, fillcolor; + gv_gradient_t gradient; pen_type pen; fill_type fill; double penwidth; diff --git a/lib/gvc/gvcproc.h b/lib/gvc/gvcproc.h index 53d6c63c4..f149c8eb2 100644 --- a/lib/gvc/gvcproc.h +++ b/lib/gvc/gvcproc.h @@ -103,10 +103,15 @@ extern void gvrender_set_pencolor(GVJ_t * job, char *name); extern void gvrender_set_penwidth(GVJ_t * job, double penwidth); extern void gvrender_set_fillcolor(GVJ_t * job, char *name); + extern void gvrender_set_gradientcolor(GVJ_t * job, char *gr_color_ptr[2]); + extern void gvrender_set_gradientAngle(GVJ_t * job, int angle); + extern void gvrender_set_gradientId(GVJ_t * job); + extern void gvrender_set_gradient(GVJ_t * job, void *g_obj, attrsym_t * color_attr, attrsym_t * angle_attr); + extern char *gvrender_set_gradient_values(GVJ_t* job, char* gradcolor, int angle); extern void gvrender_set_style(GVJ_t * job, char **s); - extern void gvrender_ellipse(GVJ_t * job, pointf * AF, int n, boolean filled); - extern void gvrender_polygon(GVJ_t * job, pointf * AF, int n, boolean filled); - extern void gvrender_box(GVJ_t * job, boxf BF, boolean filled); + extern void gvrender_ellipse(GVJ_t * job, pointf * AF, int n, int filled); + extern void gvrender_polygon(GVJ_t* job, pointf* af, int n, int filled); + extern void gvrender_box(GVJ_t * job, boxf BF, int filled); extern void gvrender_beziercurve(GVJ_t * job, pointf * AF, int n, int arrow_at_start, int arrow_at_end, boolean filled); extern void gvrender_polyline(GVJ_t * job, pointf * AF, int n); diff --git a/lib/gvc/gvrender.c b/lib/gvc/gvrender.c index 4a085cdd6..4c5763fba 100644 --- a/lib/gvc/gvrender.c +++ b/lib/gvc/gvrender.c @@ -39,12 +39,17 @@ extern int emit_once(char *str); extern shape_desc *find_user_shape(char *name); +extern char *findStopColor(void * n, attrsym_t * attr, char *dflt); +extern char *findStartColor(void * n, attrsym_t * attr, char *dflt); +extern int findGradientAngle(void * n, attrsym_t * attr); extern boolean mapbool(char *s); #ifndef HAVE_STRCASECMP extern int strcasecmp(const char *s1, const char *s2); #endif +#define GR_SZ 50 + /* storage for temporary hacks until client API is FP */ static pointf *AF; static int sizeAF; @@ -489,6 +494,92 @@ void gvrender_set_fillcolor(GVJ_t * job, char *name) *cp = ':'; } +void gvrender_set_gradientcolor(GVJ_t * job, char *gr_color_ptr[2]) +{ + gvrender_engine_t *gvre = job->render.engine; + gvcolor_t *color; + char * start_color, * stop_color; + + start_color = gr_color_ptr[0]; + stop_color = gr_color_ptr[1]; + if (gvre && gr_color_ptr[0] != NULL && gr_color_ptr[1] != NULL) { + color = &(job->obj->gradient.startcolor); + gvrender_resolve_color(job->render.features, start_color, color); + if (gvre->resolve_color) + gvre->resolve_color(job, color); + color = &(job->obj->gradient.stopcolor); + gvrender_resolve_color(job->render.features, stop_color, color); + if (gvre->resolve_color) + gvre->resolve_color(job, color); + } +} + +void gvrender_set_gradientAngle(GVJ_t * job, int angle) +{ + obj_state_t *obj = job->obj; + obj->gradient.angle = angle; + +} +static int gradientId; +void gvrender_set_gradientId(GVJ_t * job){ + + obj_state_t *obj = job->obj; + obj->gradient.id = gradientId++; +} + +void gvrender_set_gradient(GVJ_t * job, void *g_obj, attrsym_t * color_attr, attrsym_t * angle_attr){ +char *ptr, *gradcolor = NULL,*gradstartcolor; +int angle; + + + if(g_obj != NULL && color_attr != NULL) { + gradcolor = N_GNEW((2 * GR_SZ)+1,char); + gradstartcolor = N_GNEW(GR_SZ,char); + strcpy(gradcolor,findStartColor(g_obj,color_attr,DEFAULT_FILL)); + if ((ptr = strstr(gradcolor, ":")) != NULL) /* if attribute is a color list use first one */ + *ptr = '\0'; + strcpy(gradstartcolor,gradcolor); + strcat(gradcolor,":"); + strcat(gradcolor,findStopColor(g_obj,color_attr,gradstartcolor)); /* use start color as stop color if : missing */ + angle = findGradientAngle(g_obj,angle_attr); + gvrender_set_gradient_values(job,gradcolor,angle); + free(gradstartcolor); + free(gradcolor); + } + +} + +static char gradientcolor[2][GR_SZ]; + +char *gvrender_set_gradient_values(GVJ_t* job, char* gradcolor, int angle){ + char *gr_color_ptr[2], *ptr; + + gr_color_ptr[0] = gradientcolor[0]; + gr_color_ptr[1] = gradientcolor[1]; + gr_color_ptr[0][GR_SZ - 1] = gr_color_ptr[0][0] = 0; + gr_color_ptr[1][GR_SZ - 1] = gr_color_ptr[1][0] = 0; + if(gradcolor != NULL && gradcolor[0]){ + ptr= strstr(gradcolor,":"); + if(ptr != NULL){ + *ptr = '\0'; + ++ptr; + if(ptr[0]) + strncpy(gr_color_ptr[1],ptr,GR_SZ - 1); + else + strncpy(gr_color_ptr[1],gradcolor,GR_SZ - 1); //use start color as stop color + } + else + strncpy(gr_color_ptr[1],gradcolor,GR_SZ - 1); //use start color as stop color + strncpy(gr_color_ptr[0],gradcolor,GR_SZ - 1); + gvrender_set_gradientcolor(job, gr_color_ptr); + gvrender_set_gradientAngle(job,angle); + gvrender_set_gradientId(job); + } + return gr_color_ptr[1]; + +} + + void gvrender_set_style(GVJ_t * job, char **s) { gvrender_engine_t *gvre = job->render.engine; @@ -515,6 +606,10 @@ void gvrender_set_style(GVJ_t * job, char **s) obj->penwidth = atof(p); } else if (streq(line, "filled")) obj->fill = FILL_SOLID; + else if (streq(line, "linear")) + obj->fill = FILL_LINEAR; + else if (streq(line, "radial")) + obj->fill = FILL_RADIAL; else if (streq(line, "unfilled")) obj->fill = FILL_NONE; else if (streq(line, "tapered")) @@ -528,7 +623,7 @@ void gvrender_set_style(GVJ_t * job, char **s) } } -void gvrender_ellipse(GVJ_t * job, pointf * pf, int n, boolean filled) +void gvrender_ellipse(GVJ_t * job, pointf * pf, int n, int filled) { gvrender_engine_t *gvre = job->render.engine; @@ -549,10 +644,9 @@ void gvrender_ellipse(GVJ_t * job, pointf * pf, int n, boolean filled) } } -void gvrender_polygon(GVJ_t * job, pointf * af, int n, boolean filled) +void gvrender_polygon(GVJ_t * job, pointf * af, int n, int filled) { gvrender_engine_t *gvre = job->render.engine; - if (gvre) { if (gvre->polygon && job->obj->pen != PEN_NONE) { if (job->flags & GVRENDER_DOES_TRANSFORM) @@ -569,7 +663,8 @@ void gvrender_polygon(GVJ_t * job, pointf * af, int n, boolean filled) } } -void gvrender_box(GVJ_t * job, boxf B, boolean filled) + +void gvrender_box(GVJ_t * job, boxf B, int filled) { pointf A[4]; diff --git a/plugin/core/gvrender_core_svg.c b/plugin/core/gvrender_core_svg.c index c97954339..512ac04b7 100644 --- a/plugin/core/gvrender_core_svg.c +++ b/plugin/core/gvrender_core_svg.c @@ -44,6 +44,8 @@ typedef enum { FORMAT_SVG, FORMAT_SVGZ, } format_type; extern char *xml_string(char *str); extern char *xml_url_string(char *str); +extern void cairogen_get_gradient_points(pointf * A, pointf * G, int n, float angle); +extern void cairogen_get_rgradient_points(pointf * A, pointf * G, int n); /* SVG dash array */ static char *sdasharray = "5,2"; @@ -90,9 +92,18 @@ static void svg_print_color(GVJ_t * job, gvcolor_t color) static void svg_grstyle(GVJ_t * job, int filled) { obj_state_t *obj = job->obj; + char sgid[11]; gvputs(job, " fill=\""); - if (filled) { + if (filled == GRADIENT){ + sprintf(sgid,"%d",obj->gradient.id); + gvprintf(job,"url(#l_%s)",sgid); + } + else if (filled == RGRADIENT){ + sprintf(sgid,"%d",obj->gradient.id); + gvprintf(job,"url(#r_%s)",sgid); + } + else if (filled) { svg_print_color(job, obj->fillcolor); if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0 && obj->fillcolor.u.rgba[3] < 255 ) gvprintf(job, "\" fill-opacity=\"%f", ((float)obj->fillcolor.u.rgba[3]/255.0)); @@ -392,9 +403,93 @@ static void svg_textpara(GVJ_t * job, pointf p, textpara_t * para) gvputs(job, "\n"); } +/* svg_gradstyle + * Outputs the SVG statements that define the gradient pattern + */ +static void svg_gradstyle(GVJ_t * job, pointf * A, int n) +{ + pointf G[2]; + float angle; + char sgid[11]; + + obj_state_t *obj = job->obj; + angle = obj->gradient.angle * M_PI / 180; //angle of gradient line + G[0].x = G[0].y = G[1].x = G[1].y = 0.; + cairogen_get_gradient_points(A, G, n, angle); //get points on gradient line + sprintf(sgid,"%d",obj->gradient.id); + + gvprintf(job, "\n\n", G[0].x,G[0].y,G[1].x,G[1].y); + gvputs(job,"gradient.startcolor); + gvputs(job,";stop-opacity:"); + if (obj->gradient.startcolor.type == RGBA_BYTE && obj->gradient.startcolor.u.rgba[3] > 0 && obj->gradient.startcolor.u.rgba[3] < 255 ) + gvprintf(job, "%f", ((float)obj->gradient.startcolor.u.rgba[3]/255.0)); + else gvputs(job,"1."); + gvputs(job,";\"/>\n"); + gvputs(job,"gradient.stopcolor); + gvputs(job,";stop-opacity:"); + if (obj->gradient.stopcolor.type == RGBA_BYTE && obj->gradient.stopcolor.u.rgba[3] > 0 && obj->gradient.stopcolor.u.rgba[3] < 255 ) + gvprintf(job, "%f", ((float)obj->gradient.stopcolor.u.rgba[3]/255.0)); + else gvputs(job,"1."); + gvputs(job,";\"/>\n\n\n"); +} + +/* svg_gradstyle + * Outputs the SVG statements that define the radial gradient pattern + */ +static void svg_rgradstyle(GVJ_t * job, pointf * A, int n) +{ + pointf G[2]; + float angle; + char fx[5],fy[5],sgid[11]; + int ifx,ify; + + obj_state_t *obj = job->obj; + angle = obj->gradient.angle * M_PI / 180; //angle of gradient line + G[0].x = G[0].y = G[1].x = G[1].y = G[2].x = G[2].y= 0.; + cairogen_get_rgradient_points(A, G, n); + if(angle == 0.){ + ifx = ify = 50; + } + else { + ifx = 50*(1 + cos(angle)); + ify = 50*(1 - sin(angle)); + } + sprintf(sgid,"%d",obj->gradient.id); + sprintf(fx,"%d%%",ifx); + sprintf(fy,"%d%%",ify); + gvprintf(job, "\n\n",fy); + gvputs(job,"gradient.startcolor); + gvputs(job,";stop-opacity:"); + if (obj->gradient.startcolor.type == RGBA_BYTE && obj->gradient.startcolor.u.rgba[3] > 0 && obj->gradient.startcolor.u.rgba[3] < 255 ) + gvprintf(job, "%f", ((float)obj->gradient.startcolor.u.rgba[3]/255.0)); + else gvputs(job,"1."); + gvputs(job,";\"/>\n"); + gvputs(job,"gradient.stopcolor); + gvputs(job,";stop-opacity:"); + if (obj->gradient.stopcolor.type == RGBA_BYTE && obj->gradient.stopcolor.u.rgba[3] > 0 && obj->gradient.stopcolor.u.rgba[3] < 255 ) + gvprintf(job, "%f", ((float)obj->gradient.stopcolor.u.rgba[3]/255.0)); + else gvputs(job,"1."); + gvputs(job,";\"/>\n\n\n"); +} + + static void svg_ellipse(GVJ_t * job, pointf * A, int filled) { /* A[] contains 2 points: the center and corner. */ + if (filled == GRADIENT) { + svg_gradstyle(job,A,2); + } + else if (filled == (RGRADIENT)){ + svg_rgradstyle(job,A,2); + } gvputs(job, " #endif +#include "const.h" #include "gvplugin_render.h" #include "gvplugin_device.h" #include "gvio.h" @@ -30,6 +31,8 @@ #ifdef HAVE_PANGOCAIRO #include +extern void cairogen_get_gradient_points(pointf * A, pointf * G, int n, float angle); +extern void cairogen_get_rgradient_points(pointf * A, pointf * G, int n); typedef enum { FORMAT_CAIRO, @@ -65,6 +68,13 @@ static void cairogen_set_color(cairo_t * cr, gvcolor_t * color) color->u.RGBA[2], color->u.RGBA[3]); } +static void cairogen_add_color_stop_rgba(cairo_pattern_t *pat,int stop , gvcolor_t * color) +{ + cairo_pattern_add_color_stop_rgba (pat, stop,color->u.RGBA[0], color->u.RGBA[1], + color->u.RGBA[2], color->u.RGBA[3]); +} + + static cairo_status_t writer (void *closure, const unsigned char *data, unsigned int length) { @@ -259,6 +269,9 @@ static void cairogen_ellipse(GVJ_t * job, pointf * A, int filled) cairo_t *cr = (cairo_t *) job->context; cairo_matrix_t matrix; double rx, ry; + float angle,r1,r2; + pointf G[2],c1,c2; + cairo_pattern_t *pat; cairogen_set_penstyle(job, cr); @@ -278,7 +291,39 @@ if (ry < RMIN) ry = RMIN; cairo_set_matrix(cr, &matrix); - if (filled) { + if (filled == GRADIENT || filled == (RGRADIENT)) { + angle = obj->gradient.angle * M_PI / 180; + if(filled == GRADIENT) { + angle = obj->gradient.angle * M_PI / 180; + cairogen_get_gradient_points(A, G, 2, angle); + pat = cairo_pattern_create_linear (G[0].x,G[0].y,G[1].x,G[1].y); + } + else { + cairogen_get_rgradient_points(A, G, 2); + //r1 is inner radius, r2 is outter radius + r1 = G[1].x; + r2 = G[1].y; + if (angle == 0) { + c1.x = G[0].x; + c1.y = G[0].y; + } + else { + c1.x = G[0].x + (r2/4) * cos(angle); + c1.y = G[0].y - (r2/4) * sin(angle); + } + c2.x = G[0].x; + c2.y = G[0].y; + r1 = r2/4; + //r1 is inner radius, r2 is outter radius + pat = cairo_pattern_create_radial(c1.x,c1.y,r1,c2.x,c2.y,r2); + } + cairogen_add_color_stop_rgba(pat,0,&(obj->gradient.startcolor)); + cairogen_add_color_stop_rgba(pat,1,&(obj->gradient.stopcolor)); + cairo_set_source (cr, pat); + cairo_fill_preserve (cr); + cairo_pattern_destroy (pat); + } + else if (filled) { cairogen_set_color(cr, &(obj->fillcolor)); cairo_fill_preserve(cr); } @@ -292,16 +337,49 @@ cairogen_polygon(GVJ_t * job, pointf * A, int n, int filled) obj_state_t *obj = job->obj; cairo_t *cr = (cairo_t *) job->context; int i; + float angle,r1,r2; + cairo_pattern_t *pat; + pointf G[2],c1,c2; cairogen_set_penstyle(job, cr); cairo_move_to(cr, A[0].x, -A[0].y); for (i = 1; i < n; i++) - cairo_line_to(cr, A[i].x, -A[i].y); + cairo_line_to(cr, A[i].x, -A[i].y); cairo_close_path(cr); - if (filled) { - cairogen_set_color(cr, &(obj->fillcolor)); - cairo_fill_preserve(cr); + if (filled == GRADIENT || filled == (RGRADIENT)) { + angle = obj->gradient.angle * M_PI / 180; + if(filled == GRADIENT) { + cairogen_get_gradient_points(A, G, n, angle); + pat = cairo_pattern_create_linear (G[0].x,G[0].y,G[1].x,G[1].y); + } + else { + cairogen_get_rgradient_points(A, G, n); + r1 = G[1].x; + r2 = G[1].y; + if (angle == 0) { + c1.x = G[0].x; + c1.y = G[0].y; + } + else { + c1.x = G[0].x + (r2/4) * cos(angle); + c1.y = G[0].y - (r2/4) * sin(angle); + } + c2.x = G[0].x; + c2.y = G[0].y; + r1 = r2/4; + //r1 is inner radius, r2 is outter radius + pat = cairo_pattern_create_radial(c1.x,c1.y,r1,c2.x,c2.y,r2); + } + cairogen_add_color_stop_rgba(pat,0,&(obj->gradient.startcolor)); + cairogen_add_color_stop_rgba(pat,1,&(obj->gradient.stopcolor)); + cairo_set_source (cr, pat); + cairo_fill_preserve (cr); + cairo_pattern_destroy (pat); + } + else if (filled) { + cairogen_set_color(cr, &(obj->fillcolor)); + cairo_fill_preserve(cr); } cairogen_set_color(cr, &(obj->pencolor)); cairo_stroke(cr);