diff --git a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c index 2e78e87834..e68edbb0c9 100644 --- a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c +++ b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c @@ -918,6 +918,16 @@ _handle_transform_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, cons node->transform = _parse_transformation_matrix(value); } + +static void _handle_clip_path_attr(Evas_SVG_Loader* loader EINA_UNUSED, Svg_Node* node, const char* value) +{ + Svg_Style_Property* style = node->style; + style->comp.flags |= SVG_COMPOSITE_FLAGS_CLIP_PATH; + + int len = strlen(value); + if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _id_from_url((const char*)(value + 3)); +} + static void _handle_display_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value) { @@ -1003,6 +1013,10 @@ _attr_parse_g_node(void *data, const char *key, const char *value) { node->transform = _parse_transformation_matrix(value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else if (!strcmp(key, "id")) { node->id = _copy_id(value); @@ -1015,6 +1029,37 @@ _attr_parse_g_node(void *data, const char *key, const char *value) } +/* parse clipPath node + * https://www.w3.org/TR/SVG/struct.html#Groups + */ +static Eina_Bool _attr_parse_clip_path_node(void* data, const char* key, const char* value) +{ + Evas_SVG_Loader *loader = data; + Svg_Node* node = loader->svg_parse->node; + + if (!strcmp(key, "style")) + { + return _attr_style_node(loader, value); + } + else if (!strcmp(key, "transform")) + { + node->transform = _parse_transformation_matrix(value); + } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } + else if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else + { + _parse_style_attr(loader, key, value); + } + return EINA_TRUE; +} + static Svg_Node * _create_node(Svg_Node *parent, Svg_Node_Type type) { @@ -1111,10 +1156,11 @@ _create_mask_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNU static Svg_Node * _create_clipPath_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED) { - Svg_Node *node = _create_node(NULL, SVG_NODE_UNKNOWN); + loader->svg_parse->node = _create_node(parent, SVG_NODE_CLIP_PATH); - node->display = EINA_FALSE; - return node; + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_clip_path_node, loader); + return loader->svg_parse->node; } static Eina_Bool @@ -1132,6 +1178,10 @@ _attr_parse_path_node(void *data, const char *key, const char *value) { _attr_style_node(loader, value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else if (!strcmp(key, "id")) { node->id = _copy_id(value); @@ -1194,6 +1244,10 @@ _attr_parse_circle_node(void *data, const char *key, const char *value) { _attr_style_node(loader, value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else if (!strcmp(key, "id")) { node->id = _copy_id(value); @@ -1256,6 +1310,10 @@ _attr_parse_ellipse_node(void *data, const char *key, const char *value) { node->id = _copy_id(value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else if (!strcmp(key, "style")) { _attr_style_node(loader, value); @@ -1341,6 +1399,10 @@ _attr_parse_polygon_node(void *data, const char *key, const char *value) { _attr_style_node(loader, value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else if (!strcmp(key, "id")) { node->id = _copy_id(value); @@ -1425,6 +1487,10 @@ _attr_parse_rect_node(void *data, const char *key, const char *value) { _attr_style_node(loader, value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else { _parse_style_attr(loader, key, value); @@ -1492,6 +1558,10 @@ _attr_parse_line_node(void *data, const char *key, const char *value) { _attr_style_node(loader, value); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else { _parse_style_attr(loader, key, value); @@ -1550,6 +1620,20 @@ _find_child_by_id(Svg_Node *node, const char *id) return NULL; } +static Svg_Node* _find_node_by_id(Svg_Node *node, const char* id) +{ + Svg_Node *child, *result = NULL; + Eina_List *l; + if ((node->id) && !strcmp(node->id, id)) return node; + + EINA_LIST_FOREACH(node->child, l, child) + { + result = _find_node_by_id(child, id); + if (result) break; + } + return result; +} + static Eina_List * _clone_grad_stops(Eina_List *from) { @@ -1698,6 +1782,10 @@ _attr_parse_use_node(void *data, const char *key, const char *value) _clone_node(node_from, node); eina_stringshare_del(id); } + else if (!strcmp(key, "clip-path")) + { + _handle_clip_path_attr(loader, node, value); + } else { _attr_parse_g_node(data, key, value); @@ -2207,6 +2295,7 @@ _evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader, } else { + if (!strcmp(tag_name, "svg")) return; //Already loadded (SvgNodeType::Doc) tag parent = _get_parent_node_from_loader(loader); node = method(loader, parent, attrs, attrs_length); } @@ -2469,6 +2558,21 @@ _update_gradient(Svg_Node *node, Eina_List *grad_list) } } } + +static void _update_composite(Svg_Node* node, Svg_Node* root) +{ + Svg_Node *child; + Eina_List *l; + if (node->style->comp.url && !node->style->comp.node) { + Svg_Node *findResult = _find_node_by_id(root, node->style->comp.url); + if (findResult) node->style->comp.node = findResult; + } + EINA_LIST_FOREACH(node->child, l, child) + { + _update_composite(child, root); + } +} + static Eina_Bool evas_vg_load_file_data_svg(Vg_File_Data *vfd EINA_UNUSED) { @@ -2521,6 +2625,9 @@ evas_vg_load_file_open_svg(Eina_File *file, eina_list_free(gradient_list); } + _update_composite(loader.doc, loader.doc); + if (defs) _update_composite(loader.doc, defs); + *error = EVAS_LOAD_ERROR_NONE; } else diff --git a/src/static_libs/vg_common/vg_common.h b/src/static_libs/vg_common/vg_common.h index 43e36df4d8..e9cbb4f475 100644 --- a/src/static_libs/vg_common/vg_common.h +++ b/src/static_libs/vg_common/vg_common.h @@ -25,12 +25,14 @@ typedef struct _Svg_Ellipse_Node Svg_Ellipse_Node; typedef struct _Svg_Polygon_Node Svg_Polygon_Node; typedef struct _Svg_Rect_Node Svg_Rect_Node; typedef struct _Svg_Path_Node Svg_Path_Node; +typedef struct _Svg_Composite Svg_Composite; typedef struct _Svg_Style_Property Svg_Style_Property; typedef struct _Svg_Line_Node Svg_Line_Node; typedef struct _Svg_Custom_Command_Node Svg_Custom_Command_Node; typedef struct _Svg_Style_Stroke Svg_Style_Stroke; typedef struct _Svg_Style_Fill Svg_Style_Fill; +typedef enum _Svg_Composite_Flags Svg_Composite_Flags; typedef enum _Svg_Fill_Flags Svg_Fill_Flags; typedef enum _Svg_Stroke_Flags Svg_Stroke_Flags; @@ -62,6 +64,7 @@ enum _Svg_Node_Type SVG_NODE_USE, SVG_NODE_VIDEO, SVG_NODE_CUSTOME_COMMAND, + SVG_NODE_CLIP_PATH, SVG_NODE_UNKNOWN }; @@ -224,6 +227,12 @@ struct _Svg_Paint Eina_Stringshare *url; }; +enum _Svg_Composite_Flags +{ + SVG_COMPOSITE_FLAGS_CLIP_PATH = 0x01, +}; + + enum _Svg_Fill_Flags { SVG_FILL_FLAGS_PAINT = 0x1, @@ -266,10 +275,18 @@ struct _Svg_Style_Stroke int dash_count; }; +struct _Svg_Composite +{ + Svg_Composite_Flags flags; + const char *url; + Svg_Node* node; +}; + struct _Svg_Style_Property { Svg_Style_Fill fill; Svg_Style_Stroke stroke; + Svg_Composite comp; // the color property indirectly // used by fill and stroke int r; diff --git a/src/static_libs/vg_common/vg_common_svg.c b/src/static_libs/vg_common/vg_common_svg.c index 38e203b205..30fc0c0eb5 100644 --- a/src/static_libs/vg_common/vg_common_svg.c +++ b/src/static_libs/vg_common/vg_common_svg.c @@ -763,7 +763,7 @@ _apply_vg_property(Svg_Node *node, Efl_VG *vg, Efl_VG *parent, Vg_File_Data *vg_ efl_gfx_color_set(vg, ((float) r) * fa, ((float) g) * fa, ((float) b) * fa, ((float) a) * fa); } - if (node->type == SVG_NODE_G) return; + if (node->type == SVG_NODE_G || node->type == SVG_NODE_CLIP_PATH) return; // apply the fill style property efl_gfx_shape_fill_rule_set(vg, style->fill.fill_rule); @@ -828,6 +828,15 @@ _add_polyline(Efl_VG *vg, double *array, int size, Eina_Bool polygon) efl_gfx_path_append_close(vg); } +static Efl_VG * +_create_vg_container(Efl_VG *parent) +{ + if (!parent) + return efl_add_ref(EFL_CANVAS_VG_CONTAINER_CLASS, NULL); + else + return efl_add(EFL_CANVAS_VG_CONTAINER_CLASS, parent); +} + static Efl_VG * vg_common_create_vg_node_helper(Svg_Node *node, Efl_VG *parent, Vg_File_Data *vg_data) { @@ -835,6 +844,36 @@ vg_common_create_vg_node_helper(Svg_Node *node, Efl_VG *parent, Vg_File_Data *vg Svg_Node *child; Eina_List *l; + // apply composite node + if (node->style->comp.node) + { + // composite ClipPath + if (node->style->comp.flags & SVG_COMPOSITE_FLAGS_CLIP_PATH) + { + Svg_Node *comp_node = node->style->comp.node; + Efl_VG *comp_parent = NULL ,*comp_vg_container = NULL; + + //NOTE:: If node has a composition node, add a container to use VG_COMPOSITION_METHOD. + // The composition method is applied to the newly added container. + comp_parent = _create_vg_container(parent); + comp_vg_container = _create_vg_container(parent); + + // apply the transformation + if (comp_node->transform) efl_canvas_vg_node_transformation_set(comp_vg_container, comp_node->transform); + + EINA_LIST_FOREACH(comp_node->child, l, child) + { + Efl_VG *vg = vg_common_create_vg_node_helper(child, comp_vg_container, vg_data); + // clippath does not require color blending. That's why we keep 255 opacity. + efl_gfx_color_set(vg, 255, 255, 255, 255); + } + + // Composition matte alpha + efl_canvas_vg_node_comp_method_set(comp_parent, comp_vg_container, EFL_GFX_VG_COMPOSITE_METHOD_MATTE_ALPHA); + + parent = comp_parent; // replace parent + } + } switch (node->type) { case SVG_NODE_DOC: