From f09ea1d8f1f9a3629896aa5cb3d99b9d91c59e0b Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sun, 2 Dec 2007 05:53:31 +0000 Subject: Implement polyline, polygon, more path segment types, and more transforms. svn path=/trunk/netsurf/; revision=3659 --- image/svg.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 222 insertions(+), 51 deletions(-) (limited to 'image/svg.c') diff --git a/image/svg.c b/image/svg.c index 6cfbe30f9..d7c3bd6d6 100644 --- a/image/svg.c +++ b/image/svg.c @@ -22,6 +22,7 @@ #define _GNU_SOURCE /* for strndup */ #include +#include #include #include #include @@ -67,6 +68,8 @@ static bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state); static bool svg_redraw_rect(xmlNode *rect, struct svg_redraw_state state); static bool svg_redraw_circle(xmlNode *circle, struct svg_redraw_state state); static bool svg_redraw_line(xmlNode *line, struct svg_redraw_state state); +static bool svg_redraw_poly(xmlNode *poly, struct svg_redraw_state state, + bool polygon); static bool svg_redraw_text(xmlNode *text, struct svg_redraw_state state); static void svg_parse_position_attributes(const xmlNode *node, const struct svg_redraw_state state, @@ -242,6 +245,10 @@ bool svg_redraw_svg(xmlNode *svg, struct svg_redraw_state state) ok = svg_redraw_circle(child, state); else if (strcmp(child->name, "line") == 0) ok = svg_redraw_line(child, state); + else if (strcmp(child->name, "polyline") == 0) + ok = svg_redraw_poly(child, state, false); + else if (strcmp(child->name, "polygon") == 0) + ok = svg_redraw_poly(child, state, true); else if (strcmp(child->name, "text") == 0) ok = svg_redraw_text(child, state); } @@ -256,6 +263,8 @@ bool svg_redraw_svg(xmlNode *svg, struct svg_redraw_state state) /** * Redraw a element node. + * + * http://www.w3.org/TR/SVG11/paths#PathElement */ bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state) @@ -285,15 +294,15 @@ bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state) s[i] = ' '; unsigned int i = 0; float last_x = 0, last_y = 0; + float last_cubic_x = 0, last_cubic_y = 0; + float last_quad_x = 0, last_quad_y = 0; while (*s) { char command[2]; int plot_command; float x, y, x1, y1, x2, y2; int n; - /*LOG(("s \"%s\"", s));*/ - - /* M, m, L, l (2 arguments) */ + /* moveto (M, m), lineto (L, l) (2 arguments) */ if (sscanf(s, " %1[MmLl] %f %f %n", command, &x, &y, &n) == 3) { /*LOG(("moveto or lineto"));*/ if (*command == 'M' || *command == 'm') @@ -306,43 +315,47 @@ bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state) x += last_x; y += last_y; } - p[i++] = last_x = x; - p[i++] = last_y = y; + p[i++] = last_cubic_x = last_quad_x = last_x + = x; + p[i++] = last_cubic_y = last_quad_y = last_y + = y; s += n; plot_command = PLOTTER_PATH_LINE; } while (sscanf(s, "%f %f %n", &x, &y, &n) == 2); - /* Z, z (no arguments) */ + /* closepath (Z, z) (no arguments) */ } else if (sscanf(s, " %1[Zz] %n", command, &n) == 1) { /*LOG(("closepath"));*/ p[i++] = PLOTTER_PATH_CLOSE; s += n; - /* H, h (1 argument) */ + /* horizontal lineto (H, h) (1 argument) */ } else if (sscanf(s, " %1[Hh] %f %n", command, &x, &n) == 2) { /*LOG(("horizontal lineto"));*/ do { p[i++] = PLOTTER_PATH_LINE; if (*command == 'h') x += last_x; - p[i++] = last_x = x; - p[i++] = last_y; + p[i++] = last_cubic_x = last_quad_x = last_x + = x; + p[i++] = last_cubic_y = last_quad_y = last_y; s += n; } while (sscanf(s, "%f %n", &x, &n) == 1); - /* V, v (1 argument) */ + /* vertical lineto (V, v) (1 argument) */ } else if (sscanf(s, " %1[Vv] %f %n", command, &y, &n) == 2) { /*LOG(("vertical lineto"));*/ do { p[i++] = PLOTTER_PATH_LINE; if (*command == 'v') y += last_y; - p[i++] = last_x; - p[i++] = last_y = y; + p[i++] = last_cubic_x = last_quad_x = last_x; + p[i++] = last_cubic_y = last_quad_y = last_y + = y; s += n; } while (sscanf(s, "%f %n", &x, &n) == 1); - /* C, c (6 arguments) */ + /* curveto (C, c) (6 arguments) */ } else if (sscanf(s, " %1[Cc] %f %f %f %f %f %f %n", command, &x1, &y1, &x2, &y2, &x, &y, &n) == 7) { /*LOG(("curveto"));*/ @@ -358,14 +371,89 @@ bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state) } p[i++] = x1; p[i++] = y1; - p[i++] = x2; - p[i++] = y2; - p[i++] = last_x = x; - p[i++] = last_y = y; + p[i++] = last_cubic_x = x2; + p[i++] = last_cubic_y = y2; + p[i++] = last_quad_x = last_x = x; + p[i++] = last_quad_y = last_y = y; s += n; } while (sscanf(s, "%f %f %f %f %f %f %n", &x1, &y1, &x2, &y2, &x, &y, &n) == 6); + /* shorthand/smooth curveto (S, s) (4 arguments) */ + } else if (sscanf(s, " %1[Ss] %f %f %f %f %n", command, + &x2, &y2, &x, &y, &n) == 5) { + /*LOG(("shorthand/smooth curveto"));*/ + do { + p[i++] = PLOTTER_PATH_BEZIER; + x1 = last_x + (last_x - last_cubic_x); + y1 = last_y + (last_y - last_cubic_y); + if (*command == 's') { + x2 += last_x; + y2 += last_y; + x += last_x; + y += last_y; + } + p[i++] = x1; + p[i++] = y1; + p[i++] = last_cubic_x = x2; + p[i++] = last_cubic_y = y2; + p[i++] = last_quad_x = last_x = x; + p[i++] = last_quad_y = last_y = y; + s += n; + } while (sscanf(s, "%f %f %f %f %n", + &x2, &y2, &x, &y, &n) == 4); + + /* quadratic Bezier curveto (Q, q) (4 arguments) */ + } else if (sscanf(s, " %1[Qq] %f %f %f %f %n", command, + &x1, &y1, &x, &y, &n) == 5) { + /*LOG(("quadratic Bezier curveto"));*/ + do { + p[i++] = PLOTTER_PATH_BEZIER; + last_quad_x = x1; + last_quad_y = y1; + if (*command == 'q') { + x1 += last_x; + y1 += last_y; + x += last_x; + y += last_y; + } + p[i++] = 1./3 * last_x + 2./3 * x1; + p[i++] = 1./3 * last_y + 2./3 * y1; + p[i++] = 2./3 * x1 + 1./3 * x; + p[i++] = 2./3 * y1 + 1./3 * y; + p[i++] = last_cubic_x = last_x = x; + p[i++] = last_cubic_y = last_y = y; + s += n; + } while (sscanf(s, "%f %f %f %f %n", + &x1, &y1, &x, &y, &n) == 4); + + /* shorthand/smooth quadratic Bezier curveto (T, t) + (2 arguments) */ + } else if (sscanf(s, " %1[Tt] %f %f %n", command, + &x, &y, &n) == 3) { + /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/ + do { + p[i++] = PLOTTER_PATH_BEZIER; + x1 = last_x + (last_x - last_quad_x); + y1 = last_y + (last_y - last_quad_y); + last_quad_x = x1; + last_quad_y = y1; + if (*command == 't') { + x1 += last_x; + y1 += last_y; + x += last_x; + y += last_y; + } + p[i++] = 1./3 * last_x + 2./3 * x1; + p[i++] = 1./3 * last_y + 2./3 * y1; + p[i++] = 2./3 * x1 + 1./3 * x; + p[i++] = 2./3 * y1 + 1./3 * y; + p[i++] = last_cubic_x = last_x = x; + p[i++] = last_cubic_y = last_y = y; + s += n; + } while (sscanf(s, "%f %f %n", + &x, &y, &n) == 2); + } else { LOG(("parse failed at \"%s\"", s)); break; @@ -390,6 +478,8 @@ bool svg_redraw_path(xmlNode *path, struct svg_redraw_state state) /** * Redraw a element node. + * + * http://www.w3.org/TR/SVG11/shapes#RectElement */ bool svg_redraw_rect(xmlNode *rect, struct svg_redraw_state state) @@ -401,31 +491,14 @@ bool svg_redraw_rect(xmlNode *rect, struct svg_redraw_state state) svg_parse_paint_attributes(rect, &state); svg_parse_transform_attributes(rect, &state); - int p[8] = { x, y, - x + width, y, - x + width, y + height, - x, y + height }; - for (unsigned int i = 0; i != 8; i += 2) { - p[i] = state.origin_x + state.ctm.a * p[i] + - state.ctm.c * p[i+1] + state.ctm.e; - p[i+1] = state.origin_y + state.ctm.b * p[i] + - state.ctm.d * p[i+1] + state.ctm.f; - } + float p[] = { PLOTTER_PATH_MOVE, x, y, + PLOTTER_PATH_LINE, x + width, y, + PLOTTER_PATH_LINE, x + width, y + height, + PLOTTER_PATH_LINE, x, y + height, + PLOTTER_PATH_CLOSE }; - if (state.fill != TRANSPARENT) - if (!plot.polygon(p, 4, state.fill)) - return false; - - if (state.stroke != TRANSPARENT) { - for (unsigned int i = 0; i != 8; i += 2) { - if (!plot.line(p[i], p[i+1], p[(i+2)%8], p[(i+3)%8], - state.stroke_width, state.stroke, - false, false)) - return false; - } - } - - return true; + return plot.path(p, sizeof p / sizeof p[0], state.fill, + state.stroke_width, state.stroke, &state.ctm.a); } @@ -508,6 +581,70 @@ bool svg_redraw_line(xmlNode *line, struct svg_redraw_state state) } +/** + * Redraw a or element node. + * + * http://www.w3.org/TR/SVG11/shapes#PolylineElement + * http://www.w3.org/TR/SVG11/shapes#PolygonElement + */ + +bool svg_redraw_poly(xmlNode *poly, struct svg_redraw_state state, + bool polygon) +{ + char *s, *points; + + svg_parse_paint_attributes(poly, &state); + svg_parse_transform_attributes(poly, &state); + + /* read d attribute */ + s = points = (char *) xmlGetProp(poly, (const xmlChar *) "points"); + if (!s) { + LOG(("poly missing d attribute")); + return false; + } + + /* allocate space for path: it will never have more elements than s */ + float *p = malloc(sizeof p[0] * strlen(s)); + if (!p) { + LOG(("out of memory")); + return false; + } + + /* parse s and build path */ + for (unsigned int i = 0; s[i]; i++) + if (s[i] == ',') + s[i] = ' '; + unsigned int i = 0; + while (*s) { + float x, y; + int n; + + if (sscanf(s, "%f %f %n", &x, &y, &n) == 2) { + if (i == 0) + p[i++] = PLOTTER_PATH_MOVE; + else + p[i++] = PLOTTER_PATH_LINE; + p[i++] = x; + p[i++] = y; + s += n; + } else { + break; + } + } + if (polygon) + p[i++] = PLOTTER_PATH_CLOSE; + + xmlFree(points); + + bool ok = plot.path(p, i, state.fill, state.stroke_width, state.stroke, + &state.ctm.a); + + free(p); + + return ok; +} + + /** * Redraw a or element node. */ @@ -731,6 +868,8 @@ void svg_parse_font_attributes(const xmlNode *node, /** * Parse transform attributes, if present. + * + * http://www.w3.org/TR/SVG11/coords#TransformAttribute */ void svg_parse_transform_attributes(xmlNode *node, @@ -739,6 +878,7 @@ void svg_parse_transform_attributes(xmlNode *node, char *transform, *s; float a, b, c, d, e, f; float ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f; + float angle, x, y; int n; /* parse transform */ @@ -750,19 +890,50 @@ void svg_parse_transform_attributes(xmlNode *node, transform[i] = ' '; while (*s) { + a = d = 1; + b = c = 0; + e = f = 0; if (sscanf(s, "matrix (%f %f %f %f %f %f) %n", - &a, &b, &c, &d, &e, &f, &n) == 6) { + &a, &b, &c, &d, &e, &f, &n) == 6) + ; + else if (sscanf(s, "translate (%f %f) %n", + &e, &f, &n) == 2) + ; + else if (sscanf(s, "translate (%f) %n", + &e, &n) == 1) ; - } else if (sscanf(s, "translate (%f %f) %n", - &e, &f, &n) == 2) { - a = d = 1; - b = c = 0; - } else if (sscanf(s, "scale (%f %f) %n", - &a, &d, &n) == 2) { - b = c = e = f = 0; - } else { + else if (sscanf(s, "scale (%f %f) %n", + &a, &d, &n) == 2) + ; + else if (sscanf(s, "scale (%f) %n", + &a, &n) == 1) + d = a; + else if (sscanf(s, "rotate (%f %f %f) %n", + &angle, &x, &y, &n) == 3) { + angle = -angle / 180 * M_PI; + a = cos(angle); + b = sin(angle); + c = -sin(angle); + d = cos(angle); + e = -x * cos(angle) + y * sin(angle) + x; + f = -x * sin(angle) - y * cos(angle) + y; + } else if (sscanf(s, "rotate (%f) %n", + &angle, &n) == 1) { + angle = -angle / 180 * M_PI; + a = cos(angle); + b = sin(angle); + c = -sin(angle); + d = cos(angle); + } else if (sscanf(s, "skewX (%f) %n", + &angle, &n) == 1) { + angle = angle / 180 * M_PI; + c = tan(angle); + } else if (sscanf(s, "skewY (%f) %n", + &angle, &n) == 1) { + angle = angle / 180 * M_PI; + b = tan(angle); + } else break; - } ctm_a = state->ctm.a * a + state->ctm.c * b; ctm_b = state->ctm.b * a + state->ctm.d * b; ctm_c = state->ctm.a * c + state->ctm.c * d; -- cgit v1.2.3