summaryrefslogtreecommitdiff
path: root/src/cairo-svg-surface.c
diff options
context:
space:
mode:
authorAnton Danilkin <afdw@yandex.ru>2021-04-10 18:39:18 +0200
committerAnton Danilkin <afdw@yandex.ru>2021-04-11 23:59:47 +0200
commit9ac707374fa3fc6029c4c33e2193fca0b50e4109 (patch)
tree508ebadb396b34f9eaf2fb7747bd4e1e4f0f03cd /src/cairo-svg-surface.c
parentd397ab6d10123e6bbcf9ae446cc8ffd981f18222 (diff)
downloadcairo-9ac707374fa3fc6029c4c33e2193fca0b50e4109.tar.gz
Fix problems with radial gradients
Diffstat (limited to 'src/cairo-svg-surface.c')
-rw-r--r--src/cairo-svg-surface.c235
1 files changed, 104 insertions, 131 deletions
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 71f8d1215..e6103e05b 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1129,6 +1129,22 @@ _cairo_svg_surface_are_operation_and_pattern_supported (cairo_svg_surface_t *sur
return FALSE;
}
+ /* SVG 1.1 does not support the focal point (fx, fy) that is outside of the circle defined by (cx, cy) and r. */
+ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *radial_pattern = (cairo_radial_pattern_t *) pattern;
+ double max_radius;
+ if (radial_pattern->cd1.radius > radial_pattern->cd2.radius) {
+ max_radius = radial_pattern->cd1.radius;
+ } else {
+ max_radius = radial_pattern->cd2.radius;
+ }
+ cairo_point_double_t c1 = radial_pattern->cd1.center;
+ cairo_point_double_t c2 = radial_pattern->cd2.center;
+ if ((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y) >= max_radius * max_radius) {
+ return FALSE;
+ }
+ }
+
/* SVG doesn't support extends reflect and pad for surface pattern */
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && (pattern->extend != CAIRO_EXTEND_NONE &&
pattern->extend != CAIRO_EXTEND_REPEAT)) {
@@ -1959,7 +1975,7 @@ _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
static cairo_status_t
_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
- cairo_gradient_pattern_t const *pattern,
+ const cairo_gradient_pattern_t *pattern,
double start_offset,
cairo_bool_t reverse_stops,
cairo_bool_t emulate_reflect)
@@ -1998,14 +2014,14 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
stops[i] = pattern->stops[i];
}
if (emulate_reflect) {
- stops[i].offset /= 2;
- if (i > 0 && i < (pattern->n_stops - 1)) {
+ stops[i].offset *= 0.5;
+ 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].offset = 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
} else {
stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
- stops[i + pattern->n_stops - 1].offset = 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
+ stops[i + pattern->n_stops - 1].offset = 1.0 - 0.5 * stops[i + pattern->n_stops - 1].offset;
}
}
}
@@ -2021,7 +2037,7 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
"<stop offset=\"%f\" "
"stop-color=\"rgb(%f%%, %f%%, %f%%)\" "
"stop-opacity=\"%f\"/>\n",
- start_offset + (1 - start_offset) * stops[i].offset,
+ start_offset + (1.0 - start_offset) * stops[i].offset,
stops[i].color.red * 100.0,
stops[i].color.green * 100.0,
stops[i].color.blue * 100.0,
@@ -2032,14 +2048,14 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
unsigned int offset_index;
cairo_color_stop_t offset_color_start, offset_color_stop;
- for (unsigned int i = 0; i < n_stops; i++) {
- if (stops[i].offset >= -start_offset) {
+ for (unsigned int i = 0; i <= n_stops; i++) {
+ double x1 = i == n_stops ? stops[0].offset + 1 : stops[i].offset;
+ cairo_color_stop_t *color1 = i == n_stops ? &stops[0].color : &stops[i].color;
+ if (x1 >= -start_offset) {
if (i > 0) {
- if (stops[i].offset != stops[i - 1].offset) {
- double x0 = stops[i - 1].offset;
- double x1 = stops[i].offset;
- cairo_color_stop_t *color0 = &stops[i - 1].color;
- cairo_color_stop_t *color1 = &stops[i].color;
+ double x0 = stops[i - 1].offset;
+ cairo_color_stop_t *color0 = &stops[i - 1].color;
+ if (x0 != x1) {
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)
@@ -2219,136 +2235,93 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
unsigned int radial_pattern_id = document->radial_pattern_id++;
- if (r0 == r1) {
- unsigned int n_stops = pattern->base.n_stops;
+ double start_offset;
+ cairo_bool_t emulate_reflect = FALSE;
+
+ double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+ double 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_EXTEND_NONE and CAIRO_EXTEND_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_EXTEND_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_EXTEND_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) {
+ double r_org = r1;
- _cairo_output_stream_printf (document->xml_node_defs,
- "<radialGradient id=\"radial-pattern-%d\" "
- "gradientUnits=\"userSpaceOnUse\" "
- "cx=\"%f\" cy=\"%f\" "
- "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
- radial_pattern_id,
- x1, y1,
- x1, y1, r1);
- _cairo_svg_surface_emit_transform (document->xml_node_defs,
- "gradientTransform",
- &p2u,
- parent_matrix);
- _cairo_output_stream_printf (document->xml_node_defs, ">\n");
-
- if (extend == CAIRO_EXTEND_NONE || n_stops < 1) {
- _cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"0\" "
- "stop-color=\"rgb(0%%, 0%%, 0%%)\" "
- "stop-opacity=\"0\"/>\n");
- } else {
- _cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"0\" "
- "stop-color=\"rgb(%f%%, %f%%, %f%%)\" "
- "stop-opacity=\"%f\"/>\n",
- pattern->base.stops[0].color.red * 100.0,
- pattern->base.stops[0].color.green * 100.0,
- pattern->base.stops[0].color.blue * 100.0,
- pattern->base.stops[0].color.alpha);
- if (n_stops > 1) {
- _cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"0\" "
- "stop-color=\"rgb(%f%%, %f%%, %f%%)\" "
- "stop-opacity=\"%f\"/>\n",
- pattern->base.stops[n_stops - 1].color.red * 100.0,
- pattern->base.stops[n_stops - 1].color.green * 100.0,
- pattern->base.stops[n_stops - 1].color.blue * 100.0,
- pattern->base.stops[n_stops - 1].color.alpha);
- }
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ r1 = 2.0 * r1 - r0;
+ emulate_reflect = TRUE;
}
- } else {
- double offset, r, x, y;
- cairo_bool_t emulate_reflect = FALSE;
-
- double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
- double 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_EXTEND_NONE and CAIRO_EXTEND_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_EXTEND_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_EXTEND_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) {
- double r_org = r1;
-
- if (extend == CAIRO_EXTEND_REFLECT) {
- r1 = 2 * r1 - r0;
- emulate_reflect = TRUE;
- }
- offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
- r = r1 - r0;
+ start_offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+ double r = r1 - r0;
- /* New position of outer circle. */
- x = r * (x1 - fx) / r_org + fx;
- y = r * (y1 - fy) / r_org + fy;
+ /* New position of outer circle. */
+ double x = r * (x1 - fx) / r_org + fx;
+ double y = r * (y1 - fy) / r_org + fy;
- x1 = x;
- y1 = y;
- r1 = r;
- r0 = 0.0;
- } else {
- offset = r0 / r1;
- }
+ x1 = x;
+ y1 = y;
+ r1 = r;
+ r0 = 0.0;
+ } else {
+ start_offset = r0 / r1;
+ }
- _cairo_output_stream_printf (document->xml_node_defs,
- "<radialGradient id=\"radial-pattern-%d\" "
- "gradientUnits=\"userSpaceOnUse\" "
- "cx=\"%f\" cy=\"%f\" "
- "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
- radial_pattern_id,
- x1, y1,
- fx, fy, r1);
-
- if (emulate_reflect) {
- _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
- } else {
- _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
- }
- _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
- _cairo_output_stream_printf (document->xml_node_defs, ">\n");
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<radialGradient id=\"radial-pattern-%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "cx=\"%f\" cy=\"%f\" "
+ "fx=\"%f\" fy=\"%f\" r=\"%f\"",
+ radial_pattern_id,
+ x1, y1,
+ fx, fy, r1);
+
+ if (emulate_reflect) {
+ _cairo_output_stream_printf (document->xml_node_defs, " spreadMethod=\"repeat\"");
+ } else {
+ _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
+ }
+ _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
+ _cairo_output_stream_printf (document->xml_node_defs, ">\n");
- /* 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) {
+ /* 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\" "
+ "stop-color=\"rgb(0%%, 0%%, 0%%)\" "
+ "stop-opacity=\"0\"/>\n");
+ if (r0 != 0.0) {
_cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"0\" "
+ "<stop offset=\"%f\" "
"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\" "
- "stop-color=\"rgb(0%%, 0%%, 0%%)\" "
- "stop-opacity=\"0\"/>\n",
- r0 / r1);
- }
- }
- status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
- &pattern->base, offset,
- reverse_stops,
- emulate_reflect);
- if (unlikely (status)) {
- return status;
+ "stop-opacity=\"0\"/>\n",
+ r0 / r1);
}
+ }
+ status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
+ &pattern->base,
+ start_offset,
+ reverse_stops,
+ emulate_reflect);
+ if (unlikely (status)) {
+ return status;
+ }
- if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
- _cairo_output_stream_printf (document->xml_node_defs,
- "<stop offset=\"1\" "
- "stop-color=\"rgb(0%%, 0%%, 0%%)\" "
- "stop-opacity=\"0\"/>\n");
- }
+ if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"1\" "
+ "stop-color=\"rgb(0%%, 0%%, 0%%)\" "
+ "stop-opacity=\"0\"/>\n");
}
_cairo_output_stream_printf (document->xml_node_defs,