diff options
author | Caleb Michael Moore <cmoore@src.gnome.org> | 2004-06-12 19:08:25 +0000 |
---|---|---|
committer | Caleb Michael Moore <cmoore@src.gnome.org> | 2004-06-12 19:08:25 +0000 |
commit | 79293235c0dc1686ceec32155bfae42d88c94a48 (patch) | |
tree | cfd7a6df84d78b143fa4f65e2d5242572b374ee5 | |
parent | b3b9ac445744a9d588f5a7f7842f24afd93dd866 (diff) | |
download | librsvg-79293235c0dc1686ceec32155bfae42d88c94a48.tar.gz |
markers
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | rsvg-defs.h | 3 | ||||
-rw-r--r-- | rsvg-shapes.c | 292 | ||||
-rw-r--r-- | rsvg-shapes.h | 21 | ||||
-rw-r--r-- | rsvg-styles.c | 36 | ||||
-rw-r--r-- | rsvg-styles.h | 7 | ||||
-rw-r--r-- | rsvg.c | 10 |
7 files changed, 371 insertions, 2 deletions
@@ -1,3 +1,7 @@ +2004-06-13 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-shapes.c: Implemented markers for streight lines + 2004-06-10 Dom Lachowicz <cinamod@hotmail.com> * rsvg-css.c: Parse preserveAspectRatio diff --git a/rsvg-defs.h b/rsvg-defs.h index 25ac0a46..fdd2eb25 100644 --- a/rsvg-defs.h +++ b/rsvg-defs.h @@ -42,7 +42,8 @@ typedef enum { RSVG_DEF_PATTERN, RSVG_DEF_PATH, RSVG_DEF_FILTER, - RSVG_DEF_MASK + RSVG_DEF_MASK, + RSVG_DEF_MARKER } RsvgDefType; struct _RsvgDefVal { diff --git a/rsvg-shapes.c b/rsvg-shapes.c index bbb09eaf..93ff057d 100644 --- a/rsvg-shapes.c +++ b/rsvg-shapes.c @@ -25,6 +25,7 @@ #include <string.h> #include <math.h> #include <errno.h> +#include <stdio.h> #include "rsvg-private.h" #include "rsvg-styles.h" @@ -310,6 +311,83 @@ rsvg_render_bpath (RsvgHandle *ctx, const ArtBpath *bpath) art_free (vpath); } +static void +rsvg_render_markers(RsvgBpathDef * bpath_def, RsvgHandle *ctx) +{ + int i; + + double x, y; + double lastx, lasty; + double nextx, nexty; + double linewidth; + + RsvgState * state; + RsvgMarker * startmarker; + RsvgMarker * middlemarker; + RsvgMarker * endmarker; + + state = rsvg_state_current(ctx); + + linewidth = state->stroke_width; + startmarker = (RsvgMarker *)state->startMarker; + middlemarker = (RsvgMarker *)state->middleMarker; + endmarker = (RsvgMarker *)state->endMarker; + x = 0; + y = 0; + nextx = state->affine[0] * bpath_def->bpath[0].x3 + + state->affine[2] * bpath_def->bpath[0].y3 + state->affine[4]; + nexty = state->affine[1] * bpath_def->bpath[0].x3 + + state->affine[3] * bpath_def->bpath[0].y3 + state->affine[5]; + + for (i = 0; i < bpath_def->n_bpath - 1; i++) + { + lastx = x; + lasty = y; + x = nextx; + y = nexty; + nextx = state->affine[0] * bpath_def->bpath[i + 1].x3 + + state->affine[2] * bpath_def->bpath[i + 1].y3 + state->affine[4]; + nexty = state->affine[1] * bpath_def->bpath[i + 1].x3 + + state->affine[3] * bpath_def->bpath[i + 1].y3 + state->affine[5]; + + + if(bpath_def->bpath[i + 1].code == ART_MOVETO || + bpath_def->bpath[i + 1].code == ART_MOVETO_OPEN || + bpath_def->bpath[i + 1].code == ART_END) + { + if (endmarker) + rsvg_marker_render (endmarker, x, y, atan2(y - lasty, x - lastx), linewidth, ctx); + } + else if (bpath_def->bpath[i].code == ART_MOVETO || bpath_def->bpath[i].code == ART_MOVETO_OPEN) + { + if (startmarker) + rsvg_marker_render (startmarker, x, y, atan2(nexty - y, nextx - x), linewidth, ctx); + } + else + { + double xdifin, ydifin, xdifout, ydifout, intot, outtot, angle; + + xdifin = x - lastx; + ydifin = y - lasty; + xdifout = nextx - x; + ydifout = nexty - y; + + intot = sqrt(xdifin * xdifin + ydifin * ydifin); + outtot = sqrt(xdifout * xdifout + ydifout * ydifout); + + xdifin /= intot; + ydifin /= intot; + xdifout /= outtot; + ydifout /= outtot; + + angle = atan2((ydifin + ydifout) / 2, (xdifin + xdifout) / 2); + + if (middlemarker) + rsvg_marker_render (middlemarker, x, y, angle, linewidth, ctx); + } + } +} + void rsvg_render_path(RsvgHandle *ctx, const char *d) { @@ -320,6 +398,8 @@ rsvg_render_path(RsvgHandle *ctx, const char *d) rsvg_render_bpath (ctx, bpath_def->bpath); + rsvg_render_markers(bpath_def, ctx); + rsvg_bpath_def_free (bpath_def); } @@ -1758,3 +1838,215 @@ rsvg_start_use (RsvgHandle *ctx, RsvgPropertyBag *atts) } } } + +static void +rsvg_marker_free(RsvgDefVal* self) +{ + RsvgMarker *marker; + marker = (RsvgMarker *)self; + g_free(self); +} + +void +rsvg_start_marker (RsvgHandle *ctx, RsvgPropertyBag *atts) +{ + const char *id = NULL, *value; + RsvgMarker *marker; + double font_size; + double x = 0., y = 0., w = 0., h = 0.; + double vbx = 0., vby = 0., vbw = 1., vbh = 1.; + gboolean obj_bbox = TRUE; + gboolean got_x, got_y, got_bbox, got_vbox, got_width, got_height; + got_x = got_y = got_bbox = got_vbox = got_width = got_height = FALSE; + + font_size = rsvg_state_current_font_size (ctx); + marker = g_new (RsvgMarker, 1); + + marker->orient = 0; + marker->orientAuto = FALSE; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + if ((value = rsvg_property_bag_lookup (atts, "viewBox"))) + { + got_vbox = rsvg_css_parse_vbox (value, &vbx, &vby, + &vbw, &vbh); + } + if ((value = rsvg_property_bag_lookup (atts, "refX"))) { + x = rsvg_css_parse_normalized_length (value, ctx->dpi, 1, font_size); + got_x = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "refY"))) { + y = rsvg_css_parse_normalized_length (value, ctx->dpi, 1, font_size); + got_y = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "markerWidth"))) { + w = rsvg_css_parse_normalized_length (value, ctx->dpi, 1, font_size); + got_width = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "markerHeight"))) { + h = rsvg_css_parse_normalized_length (value, ctx->dpi, 1, font_size); + got_height = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "orient"))) { + if (!strcmp (value, "auto")) + marker->orientAuto = TRUE; + else + marker->orient = rsvg_css_parse_angle(value); + } + if ((value = rsvg_property_bag_lookup (atts, "markerUnits"))) { + if (!strcmp (value, "userSpaceOnUse")) + obj_bbox = FALSE; + else + obj_bbox = TRUE; + got_bbox = TRUE; + } + } + + if (got_x) + marker->refX = x; + else + marker->refX = 0; + + if (got_y) + marker->refY = y; + else + marker->refY = 0; + + if (got_width) + marker->width = w; + else + marker->width = 1; + + if (got_height) + marker->height = h; + else + marker->height = 1; + + if (got_bbox) + marker->bbox = obj_bbox; + else + marker->bbox = TRUE; + + if (got_vbox) + { + marker->vbx = vbx; + marker->vby = vby; + marker->vbw = vbw; + marker->vbh = vbh; + marker->vbox = TRUE; + } + else + marker->vbox = FALSE; + + /* set up the defval stuff */ + marker->super.type = RSVG_DEF_MARKER; + + marker->contents = (RsvgDefsDrawable *)&(rsvg_push_def_group (ctx, "")->super); + + marker->super.free = rsvg_marker_free; + + rsvg_defs_set (ctx->defs, id, &marker->super); +} + +void +rsvg_marker_render (RsvgMarker *self, gdouble x, gdouble y, gdouble orient, gdouble linewidth, RsvgHandle *ctx) +{ + gdouble affine[6]; + gdouble taffine[6]; + int i; + gdouble rotation; + + if (self->bbox) { + art_affine_scale(affine,linewidth * rsvg_state_current(ctx)->affine[0], + linewidth * rsvg_state_current(ctx)->affine[3]); + } else { + for (i = 0; i < 6; i++) + affine[i] = rsvg_state_current(ctx)->affine[i]; + } + + if (self->vbox) { + taffine[0] = self->width / self->vbw; + taffine[1] = 0.; + taffine[2] = 0.; + taffine[3] = self->height / self->vbh; + taffine[4] = - self->vbx / self->vbw; + taffine[5] = - self->vby / self->vbh; + art_affine_multiply(affine, taffine, affine); + } + + art_affine_translate(taffine, -self->refX, -self->refY); + + art_affine_multiply(affine, taffine, affine); + + if (self->orientAuto) + rotation = orient * 180 / 3.14159265358979323; + else + rotation = self->orient; + + art_affine_rotate(taffine, rotation); + + art_affine_multiply(affine, affine, taffine); + + art_affine_translate(taffine, x, y); + + art_affine_multiply(affine, affine, taffine); + + /* push the state stack */ + if (ctx->n_state == ctx->n_state_max) + ctx->state = g_renew (RsvgState, ctx->state, + ctx->n_state_max <<= 1); + if (ctx->n_state) + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + else + rsvg_state_init (ctx->state); + ctx->n_state++; + + for (i = 0; i < 6; i++) + { + rsvg_state_current(ctx)->affine[i] = affine[i]; + } + + rsvg_defs_drawable_draw (self->contents, ctx, 2); + + /* pop the state stack */ + ctx->n_state--; + rsvg_state_finalize (&ctx->state[ctx->n_state]); +} + +RsvgDefVal * +rsvg_marker_parse (const RsvgDefs * defs, const char *str) +{ + if (!strncmp (str, "url(", 4)) + { + const char *p = str + 4; + int ix; + char *name; + RsvgDefVal *val; + + while (g_ascii_isspace (*p)) + p++; + + if (*p == '#') + { + p++; + for (ix = 0; p[ix]; ix++) + if (p[ix] == ')') + break; + + if (p[ix] == ')') + { + name = g_strndup (p, ix); + val = rsvg_defs_lookup (defs, name); + g_free (name); + + if (val && val->type == RSVG_DEF_MARKER) + return (RsvgDefVal *) val; + } + } + } + return NULL; +} diff --git a/rsvg-shapes.h b/rsvg-shapes.h index 33b8d801..0e2bec6f 100644 --- a/rsvg-shapes.h +++ b/rsvg-shapes.h @@ -74,6 +74,27 @@ struct _RsvgDefsDrawableUse { RsvgDefsDrawable *child; }; +typedef struct _RsvgMarker RsvgMarker; + +struct _RsvgMarker { + RsvgDefVal super; + RsvgDefsDrawable * contents; + gboolean bbox; + double refX, refY, orient; + double vbx, vby, vbw, vbh, width, height; + gboolean vbox; + gboolean orientAuto; +}; + +void +rsvg_start_marker (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_marker_render (RsvgMarker *self, gdouble x, gdouble y, gdouble orient, gdouble linewidth, RsvgHandle *ctx); + +RsvgDefVal * +rsvg_marker_parse (const RsvgDefs * defs, const char *str); + GdkPixbuf * rsvg_pixbuf_new_from_href (const char *href, const char *base_uri, diff --git a/rsvg-styles.c b/rsvg-styles.c index 2accf135..fdee04d9 100644 --- a/rsvg-styles.c +++ b/rsvg-styles.c @@ -92,6 +92,9 @@ rsvg_state_init (RsvgState *state) state->text_anchor = TEXT_ANCHOR_START; state->visible = TRUE; state->filter = NULL; + state->startMarker = NULL; + state->middleMarker = NULL; + state->endMarker = NULL; state->has_current_color = FALSE; state->has_fill_server = FALSE; @@ -117,6 +120,9 @@ rsvg_state_init (RsvgState *state) state->has_font_decor = FALSE; state->has_text_dir = FALSE; state->has_text_anchor = FALSE; + state->has_startMarker = FALSE; + state->has_middleMarker = FALSE; + state->has_endMarker = FALSE; } void @@ -194,6 +200,12 @@ rsvg_state_reinherit (RsvgState *dst, const RsvgState *src) dst->text_dir = src->text_dir; if (!dst->has_text_anchor) dst->text_anchor = src->text_anchor; + if (!dst->has_startMarker) + dst->startMarker = src->startMarker; + if (!dst->has_middleMarker) + dst->middleMarker = src->middleMarker; + if (!dst->has_endMarker) + dst->endMarker = src->endMarker; if (!dst->has_font_family) { g_free(dst->font_family); /* font_family is always set to something */ @@ -268,6 +280,12 @@ rsvg_state_dominate (RsvgState *dst, const RsvgState *src) dst->text_dir = src->text_dir; if (!dst->has_text_anchor || src->has_text_anchor) dst->text_anchor = src->text_anchor; + if (!dst->has_startMarker || !src->has_startMarker) + dst->startMarker = src->startMarker; + if (!dst->has_middleMarker || !src->middleMarker) + dst->middleMarker = src->middleMarker; + if (!dst->has_endMarker || !src->endMarker) + dst->endMarker = src->endMarker; if (!dst->has_font_family || src->has_font_family) { g_free(dst->font_family); /* font_family is always set to something */ @@ -594,6 +612,21 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) state->stop_opacity = rsvg_css_parse_opacity (str + arg_off); } } + else if (rsvg_css_param_match (str, "marker-start")) + { + state->startMarker = rsvg_marker_parse(ctx->defs, str + arg_off); + state->has_startMarker = TRUE; + } + else if (rsvg_css_param_match (str, "marker-mid")) + { + state->middleMarker = rsvg_marker_parse(ctx->defs, str + arg_off); + state->has_middleMarker = TRUE; + } + else if (rsvg_css_param_match (str, "marker-end")) + { + state->endMarker = rsvg_marker_parse(ctx->defs, str + arg_off); + state->has_endMarker = TRUE; + } else if (rsvg_css_param_match (str, "stroke-miterlimit")) { state->has_miter_limit = TRUE; @@ -707,6 +740,9 @@ rsvg_parse_style_pairs (RsvgHandle *ctx, RsvgState *state, rsvg_lookup_parse_style_pair (ctx, state, "visibility", atts); rsvg_lookup_parse_style_pair (ctx, state, "writing-mode", atts); rsvg_lookup_parse_style_pair (ctx, state, "xml:lang", atts); + rsvg_lookup_parse_style_pair (ctx, state, "marker-start", atts); + rsvg_lookup_parse_style_pair (ctx, state, "marker-mid", atts); + rsvg_lookup_parse_style_pair (ctx, state, "marker-end", atts); } /* Split a CSS2 style into individual style arguments, setting attributes diff --git a/rsvg-styles.h b/rsvg-styles.h index d4280bbc..be85c697 100644 --- a/rsvg-styles.h +++ b/rsvg-styles.h @@ -125,6 +125,13 @@ struct _RsvgState { guint32 current_color; gboolean has_current_color; + RsvgDefVal * startMarker; + RsvgDefVal * middleMarker; + RsvgDefVal * endMarker; + gboolean has_startMarker; + gboolean has_middleMarker; + gboolean has_endMarker; + GdkPixbuf *save_pixbuf; }; @@ -1221,7 +1221,11 @@ rsvg_start_element (void *data, const xmlChar *name, ctx->in_defs++; rsvg_start_mask(ctx, bag); } - + else if (!strcmp ((char *)name, "marker")) + { + ctx->in_defs++; + rsvg_start_marker (ctx, bag); + } /* see conicalGradient discussion above */ else if (!strcmp ((char *)name, "linearGradient")) rsvg_start_linear_gradient (ctx, bag); @@ -1275,6 +1279,10 @@ rsvg_end_element (void *data, const xmlChar *name) rsvg_end_mask(ctx); ctx->in_defs--; } + else if (!strcmp ((char *)name, "marker")){ + rsvg_pop_def_group(ctx); + ctx->in_defs--; + } else if (!strcmp ((char *)name, "pattern")) { ctx->in_defs--; rsvg_pop_def_group(ctx); |