[cairo-commit] src/cairo-svg-surface.c
Carl Worth
cworth at kemper.freedesktop.org
Tue Mar 6 15:52:25 PST 2007
src/cairo-svg-surface.c | 341 +++++++++++++++++++++++++++++++++++++++---------
1 files changed, 279 insertions(+), 62 deletions(-)
New commits:
diff-tree 734d32ed7a50284fcc8984af67734bb306735691 (from 32536a7b794c38ff1944b8af5e56e8962e76c311)
Author: Emmanuel Pacaud <emmanuel.pacaud at lapp.in2p3.fr>
Date: Tue Mar 6 15:49:53 2007 -0800
SVG: Fix CAIRO_EXTEND_REFLECT for radial gradients.
This patch also handles cases where r0 > r1, (one circle must still
be wholly contained within the other as that's all SVG supports).
This patch should also prevent a crash when r0 == r1.
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 7cb7d4d..4e318c1 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1191,41 +1191,162 @@ static void
emit_pattern_stops (cairo_output_stream_t *output,
cairo_gradient_pattern_t const *pattern,
double start_offset,
- cairo_extend_t extend)
+ cairo_bool_t reverse_stops,
+ cairo_bool_t emulate_reflect)
{
+ pixman_gradient_stop_t *stops;
double offset;
- unsigned int i, stop_index;
+ unsigned int n_stops;
+ unsigned int i;
+
+ if (pattern->n_stops < 1)
+ return;
+
+ if (pattern->n_stops == 1) {
+ _cairo_output_stream_printf (output,
+ "<stop offset=\"%f\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ _cairo_fixed_to_double (pattern->stops[0].x),
+ pattern->stops[0].color.red / 655.35,
+ pattern->stops[0].color.green / 655.35,
+ pattern->stops[0].color.blue / 655.35,
+ pattern->stops[0].color.alpha / 65535.0);
+ return;
+ }
+
+ if (emulate_reflect || reverse_stops) {
+ n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
+ stops = malloc (sizeof (pixman_gradient_stop_t) * n_stops);
- if (start_offset > 0.0
- && (extend == CAIRO_EXTEND_REPEAT
- || extend == CAIRO_EXTEND_REFLECT))
for (i = 0; i < pattern->n_stops; i++) {
- offset = start_offset *
- _cairo_fixed_to_double (pattern->stops[i].x);
- stop_index = (extend == CAIRO_EXTEND_REPEAT) ? i : pattern->n_stops - i - 1;
+ if (reverse_stops) {
+ stops[i] = pattern->stops[pattern->n_stops - i - 1];
+ stops[i].x = _cairo_fixed_from_double (1.0 - _cairo_fixed_to_double (stops[i].x));
+ } else
+ stops[i] = pattern->stops[i];
+ if (emulate_reflect) {
+ stops[i].x /= 2;
+ if (i > 0 && i < (pattern->n_stops - 1)) {
+ if (reverse_stops) {
+ stops[i + pattern->n_stops - 1] = pattern->stops[i];
+ stops[i + pattern->n_stops - 1].x =
+ _cairo_fixed_from_double (0.5 + 0.5
+ * _cairo_fixed_to_double (stops[i + pattern->n_stops - 1].x));
+ } else {
+ stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
+ stops[i + pattern->n_stops - 1].x =
+ _cairo_fixed_from_double (1 - 0.5
+ * _cairo_fixed_to_double (stops [i + pattern->n_stops - 1].x));
+ }
+ }
+ }
+ }
+ } else {
+ n_stops = pattern->n_stops;
+ stops = pattern->stops;
+ }
+
+ if (start_offset >= 0.0)
+ for (i = 0; i < n_stops; i++) {
+ offset = start_offset + (1 - start_offset ) *
+ _cairo_fixed_to_double (stops[i].x);
_cairo_output_stream_printf (output,
"<stop offset=\"%f\" style=\""
"stop-color: rgb(%f%%,%f%%,%f%%); "
"stop-opacity: %f;\"/>\n",
offset,
- pattern->stops[stop_index].color.red / 655.35,
- pattern->stops[stop_index].color.green / 655.35,
- pattern->stops[stop_index].color.blue / 655.35,
- pattern->stops[stop_index].color.alpha / 65535.0);
- }
- for (i = 0; i < pattern->n_stops; i++) {
- offset = start_offset + (1 - start_offset ) *
- _cairo_fixed_to_double (pattern->stops[i].x);
+ stops[i].color.red / 655.35,
+ stops[i].color.green / 655.35,
+ stops[i].color.blue / 655.35,
+ stops[i].color.alpha / 65535.0);
+ }
+ else {
+ cairo_bool_t found = FALSE;
+ unsigned int offset_index;
+ pixman_color_t offset_color_start, offset_color_stop;
+
+ for (i = 0; i < n_stops; i++) {
+ if (_cairo_fixed_to_double (stops[i].x) >= -start_offset) {
+ if (i > 0) {
+ if (stops[i].x != stops[i-1].x) {
+ double x0, x1;
+ pixman_color_t *color0, *color1;
+
+ x0 = _cairo_fixed_to_double (stops[i-1].x);
+ x1 = _cairo_fixed_to_double (stops[i].x);
+ color0 = &stops[i-1].color;
+ color1 = &stops[i].color;
+ offset_color_start.red = color0->red + (color1->red - color0->red)
+ * (-start_offset - x0) / (x1 - x0);
+ offset_color_start.green = color0->green + (color1->green - color0->green)
+ * (-start_offset - x0) / (x1 - x0);
+ offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
+ * (-start_offset - x0) / (x1 - x0);
+ offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
+ * (-start_offset - x0) / (x1 - x0);
+ offset_color_stop = offset_color_start;
+ } else {
+ offset_color_stop = stops[i-1].color;
+ offset_color_start = stops[i].color;
+ }
+ } else
+ offset_color_stop = offset_color_start = stops[i].color;
+ offset_index = i;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ offset_index = n_stops - 1;
+ offset_color_stop = offset_color_start = stops[offset_index].color;
+ }
+
_cairo_output_stream_printf (output,
- "<stop offset=\"%f\" style=\""
+ "<stop offset=\"0\" style=\""
"stop-color: rgb(%f%%,%f%%,%f%%); "
"stop-opacity: %f;\"/>\n",
- offset,
- pattern->stops[i].color.red / 655.35,
- pattern->stops[i].color.green / 655.35,
- pattern->stops[i].color.blue / 655.35,
- pattern->stops[i].color.alpha / 65535.0);
+ offset_color_start.red / 655.35,
+ offset_color_start.green / 655.35,
+ offset_color_start.blue / 655.35,
+ offset_color_start.alpha / 65535.0);
+ for (i = offset_index; i < n_stops; i++) {
+ _cairo_output_stream_printf (output,
+ "<stop offset=\"%f\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ _cairo_fixed_to_double (stops[i].x) + start_offset,
+ stops[i].color.red / 655.35,
+ stops[i].color.green / 655.35,
+ stops[i].color.blue / 655.35,
+ stops[i].color.alpha / 65535.0);
+ }
+ for (i = 0; i < offset_index; i++) {
+ _cairo_output_stream_printf (output,
+ "<stop offset=\"%f\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ 1.0 + _cairo_fixed_to_double (stops[i].x) + start_offset,
+ stops[i].color.red / 655.35,
+ stops[i].color.green / 655.35,
+ stops[i].color.blue / 655.35,
+ stops[i].color.alpha / 65535.0);
+ }
+
+ _cairo_output_stream_printf (output,
+ "<stop offset=\"1\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ offset_color_stop.red / 655.35,
+ offset_color_stop.green / 655.35,
+ offset_color_stop.blue / 655.35,
+ offset_color_stop.alpha / 65535.0);
+
}
+
+ if (reverse_stops || emulate_reflect)
+ free (stops);
}
static void
@@ -1272,7 +1393,7 @@ emit_linear_pattern (cairo_svg_surface_t
cairo_matrix_invert (&p2u);
emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
- emit_pattern_stops (document->xml_node_defs ,&pattern->base, 0.0, CAIRO_EXTEND_NONE);
+ emit_pattern_stops (document->xml_node_defs ,&pattern->base, 0.0, FALSE, FALSE);
_cairo_output_stream_printf (document->xml_node_defs,
"</linearGradient>\n");
@@ -1293,57 +1414,153 @@ emit_radial_pattern (cairo_svg_surface_t
{
cairo_svg_document_t *document = surface->document;
cairo_matrix_t p2u;
+ cairo_extend_t extend;
double x0, y0, x1, y1, r0, r1;
double fx, fy;
+ cairo_bool_t reverse_stops;
+ pixman_circle_t *c0, *c1;
- x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
- y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
- r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
- x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
- y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
- r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
-
- /* SVG doesn't have a start radius, so computing now SVG focal coordinates
- * and emulating start radius by translating color stops.
- * FIXME: Handle radius1 <= radius0 */
- fx = (r1 * x0 - r0 * x1) / (r1 - r0);
- fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+ extend = pattern->base.base.extend;
- _cairo_output_stream_printf (document->xml_node_defs,
- "<radialGradient id=\"radial%d\" "
- "gradientUnits=\"userSpaceOnUse\" "
- "cx=\"%f\" cy=\"%f\" "
- "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
- document->radial_pattern_id,
- x1, y1,
- fx, fy, r1);
+ if (pattern->gradient.c1.radius < pattern->gradient.c2.radius) {
+ c0 = &pattern->gradient.c1;
+ c1 = &pattern->gradient.c2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &pattern->gradient.c2;
+ c1 = &pattern->gradient.c1;
+ reverse_stops = TRUE;
+ }
+
+ x0 = _cairo_fixed_to_double (c0->x);
+ y0 = _cairo_fixed_to_double (c0->y);
+ r0 = _cairo_fixed_to_double (c0->radius);
+ x1 = _cairo_fixed_to_double (c1->x);
+ y1 = _cairo_fixed_to_double (c1->y);
+ r1 = _cairo_fixed_to_double (c1->radius);
- emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
p2u = pattern->base.base.matrix;
cairo_matrix_invert (&p2u);
- emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
- /* To support cairo's EXTEND_NONE, (for which SVG has no similar
- * notion), we add transparent color stops on either end of the
- * user-provided stops. */
- if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+ if (pattern->gradient.c1.radius == pattern->gradient.c2.radius) {
_cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"0\" style=\""
- "stop-color: rgb(0%%,0%%,0%%); "
- "stop-opacity: 0;\"/>\n");
- if (r0 != 0.0)
+ "<radialGradient id=\"radial%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "cx=\"%f\" cy=\"%f\" "
+ "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+ document->radial_pattern_id,
+ x1, y1,
+ x1, y1, r1);
+
+ emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
+
+ if (extend == CAIRO_EXTEND_NONE ||
+ pattern->base.n_stops < 1)
_cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"%f\" style=\""
+ "<stop offset=\"0\" style=\""
"stop-color: rgb(0%%,0%%,0%%); "
- "stop-opacity: 0;\"/>\n",
- r0 / r1);
- }
- emit_pattern_stops (document->xml_node_defs, &pattern->base, r0 / r1, pattern->base.base.extend);
- if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
+ "stop-opacity: 0;\"/>\n");
+ else {
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"0\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ pattern->base.stops[0].color.red / 655.35,
+ pattern->base.stops[0].color.green / 655.35,
+ pattern->base.stops[0].color.blue / 655.35,
+ pattern->base.stops[0].color.alpha / 65535.0);
+ if (pattern->base.n_stops > 1)
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"0\" style=\""
+ "stop-color: rgb(%f%%,%f%%,%f%%); "
+ "stop-opacity: %f;\"/>\n",
+ pattern->base.stops[1].color.red / 655.35,
+ pattern->base.stops[1].color.green / 655.35,
+ pattern->base.stops[1].color.blue / 655.35,
+ pattern->base.stops[1].color.alpha / 65535.0);
+ }
+
+ } else {
+ double offset, r, x, y;
+ cairo_bool_t emulate_reflect = FALSE;
+
+ fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+ fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+
+ /* SVG doesn't support the inner circle and use instead a gradient focal.
+ * That means we need to emulate the cairo behaviour by processing the
+ * cairo gradient stops.
+ * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
+ * it's just a matter of stop position translation and calculation of
+ * the corresponding SVG radial gradient focal.
+ * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
+ * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
+ * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
+ * list that maps to the original cairo stop list.
+ */
+ if ((extend == CAIRO_EXTEND_REFLECT
+ || extend == CAIRO_EXTEND_REPEAT)
+ && r0 > 0.0) {
+ offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+ r = r1 - r0;
+
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ r *= 2.0;
+ offset *= 0.5;
+ emulate_reflect = TRUE;
+ }
+
+ /* New position of outer circle. */
+ x = r * (x1 - fx) / r1 + fx;
+ y = r * (y1 - fy) / r1 + fy;
+
+ x1 = x;
+ y1 = y;
+ r1 = r;
+
+ r0 = 0.0;
+ } else {
+ offset = r0 / r1;
+ }
+
_cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"1.0\" style=\""
- "stop-color: rgb(0%%,0%%,0%%); "
- "stop-opacity: 0;\"/>\n");
+ "<radialGradient id=\"radial%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "cx=\"%f\" cy=\"%f\" "
+ "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+ document->radial_pattern_id,
+ x1, y1,
+ fx, fy, r1);
+
+ if (emulate_reflect)
+ _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
+ else
+ emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
+ emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
+
+ /* To support cairo's EXTEND_NONE, (for which SVG has no similar
+ * notion), we add transparent color stops on either end of the
+ * user-provided stops. */
+ if (extend == CAIRO_EXTEND_NONE) {
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"0\" style=\""
+ "stop-color: rgb(0%%,0%%,0%%); "
+ "stop-opacity: 0;\"/>\n");
+ if (r0 != 0.0)
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"%f\" style=\""
+ "stop-color: rgb(0%%,0%%,0%%); "
+ "stop-opacity: 0;\"/>\n",
+ r0 / r1);
+ }
+ emit_pattern_stops (document->xml_node_defs, &pattern->base, offset,
+ reverse_stops, emulate_reflect);
+ if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"1.0\" style=\""
+ "stop-color: rgb(0%%,0%%,0%%); "
+ "stop-opacity: 0;\"/>\n");
+ }
_cairo_output_stream_printf (document->xml_node_defs,
"</radialGradient>\n");
More information about the cairo-commit
mailing list