summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaleb Michael Moore <cmoore@src.gnome.org>2004-06-12 19:08:25 +0000
committerCaleb Michael Moore <cmoore@src.gnome.org>2004-06-12 19:08:25 +0000
commit79293235c0dc1686ceec32155bfae42d88c94a48 (patch)
treecfd7a6df84d78b143fa4f65e2d5242572b374ee5
parentb3b9ac445744a9d588f5a7f7842f24afd93dd866 (diff)
downloadlibrsvg-79293235c0dc1686ceec32155bfae42d88c94a48.tar.gz
markers
-rw-r--r--ChangeLog4
-rw-r--r--rsvg-defs.h3
-rw-r--r--rsvg-shapes.c292
-rw-r--r--rsvg-shapes.h21
-rw-r--r--rsvg-styles.c36
-rw-r--r--rsvg-styles.h7
-rw-r--r--rsvg.c10
7 files changed, 371 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index c38dd83f..3ad6bfa3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
};
diff --git a/rsvg.c b/rsvg.c
index 678b08eb..cacdeac1 100644
--- a/rsvg.c
+++ b/rsvg.c
@@ -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);