Implement polyline, polygon, more path segment types, and more transforms.
svn path=/trunk/netsurf/; revision=3659
This commit is contained in:
parent
b2c0baffcd
commit
f09ea1d8f1
273
image/svg.c
273
image/svg.c
|
@ -22,6 +22,7 @@
|
|||
|
||||
#define _GNU_SOURCE /* for strndup */
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
@ -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 <path> 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 <rect> 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 <polyline> or <polygon> 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 <text> or <tspan> 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) {
|
||||
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, "translate (%f %f) %n",
|
||||
&e, &f, &n) == 2)
|
||||
;
|
||||
else if (sscanf(s, "translate (%f) %n",
|
||||
&e, &n) == 1)
|
||||
;
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue