vg_load_svg: Implement ClipPath feature 89/245889/1
authorJunsuChoi <jsuya.choi@samsung.com>
Wed, 14 Oct 2020 10:16:53 +0000 (19:16 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Mon, 19 Oct 2020 06:52:53 +0000 (15:52 +0900)
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

Change-Id: I1e02b0405d32ebea4ba9b072586aaad7248ee598

src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
src/static_libs/vg_common/vg_common.h
src/static_libs/vg_common/vg_common_svg.c

index 2e78e87..e68edbb 100644 (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
index 43e36df..e9cbb4f 100644 (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;
index 38e203b..30fc0c0 100644 (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);
@@ -829,12 +829,51 @@ _add_polyline(Efl_VG *vg, double *array, int size, Eina_Bool polygon)
 }
 
 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)
 {
    Efl_VG *vg = NULL;
    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: