vg_load_svg: Implement ClipPath feature

Summary:
Supports case of using style attribute for defined <clipPath> and node.
In SVG, <clipPath> can be used as a "clipPath" attribute or a style "clip-path".
If there is a clip-path node, save it as a composition node and
use composition method(matte_alpha) to compose it.

Below node types support clip-path.
<circle>
<ellipse>
<g>
<path>
<polygon>
<polyline>
<rect>

Test Plan:
Please see attached svg files
{F4026162}

Reviewers: Hermet, smohanty

Reviewed By: Hermet

Subscribers: #reviewers, #committers, cedric, herb, kimcinoo

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D12179
This commit is contained in:
junsu choi 2020-10-14 19:16:53 +09:00 committed by Hermet Park
parent 25e64a9a4e
commit 3274390f73
3 changed files with 167 additions and 4 deletions

View File

@ -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 <svg>(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

View File

@ -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;

View File

@ -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: