From 921dcc16f980fcf9a8df4c193720e8ec1e53f1dd Mon Sep 17 00:00:00 2001 From: Emmanuel Pacaud Date: Mon, 13 Feb 2006 12:41:10 +0000 Subject: [PATCH] Add mask support and switch to SVG 1.2 for compositing operators. return xml node instead of cairo status. (emit_composite_svg_pattern): ditto. (emit_composite_pattern): ditto. (emit_operator): use SVG 1.2 operators, since feComposite filter always composite resulting surface to current canvas. (_cairo_svg_surface_composite): removed. (_cairo_svg_surface_fill_rectangles): removed. (intersect): removed. (_cairo_svg_surface_composite_trapezoids): removed. (emit_paint): new. (_cairo_svg_surface_paint): new. (_cairo_svg_surface_mask): new. (_cairo_svg_surface_stroke): emit operator. --- ChangeLog | 19 +++ src/cairo-svg-surface.c | 294 ++++++++++++---------------------------- 2 files changed, 105 insertions(+), 208 deletions(-) diff --git a/ChangeLog b/ChangeLog index def0ec3c9..2dd825db3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2006-02-13 Emmanuel Pacaud + + Add mask support and switch to SVG 1.2 for compositing operators. + + * src/cairo-svg-surface.c (emit_composite_image_pattern): return xml + node instead of cairo status. + (emit_composite_svg_pattern): ditto. + (emit_composite_pattern): ditto. + (emit_operator): use SVG 1.2 operators, since feComposite filter + always composite resulting surface to current canvas. + (_cairo_svg_surface_composite): removed. + (_cairo_svg_surface_fill_rectangles): removed. + (intersect): removed. + (_cairo_svg_surface_composite_trapezoids): removed. + (emit_paint): new. + (_cairo_svg_surface_paint): new. + (_cairo_svg_surface_mask): new. + (_cairo_svg_surface_stroke): emit operator. + 2006-02-11 Christian Biesinger * doc/public/cairo-docs.xml: diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 1e280d69d..efbbe18cd 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -74,6 +74,7 @@ struct cairo_svg_document { unsigned int pattern_id; unsigned int filter_id; unsigned int clip_id; + unsigned int mask_id; }; struct cairo_svg_surface { @@ -392,7 +393,7 @@ _cairo_surface_base64_encode (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t +static xmlNodePtr emit_composite_image_pattern (xmlNodePtr node, cairo_surface_pattern_t *pattern, double *width, @@ -410,11 +411,11 @@ emit_composite_image_pattern (xmlNodePtr node, status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (status) - return status; + return NULL; status = _cairo_surface_base64_encode (surface, &image_buffer); if (status) - return status; + return NULL; child = xmlNewChild (node, NULL, CC2XML ("image"), NULL); _cairo_dtostr (buffer, sizeof buffer, image->width); @@ -438,10 +439,10 @@ emit_composite_image_pattern (xmlNodePtr node, _cairo_surface_release_source_image (pattern->surface, image, image_extra); - return status; + return child; } -static cairo_int_status_t +static xmlNodePtr emit_composite_svg_pattern (xmlNodePtr node, cairo_surface_pattern_t *pattern, double *width, @@ -455,7 +456,7 @@ emit_composite_svg_pattern (xmlNodePtr node, /* FIXME: self copy is not supported yet */ if (surface->id == 0) - return CAIRO_STATUS_SUCCESS; + return NULL; child = xmlNewChild (node, NULL, CC2XML("use"), NULL); snprintf (buffer, sizeof buffer, "#surface%d", surface->id); @@ -472,10 +473,10 @@ emit_composite_svg_pattern (xmlNodePtr node, if (height != NULL) *height = surface->height; - return CAIRO_STATUS_SUCCESS; + return child; } -static cairo_int_status_t +static xmlNodePtr emit_composite_pattern (xmlNodePtr node, cairo_surface_pattern_t *pattern, double *width, @@ -488,69 +489,27 @@ emit_composite_pattern (xmlNodePtr node, return emit_composite_image_pattern (node, pattern, width, height, is_pattern); } -static cairo_int_status_t -_cairo_svg_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_svg_surface_t *dst = abstract_dst; - cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) src_pattern; - - if (mask_pattern) - return CAIRO_STATUS_SUCCESS; - - if (src_pattern->type != CAIRO_PATTERN_SURFACE) - return CAIRO_STATUS_SUCCESS; - - return emit_composite_pattern (dst->xml_node, src, NULL, NULL, FALSE); -} +/* FIXME: Here we use a SVG 1.2 feature. We should probably have + * an API to limit output SVG version, and fallback to image when + * necessary. */ static void -emit_operator (cairo_operator_t op, cairo_svg_surface_t *surface, - xmlBufferPtr style) +emit_operator (xmlNodePtr node, cairo_operator_t op) { char const *op_str[] = { - NULL, - NULL, NULL, "in", "out", "atop", - NULL, NULL, NULL, NULL, NULL, - "xor", NULL, NULL }; - - cairo_svg_document_t *document = surface->document; - xmlNodePtr child, primitive; - xmlBufferPtr id; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + "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 (op_str[op] == NULL) - return; - - child = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("filter"), NULL); - primitive = xmlNewChild (child, NULL, CC2XML ("feComposite"), NULL); - xmlSetProp (primitive, CC2XML ("operator"), CC2XML (op_str[op])); - xmlSetProp (primitive, CC2XML ("in"), CC2XML ("SourceGraphic")); - xmlSetProp (primitive, CC2XML ("in2"), CC2XML ("BackgroundImage")); - - id = xmlBufferCreate (); - xmlBufferCat (id, CC2XML ("filter")); - snprintf (buffer, sizeof buffer, "%d", document->filter_id); - xmlBufferCat (id, C2XML (buffer)); - xmlSetProp (child, CC2XML ("id"), C2XML (xmlBufferContent (id))); - - xmlBufferCat (style, CC2XML ("filter: url(#")); - xmlBufferCat (style, xmlBufferContent (id)); - xmlBufferCat (style, CC2XML (");")); - - xmlBufferFree (id); - - document->filter_id++; + xmlSetProp (node, CC2XML ("comp-op"), C2XML (op_str[op])); } static void @@ -828,7 +787,7 @@ typedef struct xmlBufferPtr path; } svg_path_info_t; - static cairo_status_t +static cairo_status_t _cairo_svg_path_move_to (void *closure, cairo_point_t *point) { svg_path_info_t *info = closure; @@ -917,42 +876,6 @@ _cairo_svg_path_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_svg_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_svg_surface_t *surface = abstract_surface; - xmlNodePtr child; - xmlBufferPtr style; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - int i; - - style = xmlBufferCreate (); - emit_color (color, style, "fill", "opacity"); - xmlBufferCat (style, " stroke: none;"); - emit_operator (op, surface, style); - - for (i = 0; i < num_rects; i++) { - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); - _cairo_dtostr (buffer, sizeof buffer, rects[i].x); - xmlSetProp (child, CC2XML ("x"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, rects[i].y); - xmlSetProp (child, CC2XML ("y"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, rects[i].width); - xmlSetProp (child, CC2XML ("width"), C2XML (buffer)); - _cairo_dtostr (buffer, sizeof buffer, rects[i].height); - xmlSetProp (child, CC2XML ("height"), C2XML (buffer)); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - } - - xmlBufferFree (style); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t _cairo_svg_surface_fill (void *abstract_surface, cairo_operator_t op, @@ -978,7 +901,6 @@ _cairo_svg_surface_fill (void *abstract_surface, xmlBufferCat (style, " stroke: none;"); xmlBufferCat (style, " fill-rule: "); xmlBufferCat (style, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd;" : "nonzero;"); - emit_operator (op, surface, style); status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, @@ -991,6 +913,7 @@ _cairo_svg_surface_fill (void *abstract_surface, child = xmlNewChild (surface->xml_node, NULL, CC2XML ("path"), NULL); xmlSetProp (child, CC2XML ("d"), xmlBufferContent (info.path)); xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); + emit_operator (child, op); xmlBufferFree (info.path); xmlBufferFree (style); @@ -998,91 +921,6 @@ _cairo_svg_surface_fill (void *abstract_surface, return status; } -static double -intersect (cairo_line_t *line, cairo_fixed_t y) -{ - return _cairo_fixed_to_double (line->p1.x) + - _cairo_fixed_to_double (line->p2.x - line->p1.x) * - _cairo_fixed_to_double (y - line->p1.y) / - _cairo_fixed_to_double (line->p2.y - line->p1.y); -} - -static cairo_int_status_t -_cairo_svg_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int x_src, - int y_src, - int x_dst, - int y_dst, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_svg_surface_t *surface = abstract_dst; - xmlBufferPtr style, path; - xmlNodePtr child; - double left_x1, left_x2, right_x1, right_x2; - char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - int i; - - style = xmlBufferCreate (); - emit_pattern (surface, pattern, style, 0); - xmlBufferCat (style, "stroke: none;"); - - path = xmlBufferCreate (); - - for (i = 0; i < num_traps; i++) { - - left_x1 = intersect (&traps[i].left, traps[i].top); - left_x2 = intersect (&traps[i].left, traps[i].bottom); - right_x1 = intersect (&traps[i].right, traps[i].top); - right_x2 = intersect (&traps[i].right, traps[i].bottom); - - xmlBufferCat (path, CC2XML ("M ")); - _cairo_dtostr (buffer, sizeof buffer, left_x1); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, - _cairo_fixed_to_double (traps[i].top)); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" L ")); - _cairo_dtostr (buffer, sizeof buffer, left_x2); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, - _cairo_fixed_to_double (traps[i].bottom)); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" L ")); - _cairo_dtostr (buffer, sizeof buffer, right_x2); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, - _cairo_fixed_to_double (traps[i].bottom)); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" L ")); - _cairo_dtostr (buffer, sizeof buffer, right_x1); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" ")); - _cairo_dtostr (buffer, sizeof buffer, - _cairo_fixed_to_double (traps[i].top)); - xmlBufferCat (path, CC2XML (buffer)); - xmlBufferCat (path, CC2XML (" Z")); - } - - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("path"), NULL); - - xmlSetProp (child, CC2XML ("d"), xmlBufferContent (path)); - xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); - - xmlBufferFree (path); - xmlBufferFree (style); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t _cairo_svg_surface_get_extents (void *abstract_surface, cairo_rectangle_t *rectangle) @@ -1102,26 +940,26 @@ _cairo_svg_surface_get_extents (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } - /* First attempt of paint implementation, but obviously I didn't - * understand how it's supposed to work */ - -#if 0 -static cairo_int_status_t -_cairo_svg_surface_paint (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source) +static xmlNodePtr +emit_paint (xmlNodePtr node, + cairo_svg_surface_t *surface, + cairo_operator_t op, + cairo_pattern_t *source) { - cairo_svg_surface_t *surface = abstract_surface; xmlNodePtr child; xmlBufferPtr style; char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + if (source->type == CAIRO_PATTERN_SURFACE) + return emit_composite_pattern (node, + (cairo_surface_pattern_t *) source, + NULL, NULL, FALSE); + style = xmlBufferCreate (); emit_pattern (surface, source, style, 0); xmlBufferCat (style, " stroke: none;"); - emit_operator (op, surface, style); - child = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); + child = xmlNewChild (node, NULL, CC2XML ("rect"), NULL); xmlSetProp (child, CC2XML ("x"), CC2XML ("0")); xmlSetProp (child, CC2XML ("y"), CC2XML ("0")); _cairo_dtostr (buffer, sizeof buffer, surface->width); @@ -1129,12 +967,51 @@ _cairo_svg_surface_paint (void *abstract_surface, _cairo_dtostr (buffer, sizeof buffer, surface->height); xmlSetProp (child, CC2XML ("height"), C2XML (buffer)); xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); + emit_operator (child, op); xmlBufferFree (style); + return child; +} + +static cairo_int_status_t +_cairo_svg_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) +{ + cairo_svg_surface_t *surface = abstract_surface; + + emit_paint (surface->xml_node, surface, op, source); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_mask (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + xmlNodePtr child, mask_node; + char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + + mask_node = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("mask"), NULL); + snprintf (buffer, sizeof buffer, "mask%d", document->mask_id); + xmlSetProp (mask_node, CC2XML ("id"), C2XML (buffer)); + emit_paint (mask_node, surface, op, mask); + + child = emit_paint (surface->xml_node, surface, op, source); + + if (child) { + snprintf (buffer, sizeof buffer, "url(#mask%d)", document->mask_id); + xmlSetProp (child, CC2XML ("mask"), C2XML (buffer)); + } + + document->mask_id++; + return CAIRO_STATUS_SUCCESS; } -#endif static cairo_int_status_t _cairo_svg_surface_stroke (void *abstract_dst, @@ -1231,9 +1108,9 @@ _cairo_svg_surface_stroke (void *abstract_dst, &info); child = xmlNewChild (surface->xml_node, NULL, CC2XML ("path"), NULL); - xmlSetProp (child, CC2XML ("d"), xmlBufferContent (info.path)); xmlSetProp (child, CC2XML ("style"), xmlBufferContent (style)); + emit_operator (child, op); xmlBufferFree (info.path); xmlBufferFree (style); @@ -1357,9 +1234,9 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ NULL, /* clone_similar */ - _cairo_svg_surface_composite, - _cairo_svg_surface_fill_rectangles, - _cairo_svg_surface_composite_trapezoids, + NULL, /* _cairo_svg_surface_composite, */ + NULL, /* _cairo_svg_surface_fill_rectangles, */ + NULL, /* _cairo_svg_surface_composite_trapezoids,*/ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ @@ -1371,8 +1248,8 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { NULL, /* mark dirty rectangle */ NULL, /* scaled font fini */ NULL, /* scaled glyph fini */ - NULL, /*_cairo_svg_surface_paint,*/ - NULL, /* mask */ + _cairo_svg_surface_paint, + _cairo_svg_surface_mask, _cairo_svg_surface_stroke, _cairo_svg_surface_fill, NULL /* show_glyphs */ @@ -1407,6 +1284,7 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->pattern_id = 0; document->filter_id = 0; document->clip_id = 0; + document->mask_id = 0; doc = xmlNewDoc (CC2XML ("1.0")); node = xmlNewNode (NULL, CC2XML ("svg")); @@ -1423,7 +1301,7 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, xmlSetProp (node, CC2XML ("height"), CC2XML (buffer)); xmlSetProp (node, CC2XML ("xmlns"), CC2XML ("http://www.w3.org/2000/svg")); xmlSetProp (node, CC2XML ("xmlns:xlink"), CC2XML ("http://www.w3.org/1999/xlink")); - xmlSetProp (node, CC2XML ("version"), CC2XML ("1.1")); + xmlSetProp (node, CC2XML ("version"), CC2XML ("1.2")); return document; }