forked from enlightenment/efl
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:
parent
25e64a9a4e
commit
3274390f73
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue