From 0c4317085ecfc112214cdfe2abd66d9df8038282 Mon Sep 17 00:00:00 2001 From: Emmanuel Pacaud Date: Sat, 27 Sep 2008 11:47:32 +0200 Subject: [PATCH] [svg] Fix comp-op for paint, mask and show_glyphs operations. In paint and show_glyphs, the compositing operator was not emitted at all. In mask, the operator was also emitted for the mask itself, which is wrong. SVG clear and source differ from cairo as it also affects the destination if the source pixel are completely transparent. We need to emit an additional clip-to-self property. Librsvg does not support clip-to-self, so it renders the SVG test outputs incorrectly. This patch also remove a lot of useless spaces in the style property strings (I know, this should go in another commit). --- src/cairo-svg-surface.c | 196 +++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 83 deletions(-) diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index e8567bcb5..889086510 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -366,8 +366,8 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, if (content == CAIRO_CONTENT_COLOR) { _cairo_output_stream_printf (surface->xml_node, "\n", + "style=\"opacity:1;stroke:none;" + "fill:rgb(0,0,0);\"/>\n", width, height); status = _cairo_output_stream_get_status (surface->xml_node); if (status) @@ -631,7 +631,7 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, return status; _cairo_output_stream_printf (document->xml_node_glyphs, - "xml_node_glyphs, scaled_glyph->path, NULL); if (status) @@ -706,8 +706,8 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_glyphs, "\n", - font_id, - subset_glyph_index); + font_id, + subset_glyph_index); status = _cairo_svg_document_emit_outline_glyph_data (document, scaled_font, @@ -967,9 +967,52 @@ _cairo_surface_base64_encode (cairo_surface_t *surface, return status; } +static struct { + char const *str; + cairo_bool_t clip_to_self; +} _cairo_svg_surface_operators[] = { + {"clear", TRUE}, + + {"src", TRUE}, {"src-over", FALSE}, {"src-in", FALSE}, + {"src-out", FALSE}, {"src-atop", FALSE}, + + {"dst", FALSE}, {"dst-over", FALSE}, {"dst-in", FALSE}, + {"dst-out", FALSE}, {"dst-atop", FALSE}, + + {"xor", FALSE}, {"plus", FALSE}, + {"color-dodge", FALSE} /* FIXME: saturate ? */ +}; + +static void +_cairo_svg_surface_emit_operator (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op].str); + if (_cairo_svg_surface_operators[op].clip_to_self) + _cairo_output_stream_printf (output, " clip-to-self=\"true\""); + } +} + +static void +_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op].str); + if (_cairo_svg_surface_operators[op].clip_to_self) + _cairo_output_stream_printf (output, "clip-to-self:true;"); + } +} + static cairo_status_t _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *svg_surface, + cairo_operator_t op, cairo_surface_pattern_t *pattern, int pattern_id, const cairo_matrix_t *parent_matrix, @@ -1012,8 +1055,10 @@ _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output " surface)) { - return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, pattern, + return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, op, pattern, pattern_id, parent_matrix, extra_attributes); } - return _cairo_svg_surface_emit_composite_image_pattern (output, surface, pattern, + return _cairo_svg_surface_emit_composite_image_pattern (output, surface, op, pattern, pattern_id, parent_matrix, extra_attributes); } -static void -_cairo_svg_surface_emit_operator (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op) -{ - char const *op_str[] = { - "clear", - - "src", "src-over", "src-in", - "src-out", "src-atop", - - "dst", "dst-over", "dst-in", - "dst-out", "dst-atop", - - "xor", "plus", - "color-dodge" /* FIXME: saturate ? */ - }; - - if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) - _cairo_output_stream_printf (output, "comp-op: %s; ", op_str[op]); -} - static cairo_status_t _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface, cairo_solid_pattern_t *pattern, @@ -1265,8 +1292,8 @@ _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface, cairo_bool_t is_stroke) { _cairo_output_stream_printf (style, is_stroke ? - "stroke: rgb(%f%%,%f%%,%f%%); stroke-opacity: %f;": - "fill: rgb(%f%%,%f%%,%f%%); fill-opacity: %f;", + "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;": + "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;", pattern->color.red * 100.0, pattern->color.green * 100.0, pattern->color.blue * 100.0, @@ -1288,13 +1315,13 @@ _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, pattern_id = document->pattern_id++; status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs, - surface, pattern, + surface, CAIRO_OPERATOR_SOURCE, pattern, pattern_id, parent_matrix, NULL); if (status) return status; _cairo_output_stream_printf (style, - "%s: url(#pattern%d);", + "%s:url(#pattern%d);", is_stroke ? "stroke" : "fill", pattern_id); @@ -1319,8 +1346,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, if (pattern->n_stops == 1) { _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", pattern->stops[0].offset, pattern->stops[0].color.red * 100.0, pattern->stops[0].color.green * 100.0, @@ -1366,8 +1393,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, offset = start_offset + (1 - start_offset ) * stops[i].offset; _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, @@ -1418,8 +1445,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", offset_color_start.red * 100.0, offset_color_start.green * 100.0, offset_color_start.blue * 100.0, @@ -1427,8 +1454,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, for (i = offset_index; i < n_stops; i++) { _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", stops[i].offset + start_offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, @@ -1438,8 +1465,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, for (i = 0; i < offset_index; i++) { _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", 1.0 + stops[i].offset + start_offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, @@ -1449,8 +1476,8 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, _cairo_output_stream_printf (output, "\n", + "stop-color:rgb(%f%%,%f%%,%f%%);" + "stop-opacity:%f;\"/>\n", offset_color_stop.red * 100.0, offset_color_stop.green * 100.0, offset_color_stop.blue * 100.0, @@ -1524,7 +1551,7 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, "\n"); _cairo_output_stream_printf (style, - "%s: url(#linear%d);", + "%s:url(#linear%d);", is_stroke ? "stroke" : "fill", document->linear_pattern_id); @@ -1595,13 +1622,13 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, if (extend == CAIRO_EXTEND_NONE || n_stops < 1) _cairo_output_stream_printf (document->xml_node_defs, "\n"); + "stop-color:rgb(0%%,0%%,0%%);" + "stop-opacity:0;\"/>\n"); else { _cairo_output_stream_printf (document->xml_node_defs, "\n", + "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, @@ -1609,8 +1636,8 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, if (n_stops > 1) _cairo_output_stream_printf (document->xml_node_defs, "\n", + "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, @@ -1682,13 +1709,13 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, if (extend == CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (document->xml_node_defs, "\n"); + "stop-color:rgb(0%%,0%%,0%%);" + "stop-opacity:0;\"/>\n"); if (r0 != 0.0) _cairo_output_stream_printf (document->xml_node_defs, "\n", + "stop-color:rgb(0%%,0%%,0%%);" + "stop-opacity:0;\"/>\n", r0 / r1); } status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, @@ -1701,15 +1728,15 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, if (pattern->base.base.extend == CAIRO_EXTEND_NONE) _cairo_output_stream_printf (document->xml_node_defs, "\n"); + "stop-color:rgb(0%%,0%%,0%%);" + "stop-opacity:0;\"/>\n"); } _cairo_output_stream_printf (document->xml_node_defs, "\n"); _cairo_output_stream_printf (style, - "%s: url(#radial%d);", + "%s:url(#radial%d);", is_stroke ? "stroke" : "fill", document->radial_pattern_id); @@ -1754,10 +1781,10 @@ _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, cairo_matrix_t *parent_matrix) { _cairo_output_stream_printf (output, - "fill-rule: %s; ", + "fill-rule:%s;", fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd" : "nonzero"); - _cairo_svg_surface_emit_operator (output, surface, op); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); } @@ -1802,9 +1829,9 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, } _cairo_output_stream_printf (output, - "stroke-width: %f; " - "stroke-linecap: %s; " - "stroke-linejoin: %s; ", + "stroke-width:%f;" + "stroke-linecap:%s;" + "stroke-linejoin:%s;", stroke_style->line_width, line_cap, line_join); @@ -1813,27 +1840,27 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, if (status) return status; - _cairo_svg_surface_emit_operator (output, surface, op); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); if (stroke_style->num_dashes > 0) { - _cairo_output_stream_printf (output, "stroke-dasharray: "); + _cairo_output_stream_printf (output, "stroke-dasharray:"); for (i = 0; i < stroke_style->num_dashes; i++) { _cairo_output_stream_printf (output, "%f", stroke_style->dash[i]); if (i + 1 < stroke_style->num_dashes) _cairo_output_stream_printf (output, ","); else - _cairo_output_stream_printf (output, "; "); + _cairo_output_stream_printf (output, ";"); } if (stroke_style->dash_offset != 0.0) { _cairo_output_stream_printf (output, - "stroke-dashoffset: %f; ", + "stroke-dashoffset:%f;", stroke_style->dash_offset); } } _cairo_output_stream_printf (output, - "stroke-miterlimit: %f; ", + "stroke-miterlimit:%f;", stroke_style->miter_limit); return CAIRO_STATUS_SUCCESS; @@ -1946,23 +1973,24 @@ _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, if (source->type == CAIRO_PATTERN_TYPE_SURFACE && source->extend == CAIRO_EXTEND_NONE) return _cairo_svg_surface_emit_composite_pattern (output, - surface, - (cairo_surface_pattern_t *) source, - invalid_pattern_id, - mask_source ? &mask_source->matrix :NULL, - extra_attributes); + surface, + op, + (cairo_surface_pattern_t *) source, + invalid_pattern_id, + mask_source ? &mask_source->matrix :NULL, + extra_attributes); _cairo_output_stream_printf (output, "width, surface->height); - _cairo_svg_surface_emit_operator (output, surface, op); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); if (status) return status; - _cairo_output_stream_printf (output, " stroke: none;\""); + _cairo_output_stream_printf (output, "stroke:none;\""); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); @@ -2023,9 +2051,9 @@ _cairo_svg_surface_paint (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "\n", + "style=\"opacity:1;" + "stroke:none;" + "fill:rgb(0,0,0);\"/>\n", surface->width, surface->height); } return CAIRO_STATUS_SUCCESS; @@ -2078,7 +2106,7 @@ _cairo_svg_surface_mask (void *abstract_surface, "%s", mask_id, discard_filter ? "" : " \n"); - status = _cairo_svg_surface_emit_paint (mask_stream, surface, op, mask, source, NULL); + status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); if (status) { cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); return status; @@ -2123,7 +2151,7 @@ _cairo_svg_surface_stroke (void *abstract_dst, assert (_cairo_svg_surface_operation_supported (surface, op, source)); - _cairo_output_stream_printf (surface->xml_node, "xml_node, "xml_node, surface, op, source, stroke_style, ctm_inverse); if (status) @@ -2177,6 +2205,8 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, if (status) return status; + _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); + _cairo_output_stream_printf (surface->xml_node, "\">\n"); for (i = 0; i < num_glyphs; i++) {