evas/canvas/vg: Added integration with thorvg. 25/243325/17
authorMichal Szczecinski <m.szczecinsk@partner.samsung.com>
Fri, 4 Sep 2020 11:28:18 +0000 (13:28 +0200)
committerHermet Park <chuneon.park@samsung.com>
Thu, 22 Oct 2020 10:19:30 +0000 (10:19 +0000)
If flag HAVE_THORVG is defined, thorVG lib is used to render vector
graphics. To change flag see meson_options.txt file.

To test this solution newly added examples can be used. You can find
it in src/examples/evas/evas-vg-*

Change-Id: I9765b3fc53cf77b340bd3a75d8029795f2648302

src/lib/evas/canvas/efl_canvas_vg_container.c
src/lib/evas/canvas/efl_canvas_vg_gradient.c
src/lib/evas/canvas/efl_canvas_vg_gradient_linear.c
src/lib/evas/canvas/efl_canvas_vg_gradient_radial.c
src/lib/evas/canvas/efl_canvas_vg_image.c
src/lib/evas/canvas/efl_canvas_vg_node.c
src/lib/evas/canvas/efl_canvas_vg_object.c
src/lib/evas/canvas/efl_canvas_vg_shape.c
src/lib/evas/canvas/evas_tvg_path_helpers.h [new file with mode: 0644]
src/lib/evas/canvas/evas_vg_private.h

index 7dea694..c4d2c55 100644 (file)
@@ -22,6 +22,7 @@ _invalidate_cb(void *data EINA_UNUSED, const Efl_Event *event)
       efl_unref(child);
 }
 
+#ifndef HAVE_THORVG
 static void
 _draw_comp(Evas_Object_Protected_Data *obj, Efl_VG *node,
            Ector_Surface *ector, void *engine, void *output,
@@ -46,6 +47,7 @@ _draw_comp(Evas_Object_Protected_Data *obj, Efl_VG *node,
         ENFN->ector_renderer_draw(engine, output, context, nd->renderer, NULL, EINA_FALSE);
      }
 }
+#endif
 
 static Ector_Buffer *
 _prepare_comp(Evas_Object_Protected_Data *obj,     //vector object
@@ -59,6 +61,7 @@ _prepare_comp(Evas_Object_Protected_Data *obj,     //vector object
               Ector_Buffer *comp,
               Efl_Gfx_Vg_Composite_Method comp_method)
 {
+
    Efl_Canvas_Vg_Container_Data *pd = efl_data_scope_get(comp_target, MY_CLASS);
    Efl_Canvas_Vg_Node_Data *nd =
          efl_data_scope_get(comp_target, EFL_CANVAS_VG_NODE_CLASS);
@@ -149,7 +152,9 @@ _prepare_comp(Evas_Object_Protected_Data *obj,     //vector object
    ector_buffer_pixels_set(surface, pd->comp.pixels, size.w, size.h, pd->comp.stride,
                            EFL_GFX_COLORSPACE_ARGB8888, EINA_TRUE);
    ector_surface_reference_point_set(surface, 0, 0);
+#ifndef HAVE_THORVG
    _draw_comp(obj, comp_target, surface, engine, output, context);
+#endif
 
    return pd->comp.buffer;
 }
@@ -182,6 +187,7 @@ _efl_canvas_vg_container_render_pre(Evas_Object_Protected_Data *vg_pd,
    //Container may have composite target.
    //FIXME : _prepare_comp() should only work in cases with matte or masking.
    // This condition is valid because the masking use same type as matte.
+#ifndef HAVE_THORVG
    if (pd->comp_target &&
        (pd->comp.method == EFL_GFX_VG_COMPOSITE_METHOD_MATTE_ALPHA ||
         pd->comp.method == EFL_GFX_VG_COMPOSITE_METHOD_MATTE_ALPHA_INVERSE))
@@ -191,6 +197,7 @@ _efl_canvas_vg_container_render_pre(Evas_Object_Protected_Data *vg_pd,
                              engine, output, context, surface,
                              ptransform, ctransform, p_opacity, c_a, comp, comp_method);
      }
+#endif
 
    //If the container has transparency, it internally alpha blends with ector buffer.
    //So ector buffer must be created synchronously.
@@ -235,21 +242,21 @@ _efl_canvas_vg_container_render_pre(Evas_Object_Protected_Data *vg_pd,
 
 static Eo *
 _efl_canvas_vg_container_efl_object_constructor(Eo *obj,
-                                      Efl_Canvas_Vg_Container_Data *pd)
+                                                Efl_Canvas_Vg_Container_Data *pd)
 {
    Efl_Canvas_Vg_Node_Data *nd;
 
    pd->names = eina_hash_stringshared_new(NULL);
-
    obj = efl_constructor(efl_super(obj, MY_CLASS));
 
    nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+
    nd->render_pre = _efl_canvas_vg_container_render_pre;
+
    nd->data = pd;
    nd->flags = EFL_GFX_CHANGE_FLAG_ALL;
 
    efl_gfx_color_set(obj, 255, 255, 255, 255);
-
    return obj;
 }
 
@@ -259,6 +266,7 @@ _efl_canvas_vg_container_efl_object_destructor(Eo *obj,
 {
    efl_canvas_vg_container_blend_buffer_clear(obj, pd);
 
+#ifdef HAVE_THORVG
    //Destroy comp surface
    if (pd->comp.buffer)
      {
@@ -266,6 +274,7 @@ _efl_canvas_vg_container_efl_object_destructor(Eo *obj,
           ector_buffer_unmap(pd->comp.buffer, pd->comp.pixels, pd->comp.length);
         efl_unref(pd->comp.buffer);
      }
+#endif
 
    efl_unref(pd->comp_target);
    eina_list_free(pd->comp.src);
@@ -516,6 +525,7 @@ efl_canvas_vg_container_vg_obj_update(Efl_VG *obj, Efl_Canvas_Vg_Node_Data *nd)
 void
 efl_canvas_vg_container_blend_buffer_clear(Efl_VG *obj EINA_UNUSED, Efl_Canvas_Vg_Container_Data *cd)
 {
+#ifndef HAVE_THORV
    if (!cd->blend.buffer) return;
 
    if (cd->blend.pixels)
@@ -525,6 +535,7 @@ efl_canvas_vg_container_blend_buffer_clear(Efl_VG *obj EINA_UNUSED, Efl_Canvas_V
      }
    if (cd->blend.buffer) efl_unref(cd->blend.buffer);
    cd->blend.buffer = NULL;
+#endif
 }
 
 EAPI Evas_Vg_Container *
index 940cc64..214ed96 100644 (file)
@@ -7,6 +7,65 @@
 
 #define MY_CLASS EFL_CANVAS_VG_GRADIENT_CLASS
 
+#ifdef HAVE_THORVG
+
+static void
+_gradient_stop_set(Evas_Vg_Gradient *obj, const Efl_Gfx_Gradient_Stop *colors, unsigned int length)
+{
+   Tvg_Color_Stop *color_stops = NULL;
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+
+   unsigned int i = 0;
+
+   if (!obj) return;
+
+   gd = efl_data_scope_get(obj, MY_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   color_stops = malloc(sizeof(Tvg_Color_Stop) * length);
+   if (color_stops == NULL) return;
+
+   for (i = 0; i < length; ++i)
+     {
+        color_stops[i].offset = colors[i].offset;
+        color_stops[i].r = colors[i].r;
+        color_stops[i].g = colors[i].g;
+        color_stops[i].b = colors[i].b;
+        color_stops[i].a = colors[i].a;
+     }
+
+   tvg_gradient_set_color_stops(gd->gradient, color_stops, length);
+   free(color_stops);
+}
+
+static void
+_gradient_stop_get(Evas_Vg_Gradient *obj, Efl_Gfx_Gradient_Stop **colors, unsigned int *length)
+{
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   gd = efl_data_scope_get(obj, MY_CLASS);
+   const Tvg_Color_Stop *stop = NULL;
+   uint32_t len = 0;
+   unsigned int i = 0;
+
+   if (!obj || !colors || !length) return;
+
+   gd = efl_data_scope_get(obj, MY_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_gradient_get_color_stops(gd->gradient, &stop, &len);
+
+   *colors = malloc(sizeof(Efl_Gfx_Gradient_Stop) * len);
+   for (i = 0; i < len; ++i)
+     {
+        (*colors)[i].offset = stop[i].offset;
+        (*colors)[i].a = stop[i].a;
+        (*colors)[i].r = stop[i].r;
+        (*colors)[i].g = stop[i].g;
+        (*colors)[i].b = stop[i].b;
+     }
+}
+#endif
+
 static void
 _efl_canvas_vg_gradient_efl_gfx_gradient_stop_set(Eo *obj EINA_UNUSED,
                                                 Efl_Canvas_Vg_Gradient_Data *pd,
@@ -99,13 +158,9 @@ _efl_canvas_vg_gradient_efl_gfx_path_interpolate(Eo *obj,
 static void
 _efl_canvas_vg_gradient_efl_object_destructor(Eo *obj, Efl_Canvas_Vg_Gradient_Data *pd)
 {
-   if (pd->colors)
-     {
-        free(pd->colors);
-        pd->colors = NULL;
-     }
-    pd->colors_count = 0;
-
+#ifdef HAVE_THORVG
+#endif
+   if (pd->colors) free(pd->colors);
    efl_destructor(efl_super(obj, MY_CLASS));
 }
 
@@ -125,25 +180,52 @@ _efl_canvas_vg_gradient_efl_duplicate_duplicate(const Eo *obj, Efl_Canvas_Vg_Gra
 EAPI void
 evas_vg_gradient_stop_set(Evas_Vg_Gradient *obj, const Evas_Vg_Gradient_Stop *colors, unsigned int length)
 {
+#ifdef HAVE_THORVG
+   _gradient_stop_set(obj, (const Efl_Gfx_Gradient_Stop *)colors, length);
+#else
    efl_gfx_gradient_stop_set(obj, (const Efl_Gfx_Gradient_Stop *)colors, length);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_stop_get(Evas_Vg_Gradient *obj, const Evas_Vg_Gradient_Stop **colors, unsigned int *length)
 {
+#ifdef HAVE_THORVG
+   _gradient_stop_get(obj, (Efl_Gfx_Gradient_Stop **)colors, length);
+#else
    efl_gfx_gradient_stop_get(obj, (const Efl_Gfx_Gradient_Stop **)colors, length);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_spread_set(Evas_Vg_Gradient *obj, Evas_Vg_Gradient_Spread s)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, MY_CLASS);
+   tvg_gradient_set_spread(gd->gradient, (Tvg_Stroke_Fill)s);
+#else
    efl_gfx_gradient_spread_set(obj, (Efl_Gfx_Gradient_Spread)s);
+#endif
 }
 
 EAPI Evas_Vg_Gradient_Spread
 evas_vg_gradient_spread_get(Evas_Vg_Gradient *obj)
 {
+#ifdef HAVE_THORVG
+   Tvg_Stroke_Fill fill = TVG_STROKE_FILL_PAD;
+   Efl_Canvas_Vg_Gradient_Data *gd;
+
+   if (!obj) return EVAS_VG_GRADIENT_SPREAD_LAST;
+   gd  = efl_data_scope_get(obj, MY_CLASS);
+   if (!gd || !gd->gradient) return EVAS_VG_GRADIENT_SPREAD_LAST;
+
+   tvg_gradient_get_spread(gd->gradient, &fill);
+   return (Evas_Vg_Gradient_Spread)fill;
+#else
    return (Evas_Vg_Gradient_Spread)efl_gfx_gradient_spread_get(obj);
+#endif
 }
 
 #include "efl_canvas_vg_gradient.eo.c"
index bd8cceb..750e7b7 100644 (file)
@@ -55,6 +55,7 @@ _efl_canvas_vg_gradient_linear_efl_gfx_gradient_linear_end_get(const Eo *obj EIN
    if (y) *y = pd->end.y;
 }
 
+#ifndef HAVE_THORVG
 static void
 _efl_canvas_vg_gradient_linear_render_pre(Evas_Object_Protected_Data *vg_pd EINA_UNUSED,
                                           Efl_VG *obj,
@@ -96,17 +97,35 @@ _efl_canvas_vg_gradient_linear_render_pre(Evas_Object_Protected_Data *vg_pd EINA
    ector_renderer_prepare(nd->renderer);
    ector_renderer_comp_method_set(nd->renderer, comp, comp_method);
 }
+#else
+static void
+_gradient_linear_render_pre_tvg(Efl_Canvas_Vg_Node *nd EINA_UNUSED,
+                                Efl_Canvas_Vg_Gradient_Data *gd,
+                                Tvg_Paint *shape)
+{
+   if (!gd || !shape) return;
+   tvg_shape_set_linear_gradient(shape, gd->gradient);
+}
+#endif
 
 static Eo *
 _efl_canvas_vg_gradient_linear_efl_object_constructor(Eo *obj,
                                             Efl_Canvas_Vg_Gradient_Linear_Data *pd)
 {
    Efl_Canvas_Vg_Node_Data *nd;
-
+   Efl_Canvas_Vg_Gradient_Data *gd;
    obj = efl_constructor(efl_super(obj, MY_CLASS));
-
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
    nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+#ifndef HAVE_THORVG
    nd->render_pre = _efl_canvas_vg_gradient_linear_render_pre;
+#else
+   gd->gradient_render_pre_tvg = _gradient_linear_render_pre_tvg;
+   gd->shape = NULL;
+   gd->spread = EFL_GFX_GRADIENT_SPREAD_PAD;
+   //removed by tvg_canvas_destroy()
+   gd->gradient = tvg_linear_gradient_new();
+#endif
    nd->data = pd;
 
    return obj;
@@ -174,25 +193,76 @@ _efl_canvas_vg_gradient_linear_efl_duplicate_duplicate(const Eo *obj, Efl_Canvas
 EAPI void
 evas_vg_gradient_linear_start_set(Evas_Vg_Gradient_Linear *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_linear_gradient_get(gd->gradient, &x1, &y1, &x2, &y2);
+   tvg_linear_gradient_set(gd->gradient, x, y, x2, y2);
+#else
    efl_gfx_gradient_linear_start_set(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_linear_start_get(Evas_Vg_Gradient_Linear *obj, double *x, double *y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_linear_gradient_get(gd->gradient, &x1, &y1, &x2, &y2);
+   if (x) *x = x1;
+   if (y) *y = y1;
+
+#else
    efl_gfx_gradient_linear_start_get(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_linear_end_set(Evas_Vg_Gradient_Linear *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_linear_gradient_get(gd->gradient, &x1, &y1, &x2, &y2);
+   tvg_linear_gradient_set(gd->gradient, x1, y1, x, y);
+#else
    efl_gfx_gradient_linear_end_set(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_linear_end_get(Evas_Vg_Gradient_Linear *obj, double *x, double *y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_linear_gradient_get(gd->gradient, &x1, &y1, &x2, &y2);
+   if (x) *x = x1;
+   if (y) *y = y1;
+#else
    efl_gfx_gradient_linear_end_get(obj, x, y);
+#endif
 }
 
 EAPI Evas_Vg_Gradient_Linear *
index 2634286..f423f50 100644 (file)
@@ -14,6 +14,7 @@ struct _Efl_Canvas_Vg_Gradient_Radial_Data
    double radius;
 };
 
+
 static void
 _efl_canvas_vg_gradient_radial_efl_gfx_gradient_radial_center_set(Eo *obj EINA_UNUSED,
                                                            Efl_Canvas_Vg_Gradient_Radial_Data *pd,
@@ -71,6 +72,7 @@ _efl_canvas_vg_gradient_radial_efl_gfx_gradient_radial_focal_get(const Eo *obj E
    if (y) *y = pd->focal.y;
 }
 
+#ifndef HAVE_THORVG
 static void
 _efl_canvas_vg_gradient_radial_render_pre(Evas_Object_Protected_Data *vg_pd EINA_UNUSED,
                                           Efl_VG *obj,
@@ -113,16 +115,33 @@ _efl_canvas_vg_gradient_radial_render_pre(Evas_Object_Protected_Data *vg_pd EINA
    ector_renderer_prepare(nd->renderer);
    ector_renderer_comp_method_set(nd->renderer, comp, comp_method);
 }
+#else
+static void
+_gradient_radial_render_pre_tvg(Efl_Canvas_Vg_Node *nd EINA_UNUSED,
+                                Efl_Canvas_Vg_Gradient_Data *gd,
+                                Tvg_Paint *shape)
+{
+   if (!gd || !gd->gradient || !shape) return;
+   tvg_shape_set_radial_gradient(shape, gd->gradient);
+}
+#endif
 
 static Eo *
 _efl_canvas_vg_gradient_radial_efl_object_constructor(Eo *obj, Efl_Canvas_Vg_Gradient_Radial_Data *pd)
 {
    Efl_Canvas_Vg_Node_Data *nd;
-
    obj = efl_constructor(efl_super(obj, MY_CLASS));
-
    nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+#ifndef HAVE_THORVG
    nd->render_pre = _efl_canvas_vg_gradient_radial_render_pre;
+#else
+   Efl_Canvas_Vg_Gradient_Data *gd;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   gd->gradient_render_pre_tvg = _gradient_radial_render_pre_tvg;
+   gd->shape = NULL;
+   gd->spread = EFL_GFX_GRADIENT_SPREAD_PAD;
+   gd->gradient = tvg_radial_gradient_new();
+#endif
    nd->data = pd;
 
    return obj;
@@ -196,25 +215,69 @@ _efl_canvas_vg_gradient_radial_efl_duplicate_duplicate(const Eo *obj, Efl_Canvas
 EAPI void
 evas_vg_gradient_radial_center_set(Evas_Vg_Gradient_Radial *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float r = 0.0f;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_radial_gradient_get(gd->gradient, NULL, NULL, &r);
+   tvg_radial_gradient_set(gd->gradient, x, y, r);
+#else
    efl_gfx_gradient_radial_center_set(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_radial_center_get(Evas_Vg_Gradient_Radial *obj, double *x, double *y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_radial_gradient_get(gd->gradient, (float*)x, (float*)y, NULL);
+#else
    efl_gfx_gradient_radial_center_get(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_gradient_radial_radius_set(Evas_Vg_Gradient_Radial *obj, double r)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float x = 0.0f, y = 0.0f;
+
+   if (!obj) return;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return;
+
+   tvg_radial_gradient_get(gd->gradient, &x, &y, NULL);
+   tvg_radial_gradient_set(gd->gradient, x, y, r);
+#else
    efl_gfx_gradient_radial_radius_set(obj, r);
+#endif
 }
 
 EAPI double
 evas_vg_gradient_radial_radius_get(Evas_Vg_Gradient_Radial *obj)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+   float r = 0.0f;
+   if (!obj) return 0.0;
+   gd = efl_data_scope_get(obj, EFL_CANVAS_VG_GRADIENT_CLASS);
+   if (!gd || !gd->gradient) return 0.0;
+   tvg_radial_gradient_get(gd->gradient, NULL, NULL, &r);
+   return r;
+#else
    return efl_gfx_gradient_radial_radius_get(obj);
+#endif
 }
 
 EAPI void
index eb5c306..bc95ad2 100644 (file)
@@ -8,8 +8,10 @@
 typedef struct _Efl_Canvas_Vg_Image_Data Efl_Canvas_Vg_Image_Data;
 struct _Efl_Canvas_Vg_Image_Data
 {
+#ifndef HAVE_THORVG
    Ector_Buffer *buffer;
    void *image;
+#endif
    int w;
    int h;
 };
@@ -36,6 +38,7 @@ _efl_canvas_vg_image_render_pre(Evas_Object_Protected_Data *vg_pd,
 
    nd->flags = EFL_GFX_CHANGE_FLAG_NONE;
 
+#ifndef HAVE_THORVG
    EFL_CANVAS_VG_COMPUTE_MATRIX(ctransform, ptransform, nd);
    EFL_CANVAS_VG_COMPUTE_ALPHA(c_r, c_g, c_b, c_a, p_opacity, nd);
 
@@ -66,20 +69,26 @@ _efl_canvas_vg_image_render_pre(Evas_Object_Protected_Data *vg_pd,
    ector_renderer_visibility_set(nd->renderer, nd->visibility);
    ector_renderer_comp_method_set(nd->renderer, comp, comp_method);
    ector_renderer_prepare(nd->renderer);
+#else
+   //TODO: implement using thorvg
+#endif
 }
 
 static Eo *
 _efl_canvas_vg_image_efl_object_constructor(Eo *obj, Efl_Canvas_Vg_Image_Data *pd)
 {
    Efl_Canvas_Vg_Node_Data *nd;
-
    obj = efl_constructor(efl_super(obj, MY_CLASS));
 
    nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
    nd->render_pre = _efl_canvas_vg_image_render_pre;
    nd->data = pd;
 
+#ifndef HAVE_THORVG
    efl_gfx_color_set(obj , 255, 255, 255, 255);
+#else
+   //TODO: implement using thorvg
+#endif
 
    return obj;
 }
@@ -88,11 +97,14 @@ static void
 _efl_canvas_vg_image_efl_object_destructor(Eo *obj, Efl_Canvas_Vg_Image_Data *pd EINA_UNUSED)
 {
    efl_destructor(efl_super(obj, MY_CLASS));
+
+#ifndef HAVE_THORVG
    if (pd->buffer)
      {
         efl_unref(pd->buffer);
         pd->buffer = NULL;
      }
+#endif
 }
 
 static void
@@ -101,6 +113,7 @@ _efl_canvas_vg_image_data_set(Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Image_Data *pd,
    if (!data || size.w <= 0 || size.h <= 0)
      return;
 
+#ifndef HAVE_THORVG
    if ((pd->image != data || pd->w != size.w || pd->h != size.h) && pd->buffer)
      {
         efl_unref(pd->buffer);
@@ -108,6 +121,8 @@ _efl_canvas_vg_image_data_set(Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Image_Data *pd,
      }
 
    pd->image = data;
+#endif
+
    pd->w = size.w;
    pd->h = size.h;
 }
index f2bead5..68e8f4c 100644 (file)
@@ -132,7 +132,6 @@ _efl_canvas_vg_node_efl_gfx_entity_visible_set(Eo *obj,
                                                Eina_Bool v)
 {
    pd->visibility = v;
-
    _node_change(obj, pd);
 }
 
@@ -267,13 +266,19 @@ _efl_canvas_vg_node_efl_object_constructor(Eo *obj,
 }
 
 static void
+#ifdef HAVE_THORVG
+_efl_canvas_vg_node_efl_object_invalidate(Eo *obj, Efl_Canvas_Vg_Node_Data *pd EINA_UNUSED)
+#else
 _efl_canvas_vg_node_efl_object_invalidate(Eo *obj, Efl_Canvas_Vg_Node_Data *pd)
+#endif
 {
+#ifndef HAVE_THORVG
    if (pd->renderer)
      {
         efl_unref(pd->renderer);
         pd->renderer = NULL;
      }
+#endif
 
    efl_invalidate(efl_super(obj, MY_CLASS));
 }
@@ -407,6 +412,7 @@ _efl_canvas_vg_node_efl_gfx_stack_raise_to_top(Eo *obj, Efl_Canvas_Vg_Node_Data
    if (eina_list_data_get(eina_list_last(cd->children)) == obj) return;
    cd->children = eina_list_remove(cd->children, obj);
    cd->children = eina_list_append(cd->children, obj);
+
    _node_change(parent, efl_data_scope_get(parent, MY_CLASS));
 }
 
@@ -481,6 +487,7 @@ _efl_canvas_vg_node_efl_gfx_stack_lower_to_bottom(Eo *obj, Efl_Canvas_Vg_Node_Da
    if (eina_list_data_get(cd->children) == obj) return;
    cd->children = eina_list_remove(cd->children, obj);
    cd->children = eina_list_prepend(cd->children, obj);
+
    _node_change(parent, efl_data_scope_get(parent, MY_CLASS));
 }
 
@@ -592,8 +599,10 @@ _efl_canvas_vg_node_efl_gfx_path_interpolate(Eo *obj,
    tod = efl_data_scope_get(to, MY_CLASS);
    from_map = 1.0 - pos_map;
 
+#ifndef HAVE_THORVG
    efl_unref(pd->renderer);
    pd->renderer = NULL;
+#endif
 
    //Interpolates Node Transform Matrix
    if (fromd->m || tod->m)
index 0b24645..2897ac4 100644 (file)
@@ -144,8 +144,10 @@ _efl_canvas_vg_object_root_node_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, E
    // detach/free the old root_node
    if (pd->user_entry && pd->user_entry->root)
      {
+#ifndef HAVE_THORVG
         // drop any surface cache attached to it.
         ENFN->ector_surface_cache_drop(_evas_engine_context(obj->layer->evas), pd->user_entry->root);
+#endif
         efl_canvas_vg_node_vg_obj_set(pd->user_entry->root, NULL, NULL);
         efl_replace(&pd->user_entry->root, NULL);
      }
@@ -353,8 +355,9 @@ _efl_canvas_vg_object_efl_object_invalidate(Eo *eo_obj, Efl_Canvas_Vg_Object_Dat
 
    if (pd->user_entry)
      {
-        Vg_User_Entry *user_entry = pd->user_entry;
-        ENFN->ector_surface_cache_drop(ENC, user_entry->root);
+#ifndef HAVE_THORVG
+        ENFN->ector_surface_cache_drop(ENC, pd->user_entry->root);
+#endif
         if (pd->user_entry->root) efl_unref(pd->user_entry->root);
         free(pd->user_entry);
      }
@@ -363,13 +366,34 @@ _efl_canvas_vg_object_efl_object_invalidate(Eo *eo_obj, Efl_Canvas_Vg_Object_Dat
    //Drop cache buffers
    if (pd->vg_entry)
      {
+#ifndef HAVE_THORVG
         if (pd->ckeys[0])
           ENFN->ector_surface_cache_drop(_evas_engine_context(obj->layer->evas), pd->ckeys[0]);
         if (pd->ckeys[1])
           ENFN->ector_surface_cache_drop(_evas_engine_context(obj->layer->evas), pd->ckeys[1]);
+#endif
      }
    evas_cache_vg_entry_del(pd->vg_entry);
 
+#ifdef HAVE_THORVG
+   if (pd->im)
+     {
+        ENFN->image_free(_evas_engine_context(obj->layer->evas), pd->im);
+        pd->im = NULL;
+     }
+
+   if (pd->tvg_buffer)
+     {
+        free(pd->tvg_buffer);
+        pd->tvg_buffer = NULL;
+     }
+
+   if (pd->tvg_canvas)
+     {
+        tvg_canvas_destroy(pd->tvg_canvas);
+        pd->tvg_canvas = NULL;
+     }
+#endif
    efl_invalidate(efl_super(eo_obj, MY_CLASS));
 }
 
@@ -389,11 +413,9 @@ _efl_canvas_vg_object_efl_object_constructor(Eo *eo_obj, Efl_Canvas_Vg_Object_Da
    /* default root node */
    pd->obj = obj;
    pd->root = efl_add_ref(EFL_CANVAS_VG_CONTAINER_CLASS, NULL);
-
    pd->sync_render = EINA_FALSE;
 
    eina_array_step_set(&pd->cleanup, sizeof(pd->cleanup), 8);
-
    return eo_obj;
 }
 
@@ -415,6 +437,67 @@ _efl_canvas_vg_object_efl_object_finalize(Eo *obj, Efl_Canvas_Vg_Object_Data *pd
    return obj;
 }
 
+#ifdef HAVE_THORVG
+static void
+_evas_vg_render_tvg(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
+                    void *engine, void *output, void *context, Efl_VG *node,
+                    Eina_Array *clips, int w, int h, void *canvas, Eina_Bool do_async)
+{
+   Efl_Canvas_Vg_Node_Data *nd = efl_data_scope_get(node, EFL_CANVAS_VG_NODE_CLASS);
+
+   if (efl_isa(node, EFL_CANVAS_VG_CONTAINER_CLASS)) {
+        Efl_VG *child = NULL;
+        Eina_List *l = NULL;
+
+        Efl_Canvas_Vg_Container_Data *cd = efl_data_scope_get(node, EFL_CANVAS_VG_CONTAINER_CLASS);
+
+        // Draw child node to changed buffer
+        EINA_LIST_FOREACH(cd->children, l, child)
+              _evas_vg_render_tvg(obj, pd, engine, output, context, child, clips, w, h, canvas, do_async);
+
+   } else {
+      if (!efl_isa(node, EFL_CANVAS_VG_GRADIENT_CLASS))
+         nd->render_pre_tvg(obj, node, nd, canvas);
+   }
+}
+
+static void
+_render_to_buffer_tvg(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
+                      void *engine, Efl_VG *root, int w, int h, Eina_Bool do_async)
+{
+   _evas_vg_render_tvg(obj, pd, engine, NULL, NULL, root, NULL, w, h, pd->tvg_canvas, do_async);
+
+   if (tvg_canvas_draw(pd->tvg_canvas) == TVG_RESULT_SUCCESS)
+     {
+        tvg_canvas_sync(pd->tvg_canvas);
+     }
+}
+
+static void
+_render_tvg_buffer_to_screen(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
+                         void *engine, void *output, void *context, void *surface,
+                         void *buffer, int x, int y, int w, int h, Eina_Bool do_async)
+{
+   if (!obj || !pd || !buffer) return;
+
+   if (!pd->im)
+     {
+        pd->im = ENFN->image_new_from_data(engine, w, h, buffer, 255,
+                                       EVAS_COLORSPACE_ARGB8888);
+     }
+   else
+     {
+        pd->im = ENFN->image_data_put(engine, pd->im, buffer);
+     }
+
+   ENFN->image_dirty_region(engine, pd->im, 0, 0, w, h);
+   ENFN->image_draw(engine, output, context, surface,
+                    pd->im, 0, 0, w, h, x, y, w, h,
+                    EINA_TRUE, do_async);
+}
+
+#else
+
 static void
 _evas_vg_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
                 void *engine, void *output, void *context, Efl_VG *node,
@@ -787,6 +870,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
                             do_async, EINA_TRUE);
 }
 
+#endif
+
 static void
 _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
                              Evas_Object_Protected_Data *obj,
@@ -796,7 +881,6 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
 {
    Efl_Canvas_Vg_Object_Data *pd = type_private_data;
 
-   /* render object to surface with context, and offxet by x,y */
    ENFN->context_color_set(engine, context, 255, 255, 255, 255);
    ENFN->context_multiplier_set(engine, context,
                                 obj->cur->cache.clip.r,
@@ -806,6 +890,72 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
    ENFN->context_anti_alias_set(engine, context, obj->cur->anti_alias);
    ENFN->context_render_op_set(engine, context, obj->cur->render_op);
 
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Eina_Size2D size;
+   Eina_Rect render_rect;
+
+   int w = 0, h = 0;
+
+   if (!pd || !pd->root || !pd->user_entry) return;
+   if (!obj || !obj->cur) return;
+
+   nd = efl_data_scope_get(pd->root, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+
+   size.w = obj->cur->geometry.w;
+   size.h = obj->cur->geometry.h;
+
+   if (size.w == 0 || size.h == 0) return;
+
+   if (pd->tvg_canvas_size.w != size.w || pd->tvg_canvas_size.h != size.h)
+     {
+        pd->tvg_buffer = realloc(pd->tvg_buffer, size.w * size.h * sizeof(uint32_t));
+        pd->tvg_canvas_size.w = size.w;
+        pd->tvg_canvas_size.h = size.h;
+
+        if (!pd->tvg_canvas) pd->tvg_canvas = tvg_swcanvas_create();
+
+        tvg_swcanvas_set_target(pd->tvg_canvas, pd->tvg_buffer,
+                                size.w, size.w, size.h,
+                                TVG_COLORSPACE_ARGB8888);
+
+        //if size is changed im handle also should be deleted
+        if (pd->im)
+          {
+             ENFN->image_free(engine, pd->im);
+             pd->im = NULL;
+          }
+     }
+
+   _render_to_buffer_tvg(obj, pd, NULL,
+                         pd->user_entry->root,
+                         obj->cur->geometry.w,
+                         obj->cur->geometry.h,
+                         do_async);
+
+   render_rect = EINA_RECT(x, y, size.w, size.h);
+
+   if (nd && nd->render_tvg)
+      nd->render_tvg(obj, pd->root, nd, pd->tvg_canvas);
+
+   if (pd->viewbox.w != 0 && pd->viewbox.h !=0)
+     {
+        double sx = 0, sy= 0;
+        sx = (double)w / (double)pd->viewbox.w;
+        sy = (double)h / (double)pd->viewbox.h;
+        render_rect.x = (render_rect.x - pd->viewbox.x) * sx;
+        render_rect.y = (render_rect.y - pd->viewbox.y) * sy;
+        render_rect.w *= sx;
+        render_rect.h *= sy;
+     }
+
+   _render_tvg_buffer_to_screen(obj, pd, engine, output, context, surface,
+                                pd->tvg_buffer, x + render_rect.x,
+                                y + render_rect.y,
+                                render_rect.w, render_rect.h,
+                                do_async);
+#else
    //Cache surface?
    Eina_Bool cacheable = EINA_FALSE;
 
@@ -831,6 +981,8 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
                               obj->cur->geometry.x + x, obj->cur->geometry.y + y,
                               obj->cur->geometry.w, obj->cur->geometry.h, do_async);
      }
+#endif
+
    pd->changed = EINA_FALSE;
 }
 
@@ -854,12 +1006,14 @@ _efl_canvas_vg_object_render_pre(Evas_Object *eo_obj,
    if (obj->cur->clipper)
      {
         if (obj->cur->cache.clip.dirty)
-          evas_object_clip_recalc(obj->cur->clipper);
+           evas_object_clip_recalc(obj->cur->clipper);
+
         obj->cur->clipper->func->render_pre(obj->cur->clipper->object,
                                             obj->cur->clipper,
                                             obj->cur->clipper->private_data);
      }
 
+
    /* now figure what changed and add draw rects */
    /* if it just became visible or invisible */
    is_v = evas_object_is_visible(obj);
@@ -948,7 +1102,6 @@ _efl_canvas_vg_object_render_pre(Evas_Object *eo_obj,
                                y + obj->layer->evas->framespace.y,
                                w, h);
      }
-
 done:
    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, eo_obj, is_v, was_v);
 }
@@ -1050,7 +1203,7 @@ Eina_Bool _efl_canvas_vg_object_efl_gfx_frame_controller_sector_get(const Eo *ob
    return EINA_TRUE;
 }
 
-//TIZEN_ONLY(200717):Efl.Gfx.Frame_Contoller: Add sector_list property
+//TIZEN_ONLY(200717):Efl.Gfx.Frame.Contoller: Add sector_list property
 EOLIAN static Eina_Iterator *
 _efl_canvas_vg_object_efl_gfx_frame_controller_sector_list_get(const Eo *obj EINA_UNUSED,
                                                                Efl_Canvas_Vg_Object_Data *pd)
index ce83370..66bfd02 100644 (file)
@@ -5,7 +5,34 @@
 
 #define MY_CLASS EFL_CANVAS_VG_SHAPE_CLASS
 
+#ifdef HAVE_THORVG
+#include "evas_tvg_path_helpers.h"
+#endif
+
+#ifdef HAVE_THORVG
+
+#define SVG_PATH_NUM_LEN 7
+#define DASH_PATTERN_EL_SIZE 2
+#define PTS_COUNT_LINE_MOVE 2
+#define PTS_COUNT_CUBIC 4
+
+typedef enum
+{
+  EFL_TVG_PATH_CMD_TYPE_NO_BEZIER = 0,   /**< For svg path commands other than listed below */
+  EFL_TVG_PATH_CMD_TYPE_BEZIER_CUBIC,    /**< For svg path commands S/s/C/c */
+  EFL_TVG_PATH_CMD_TYPE_BEZIER_QUADRATIC /**< For svg path commands T/t/Q/q */
+} Efl_Tvg_Path_Cmd_Type;
+
+typedef struct _Tvg_Color Tvg_Color;
+struct _Tvg_Color
+{
+   uint8_t r, g, b, a;
+};
+
+#endif
+
 typedef struct _Efl_Canvas_Vg_Shape_Data Efl_Canvas_Vg_Shape_Data;
+
 struct _Efl_Canvas_Vg_Shape_Data
 {
    Efl_Canvas_Vg_Node *fill;
@@ -14,8 +41,487 @@ struct _Efl_Canvas_Vg_Shape_Data
       Efl_Canvas_Vg_Node *fill;
       Efl_Canvas_Vg_Node *marker;
    } stroke;
+
+#ifdef HAVE_THORVG
+   Tvg_Paint *shape;
+
+   //Variables need by cubic, squbic and arc_to. Used to recreate properly svg_path
+   Tvg_Point curr_ctrl;
+   Tvg_Point curr;
+   Tvg_Point start;
+   Efl_Tvg_Path_Cmd_Type cmd_prev;
+
+   //Stroke scale is not supported by TVG. Scale variable is used to implement stroke
+   //scaling using scale * width
+   double scale;
+
+   //thorvg don't supported visibility, implementation is based on alpha change for stroke
+   //and fill, but color have to be saved here to recreate it when visibility will be changed back
+   Tvg_Color stroke_color;
+   Tvg_Color fill_color;
+
+   //Flag indicates if shape was pushed to canvas. Shape has access to canvas only in
+   //render function which may be called multiple times, and shape should be pushed
+   //only once.
+   Eina_Bool pushed;
+
+   //Flag indicates if shape was started. It is used to store start position
+   //and keep shape current point valid when shape close API is called.
+   Eina_Bool started;
+
+   //Used to compare it with node data. Don't call visibility change if it has the same value
+   Eina_Bool visibility;
+
+#endif
 };
 
+#ifdef HAVE_THORVG
+
+static inline double
+_interpolate(double from, double to, double pos_map)
+{
+   return (from * (1.0 - pos_map)) + (to * pos_map);
+}
+
+static Tvg_Paint*
+_get_tvg_shape(Evas_Vg_Shape *obj)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return NULL;
+
+   sd = nd->data;
+   if (!sd) return NULL;
+
+   return sd->shape;
+}
+
+static Efl_Canvas_Vg_Shape_Data*
+_get_shape_data(Evas_Vg_Shape *obj)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return NULL;
+
+   return nd->data;
+}
+
+static void
+_assign_current_point(Efl_Canvas_Vg_Shape_Data *sd, void *cmd EINA_UNUSED, double x, double y)
+{
+   if (!sd) return;
+   /* Assign current point of shape's svg path. If it's first append command
+      function additionally assign passed x,y as start point of shape's path */
+   sd->curr.x = x;
+   sd->curr.y = y;
+
+   if (sd->started == EINA_FALSE)
+     {
+        sd->start.x = x;
+        sd->start.y = y;
+     }
+}
+
+static void
+_assign_current_ctrl_point(Efl_Canvas_Vg_Shape_Data *sd, double x, double y)
+{
+   if (!sd) return;
+   sd->curr_ctrl.x = x;
+   sd->curr_ctrl.y = y;
+}
+
+static void
+_assign_command(Efl_Canvas_Vg_Shape_Data *sd, Efl_Tvg_Path_Cmd_Type c_prev)
+{
+   if (!sd) return;
+   sd->cmd_prev = c_prev;
+}
+
+
+static void
+_append_scubic_to(Evas_Vg_Shape *obj, double x, double y,
+                  double ctrl_x1, double ctrl_y1)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+   double ctrl_x0, ctrl_y0;
+
+   if (!obj) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   if (sd->cmd_prev == EFL_TVG_PATH_CMD_TYPE_BEZIER_CUBIC)
+     {
+        ctrl_x0 = 2 * sd->curr.x - sd->curr_ctrl.x;
+        ctrl_y0 = 2 * sd->curr.y - sd->curr_ctrl.y;
+     }
+   else
+     {
+        ctrl_x0 = sd->curr.x;
+        ctrl_y0 = sd->curr.y;
+     }
+
+   evas_vg_shape_append_cubic_to(obj, x, y, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1);
+}
+
+static void
+_append_quadratic(Evas_Vg_Shape *obj, double x, double y, double ctrl_x, double ctrl_y)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+   double ctrl_x0, ctrl_y0;
+   double ctrl_x1, ctrl_y1;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   // Convert quadratic bezier to cubic
+   ctrl_x0 = (sd->curr.x + 2 * ctrl_x) * (1.0 / 3.0);
+   ctrl_y0 = (sd->curr.y + 2 * ctrl_y) * (1.0 / 3.0);
+   ctrl_x1 = (x + 2 * ctrl_x) * (1.0 / 3.0);
+   ctrl_y1 = (y + 2 * ctrl_y) * (1.0 / 3.0);
+
+   evas_vg_shape_append_cubic_to(obj, x, y, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1);
+   _assign_current_ctrl_point(sd, ctrl_x, ctrl_y);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_BEZIER_QUADRATIC);
+}
+
+static void
+_append_squadratic(Evas_Vg_Shape *obj, double x, double y)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   double ctrl_x, ctrl_y;
+   double ctrl_x0, ctrl_y0;
+   double ctrl_x1, ctrl_y1;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   if (sd->cmd_prev == EFL_TVG_PATH_CMD_TYPE_BEZIER_QUADRATIC)
+     {
+        ctrl_x = 2 * sd->curr.x - sd->curr_ctrl.x;
+        ctrl_y = 2 * sd->curr.y - sd->curr_ctrl.y;
+     }
+   else
+     {
+        ctrl_x = sd->curr.x;
+        ctrl_y = sd->curr.y;
+     }
+
+   // Convert quadratic bezier to cubic
+   ctrl_x0 = (sd->curr.x + 2 * ctrl_x) * (1.0 / 3.0);
+   ctrl_y0 = (sd->curr.y + 2 * ctrl_y) * (1.0 / 3.0);
+   ctrl_x1 = (x + 2 * ctrl_x) * (1.0 / 3.0);
+   ctrl_y1 = (y + 2 * ctrl_y) * (1.0 / 3.0);
+
+   evas_vg_shape_append_cubic_to(obj, x, y, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1);
+   _assign_current_ctrl_point(sd, ctrl_x, ctrl_y);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_BEZIER_QUADRATIC);
+}
+
+// code based on enesim/moonlight sources
+static void
+_append_arc_to(Evas_Vg_Shape *obj, double x, double y,
+               double rx, double ry, double angle,
+               Eina_Bool large_arc, Eina_Bool sweep)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   Arc_To_Init_Variables var;
+   double sx, sy, ex, ey;
+   double c1x, c1y, c2x, c2y;
+   double theta2;
+   double cos_theta2, sin_theta2;
+   int i;
+
+   if (!obj) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   sx = ex = sd->curr.x;
+   sy = ey = sd->curr.y;
+
+   // if start and end points are identical, then no arc is drawn
+   if ((fabs(x - sx) < (1 / 256.0)) && (fabs(y - sy) < (1 / 256.0)))
+     return;
+   rx = fabs(rx);
+   ry = fabs(ry);
+   if ((rx < 0.5) || (ry < 0.5))
+     {
+        evas_vg_shape_append_line_to(obj, x, y);
+        return;
+     }
+
+   _arc_to_variables_initialization(x, y, rx, ry, angle, large_arc, sweep,
+                                    sx, sy, &var);
+
+   for (i = 0; i < var.segments; ++i)
+     {
+        // end angle (for this segment) = current + delta
+        theta2 = var.theta1 + var.delta;
+        cos_theta2 = cos(theta2);
+        sin_theta2 = sin(theta2);
+
+        // first control point (based on start point sx,sy)
+        c1x = sx - var.bcp * (var.cos_phi_rx * var.sin_theta1 +
+                              var.sin_phi_ry * var.cos_theta1);
+        c1y = sy + var.bcp * (var.cos_phi_ry * var.cos_theta1 -
+                              var.sin_phi_rx * var.sin_theta1);
+
+        // end point (for this segment)
+        ex = var.cx + (var.cos_phi_rx * cos_theta2 -
+                       var.sin_phi_ry * sin_theta2);
+        ey = var.cy + (var.sin_phi_rx * cos_theta2 +
+                       var.cos_phi_ry * sin_theta2);
+
+        // second control point (based on end point ex,ey)
+        c2x = ex + var.bcp * (var.cos_phi_rx * sin_theta2 +
+                              var.sin_phi_ry * cos_theta2);
+        c2y = ey + var.bcp * (var.sin_phi_rx * sin_theta2 -
+                              var.cos_phi_ry * cos_theta2);
+
+        evas_vg_shape_append_cubic_to(obj, ex, ey, c1x, c1y, c2x, c2y);
+
+        // next start point is the current end point (same for angle)
+        sx = ex;
+        sy = ey;
+        var.theta1 = theta2;
+        // avoid recomputations
+        var.cos_theta1 = cos_theta2;
+        var.sin_theta1 = sin_theta2;
+     }
+   _assign_current_ctrl_point(sd, ex, ey);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_NO_BEZIER);
+}
+
+static void
+_shape_reset(Evas_Vg_Shape *obj)
+{
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   sd = nd->data;
+
+   sd->curr_ctrl.x = sd->curr_ctrl.y = 0;
+   sd->curr.x = sd->curr.y = 0;
+   sd->cmd_prev = 0;
+   sd->start.x = sd->start.y = 0;
+
+   if (sd->fill != NULL)
+     {
+        efl_del(sd->fill);
+        sd->fill = NULL;
+     }
+
+   tvg_shape_reset(sd->shape);
+}
+
+static void
+_shape_dup(Evas_Vg_Shape *obj, Evas_Vg_Shape *dup_from)
+{
+   Efl_Canvas_Vg_Node_Data *nd_from = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd_from = NULL;
+
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj || !dup_from) return;
+
+   nd_from = efl_data_scope_get(dup_from, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd_from) return;
+
+   sd_from = nd_from->data;
+   if (!sd_from) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+
+   sd = nd->data;
+   if (!sd) return;
+
+   if (sd->shape) tvg_paint_del(sd->shape);
+   sd->shape = tvg_paint_duplicate(sd_from->shape);
+
+   sd->curr_ctrl.x = sd_from->curr_ctrl.x;
+   sd->curr_ctrl.y = sd_from->curr_ctrl.y;
+
+   sd->curr.x = sd_from->curr.x;
+   sd->curr.y = sd_from->curr.y;
+
+   sd->cmd_prev = sd_from->cmd_prev;
+
+   sd->start.x = sd_from->start.x;
+   sd->start.y = sd_from->start.y;
+   return;
+}
+
+static void
+_dash_set(Evas_Vg_Shape *obj, const Evas_Vg_Dash *dash, unsigned int length)
+{
+   float *dash_pattern = malloc(sizeof(float) * length * DASH_PATTERN_EL_SIZE);
+   unsigned int i = 0;
+
+   for (i = 0; i < length; ++i)
+     {
+        dash_pattern[DASH_PATTERN_EL_SIZE * i] = dash[i].length;
+        dash_pattern[DASH_PATTERN_EL_SIZE * i + 1] = dash[i].gap;
+     }
+
+   tvg_shape_set_stroke_dash(_get_tvg_shape(obj), dash_pattern, length * DASH_PATTERN_EL_SIZE);
+   free(dash_pattern);
+}
+
+static Eina_Bool
+_shape_properties_interpolate(Evas_Vg_Shape *obj,
+                              const Evas_Vg_Shape *from,
+                              const Evas_Vg_Shape *to,
+                              double pos_map)
+{
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+   const Efl_Canvas_Vg_Shape_Data *sd_from = NULL;
+   const Efl_Canvas_Vg_Shape_Data *sd_to = NULL;
+
+   unsigned int i = 0;
+   int a = 0, r = 0, g = 0, b = 0;
+   int to_a = 0, to_r = 0, to_g = 0, to_b = 0;
+
+   float *dash = NULL, *dash_to = NULL;
+   uint32_t dash_cnt = 0, dash_to_cnt = 0;
+
+   Tvg_Stroke_Cap cap, cap_to;
+   Tvg_Stroke_Join join, join_to;
+   float width, width_to;
+
+   if (!obj || !from || !to) return EINA_FALSE;
+
+   sd = _get_shape_data(obj);
+   sd_from = _get_shape_data((Evas_Vg_Shape*)from);
+   sd_to = _get_shape_data((Evas_Vg_Shape*)to);
+
+   efl_gfx_color_get((const Eo*)from, &r, &g, &b, &a);
+   efl_gfx_color_get((const Eo*)to, &to_r, &to_g, &to_b, &to_a);
+
+   a = _interpolate(a, to_a, pos_map);
+   r = _interpolate(r, to_r, pos_map);
+   g = _interpolate(g, to_g, pos_map);
+   b = _interpolate(b, to_b, pos_map);
+   efl_gfx_color_set((Eo*)obj, r, g, b, a);
+
+   // Stroke - color
+   tvg_shape_get_stroke_color(sd_from->shape, (uint8_t*)&r, (uint8_t*)&g, (uint8_t*)&b, (uint8_t*)&a);
+   tvg_shape_get_stroke_color(sd_from->shape, (uint8_t*)&to_r, (uint8_t*)&to_g, (uint8_t*)&to_b, (uint8_t*)&to_a);
+   a = _interpolate(a, to_a, pos_map);
+   r = _interpolate(r, to_r, pos_map);
+   g = _interpolate(g, to_g, pos_map);
+   b = _interpolate(b, to_b, pos_map);
+   tvg_shape_set_stroke_color(sd->shape, r, g, b, a);
+
+   // Stroke - width (scale)
+   tvg_shape_get_stroke_width(sd_from->shape, &width);
+   tvg_shape_get_stroke_width(sd_to->shape, &width_to);
+   sd->scale = _interpolate(sd_from->scale, sd_to->scale, pos_map);
+   width = _interpolate(width, width_to, pos_map) * sd->scale;
+   tvg_shape_set_stroke_width(sd->shape, width);
+
+   // Stroke - dash
+   tvg_shape_get_stroke_dash(sd->shape, (const float**)&dash, &dash_cnt);
+   tvg_shape_get_stroke_dash(sd_to->shape, (const float**)&dash_to, &dash_to_cnt);
+
+   if (dash && dash_to && dash_cnt > 0 && dash_cnt == dash_to_cnt)
+     {
+        for (i = 0; i < dash_cnt; ++i)
+          {
+             dash[i] = _interpolate(dash[i], dash_to[i], pos_map);
+          }
+     }
+
+   tvg_shape_set_stroke_dash(sd->shape, dash, dash_cnt);
+
+   // Stroke - cap
+   tvg_shape_get_stroke_cap(sd->shape, &cap);
+   tvg_shape_get_stroke_cap(sd_to->shape, &cap_to);
+   (pos_map < 0.5) ? tvg_shape_set_stroke_cap(sd->shape, cap) : tvg_shape_set_stroke_cap(sd->shape, cap_to);
+
+   // Stroke - join
+   tvg_shape_get_stroke_join(sd->shape, &join);
+   tvg_shape_get_stroke_join(sd_to->shape, &join_to);
+   (pos_map < 0.5) ? tvg_shape_set_stroke_join(sd->shape, join) : tvg_shape_set_stroke_join(sd->shape, join_to);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_shape_interpolate(Evas_Vg_Shape *obj,
+                   const Evas_Vg_Shape *from,
+                   const Evas_Vg_Shape *to,
+                   double pos_map)
+{
+   evas_vg_shape_reset(obj);
+
+   Tvg_Paint *tvg_dest = NULL;
+   Tvg_Paint *tvg_from = NULL;
+   Tvg_Paint *tvg_to = NULL;
+
+   Tvg_Path_Command *path_commands_from = NULL;
+   Tvg_Point *path_coords_from = NULL;
+
+   uint32_t cmds_count_from = 0;
+   uint32_t pts_count_from = 0;
+
+   Tvg_Path_Command *path_commands_to = NULL;
+   Tvg_Point *path_coords_to = NULL;
+
+   uint32_t cmds_count_to = 0;
+   uint32_t pts_count_to = 0;
+
+   Tvg_Point *path_coords_dest = NULL;
+   unsigned int i = 0;
+
+   if (!obj || !from || !to) return EINA_FALSE;
+
+   tvg_dest = _get_tvg_shape(obj);
+   tvg_from = _get_tvg_shape((Evas_Vg_Shape*)from);
+   tvg_to = _get_tvg_shape((Evas_Vg_Shape*)to);
+
+   if (!tvg_dest || !tvg_from || !tvg_to) return EINA_FALSE;
+
+   tvg_shape_get_path_commands(tvg_from, (const Tvg_Path_Command**)&path_commands_from, &cmds_count_from);
+   tvg_shape_get_path_coords(tvg_from, (const Tvg_Point**)&path_coords_from, &pts_count_from);
+   tvg_shape_get_path_commands(tvg_to, (const Tvg_Path_Command**)&path_commands_to, &cmds_count_to);
+   tvg_shape_get_path_coords(tvg_to, (const Tvg_Point**)&path_coords_to, &pts_count_to);
+
+   path_coords_dest = (Tvg_Point*)malloc(sizeof(Tvg_Point) * pts_count_from);
+   for (i = 0; i < pts_count_from; ++i)
+     {
+        path_coords_dest[i].x = _interpolate(path_coords_from[i].x, path_coords_to[i].x, pos_map);
+        path_coords_dest[i].y = _interpolate(path_coords_from[i].y, path_coords_to[i].y, pos_map);
+     }
+
+   _shape_properties_interpolate(obj, from, to, pos_map);
+   tvg_shape_append_path(tvg_dest, path_commands_from, cmds_count_from, path_coords_dest, pts_count_from);
+
+   return EINA_TRUE;
+}
+#endif
 // FIXME: Use the renderer bounding box when it has been created instead of an estimation
 
 static void
@@ -24,9 +530,14 @@ _efl_canvas_vg_shape_fill_set(Eo *obj EINA_UNUSED,
                        Efl_Canvas_Vg_Node *f)
 {
    if (pd->fill == f) return;
-
+#ifdef HAVE_THORVG
+   if (efl_isa(pd->fill, EFL_CANVAS_VG_GRADIENT_CLASS))
+     {
+        efl_del(pd->fill);
+        pd->fill = NULL;
+     }
+#endif
    Efl_Canvas_Vg_Node *tmp = pd->fill;
-
    pd->fill = efl_ref(f);
    efl_unref(tmp);
 }
@@ -74,6 +585,7 @@ _efl_canvas_vg_shape_stroke_marker_get(const Eo *obj EINA_UNUSED,
    return pd->stroke.marker;
 }
 
+#ifndef HAVE_THORVG
 static void
 _efl_canvas_vg_shape_render_pre(Evas_Object_Protected_Data *vg_pd,
                                 Efl_VG *obj,
@@ -124,6 +636,123 @@ _efl_canvas_vg_shape_render_pre(Evas_Object_Protected_Data *vg_pd,
    ector_renderer_prepare(nd->renderer);
    ector_renderer_comp_method_set(nd->renderer, comp, comp_method);
 }
+#endif
+
+#ifdef HAVE_THORVG
+static void
+_visibility_set(Efl_Canvas_Vg_Node_Data *nd)
+{
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!nd || !nd->data) return;
+   sd = nd->data;
+
+   if (sd->visibility == nd->visibility) return;
+   sd->visibility = nd->visibility;
+
+   if (nd->visibility == EINA_FALSE)
+     {
+        tvg_shape_get_stroke_color(sd->shape,
+                                   &sd->stroke_color.r,
+                                   &sd->stroke_color.g,
+                                   &sd->stroke_color.b,
+                                   &sd->stroke_color.a);
+
+        tvg_shape_get_fill_color(sd->shape,
+                                 &sd->fill_color.r,
+                                 &sd->fill_color.g,
+                                 &sd->fill_color.b,
+                                 &sd->fill_color.a);
+
+        tvg_shape_set_stroke_color(sd->shape, 0, 0, 0, 0);
+        tvg_shape_set_fill_color(sd->shape, 0, 0, 0, 0);
+     }
+   else
+     {
+        tvg_shape_set_stroke_color(sd->shape,
+                                   sd->stroke_color.r,
+                                   sd->stroke_color.g,
+                                   sd->stroke_color.b,
+                                   sd->stroke_color.a);
+
+        tvg_shape_set_fill_color(sd->shape,
+                                 sd->fill_color.r,
+                                 sd->fill_color.g,
+                                 sd->fill_color.b,
+                                 sd->fill_color.a);
+     }
+}
+static void
+_convert_eina_to_tvg_mat(const Eina_Matrix3 *eina_mat, Tvg_Matrix *tvg_mat)
+{
+   if (!eina_mat || !tvg_mat) return;
+
+   tvg_mat->e11 = eina_mat->xx;
+   tvg_mat->e12 = eina_mat->xy;
+   tvg_mat->e13 = eina_mat->xz;
+
+   tvg_mat->e21 = eina_mat->yx;
+   tvg_mat->e22 = eina_mat->yy;
+   tvg_mat->e23 = eina_mat->yz;
+
+   tvg_mat->e31 = eina_mat->zx;
+   tvg_mat->e32 = eina_mat->zy;
+   tvg_mat->e33 = eina_mat->zz;
+}
+
+static void
+_efl_canvas_vg_shape_render_pre_tvg(EINA_UNUSED Evas_Object_Protected_Data *vg_pd,
+                                    Efl_VG *obj,
+                                    Efl_Canvas_Vg_Node_Data *nd,
+                                    void *canvas)
+{
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+   Efl_Canvas_Vg_Gradient_Data *gd = NULL;
+
+   Tvg_Matrix trans_mat = { 0 };
+   const Eina_Matrix3 *m = NULL;
+
+   int r = 0, g = 0, b = 0, a = 0;
+
+   if (!nd || !nd->data)  return;
+   sd = nd->data;
+
+   nd->flags = EFL_GFX_CHANGE_FLAG_NONE;
+   tvg_paint_translate(sd->shape, nd->x, nd->y);
+
+   // set color fill
+   if (sd->fill != NULL && efl_isa(sd->fill, EFL_CANVAS_VG_GRADIENT_CLASS))
+     {
+        gd = efl_data_scope_get(sd->fill, EFL_CANVAS_VG_GRADIENT_CLASS);
+        gd->shape = obj;
+        gd->gradient_render_pre_tvg(sd->fill, gd, sd->shape);
+     }
+
+   m = evas_vg_node_transformation_get(obj);
+   if (m)
+     {
+        _convert_eina_to_tvg_mat(m, &trans_mat);
+        trans_mat.e13 += nd->x;
+        trans_mat.e23 += nd->y;
+
+        tvg_paint_scale(sd->shape, trans_mat.e11);
+        tvg_paint_transform(sd->shape, &trans_mat);
+     }
+
+   _visibility_set(nd);
+
+   if (sd->pushed == EINA_FALSE)
+     {
+        tvg_canvas_push((Tvg_Canvas *) canvas, sd->shape);
+        sd->pushed = EINA_TRUE;
+     }
+
+   //get fill color and set it
+   efl_gfx_color_get(obj, &r, &g, &b, &a);
+   tvg_shape_set_fill_color(sd->shape, r, g, b, a);
+   tvg_canvas_update_paint((Tvg_Canvas *) canvas, sd->shape);
+}
+#endif
 
 static Eo *
 _efl_canvas_vg_shape_efl_object_constructor(Eo *obj, Efl_Canvas_Vg_Shape_Data *pd)
@@ -131,7 +760,9 @@ _efl_canvas_vg_shape_efl_object_constructor(Eo *obj, Efl_Canvas_Vg_Shape_Data *p
    Efl_Canvas_Vg_Node_Data *nd;
 
    obj = efl_constructor(efl_super(obj, MY_CLASS));
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
 
+#ifndef HAVE_THORVG
    efl_gfx_shape_stroke_scale_set(obj, 1);
    efl_gfx_shape_stroke_location_set(obj, 0.5);
    efl_gfx_shape_stroke_cap_set(obj, EFL_GFX_CAP_BUTT);
@@ -141,10 +772,15 @@ _efl_canvas_vg_shape_efl_object_constructor(Eo *obj, Efl_Canvas_Vg_Shape_Data *p
    //      https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit
    efl_gfx_shape_stroke_miterlimit_set(obj, 4);
 
-   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
    nd->render_pre = _efl_canvas_vg_shape_render_pre;
-   nd->data = pd;
+#else
+   nd->render_pre_tvg = _efl_canvas_vg_shape_render_pre_tvg;
+   pd->shape = tvg_shape_new();
+   pd->visibility = EINA_TRUE;
+   pd->scale = 1.0;
+#endif
 
+   nd->data = pd;
    return obj;
 }
 
@@ -155,7 +791,14 @@ _efl_canvas_vg_shape_efl_object_destructor(Eo *obj, Efl_Canvas_Vg_Shape_Data *pd
    if (pd->stroke.fill) efl_unref(pd->stroke.fill);
    if (pd->stroke.marker) efl_unref(pd->stroke.marker);
 
+#ifdef HAVE_THORVG
+   //tvg_canvas_destroy is called before and it frees all shapes so there is no need to
+   //call tvg_paint_del API.
+   pd->shape = NULL;
+#else
    efl_gfx_path_reset(obj);
+#endif
+
    efl_destructor(efl_super(obj, MY_CLASS));
 }
 
@@ -206,6 +849,7 @@ _efl_canvas_vg_shape_efl_gfx_path_commit(Eo *obj,
 EOLIAN static Efl_Canvas_Vg_Node *
 _efl_canvas_vg_shape_efl_duplicate_duplicate(const Eo *obj, Efl_Canvas_Vg_Shape_Data *pd)
 {
+   //TODO: implement me!
    Efl_Canvas_Vg_Node *node;
    Efl_Canvas_Vg_Shape_Data *sd;
 
@@ -231,58 +875,109 @@ _efl_canvas_vg_shape_efl_duplicate_duplicate(const Eo *obj, Efl_Canvas_Vg_Shape_
      }
 
    efl_gfx_path_copy_from(node, obj);
-
    return node;
 }
 
 EAPI double
 evas_vg_shape_stroke_scale_get(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return 0.0;
+
+   sd = _get_shape_data(obj);
+   if (!sd) return 0.0;
+
+   return sd->scale;
+#else
    return efl_gfx_shape_stroke_scale_get(obj);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_scale_set(Evas_Vg_Shape *obj, double s)
 {
+#ifdef HAVE_THORVG
+   float width = 0;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+   if (!obj) return;
+
+   sd = _get_shape_data(obj);
+   if (!sd) return;
+
+   tvg_shape_get_stroke_width(sd->shape, &width);
+   width = width * s;
+   sd->scale = s;
+
+   tvg_shape_set_stroke_width(sd->shape, width);
+#else
    efl_gfx_shape_stroke_scale_set(obj, s);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_stroke_color_get(Evas_Vg_Shape *obj, int *r, int *g, int *b, int *a)
 {
+#ifdef HAVE_THORVG
+   tvg_shape_get_stroke_color(_get_tvg_shape(obj),
+                              (uint8_t*)r,
+                              (uint8_t*)g,
+                              (uint8_t*)b,
+                              (uint8_t*)a);
+   evas_color_argb_premul(*a, r, g, b);
+#else
    efl_gfx_shape_stroke_color_get(obj, r, g, b, a);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_color_set(Evas_Vg_Shape *obj, int r, int g, int b, int a)
 {
+#ifdef HAVE_THORVG
+   evas_color_argb_unpremul(a, &r, &g, &b);
+   tvg_shape_set_stroke_color(_get_tvg_shape(obj), r, g, b, a);
+#else
    efl_gfx_shape_stroke_color_set(obj, r, g, b, a);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI double
 evas_vg_shape_stroke_width_get(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   float w = 0.0;
+   tvg_shape_get_stroke_width(_get_tvg_shape(obj), &w);
+   return w;
+#else
    return efl_gfx_shape_stroke_width_get(obj);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_width_set(Evas_Vg_Shape *obj, double w)
 {
+#ifdef HAVE_THORVG
+   tvg_shape_set_stroke_width(_get_tvg_shape(obj), w);
+#else
    efl_gfx_shape_stroke_width_set(obj, w);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI double
 evas_vg_shape_stroke_location_get(Evas_Vg_Shape *obj)
 {
+   //TODO: implement
    return efl_gfx_shape_stroke_location_get(obj);
 }
 
 EAPI void
 evas_vg_shape_stroke_location_set(Evas_Vg_Shape *obj, double centered)
 {
+   //TODO: implement
    efl_gfx_shape_stroke_location_set(obj, centered);
    efl_canvas_vg_node_change(obj);
 }
@@ -290,45 +985,95 @@ evas_vg_shape_stroke_location_set(Evas_Vg_Shape *obj, double centered)
 EAPI void
 evas_vg_shape_stroke_dash_get(Evas_Vg_Shape *obj, const Evas_Vg_Dash **dash, unsigned int *length)
 {
+#ifdef HAVE_THORVG
+   const float *dashPattern;
+   uint32_t cnt = 0;
+
+   Evas_Vg_Dash *d = NULL;
+
+   unsigned int i = 0;
+   if (!dash || !length) return;
+
+   tvg_shape_get_stroke_dash(_get_tvg_shape(obj), &dashPattern, &cnt);
+   if (!cnt) return;
+
+   d = malloc(sizeof(Evas_Vg_Dash) * cnt / DASH_PATTERN_EL_SIZE);
+   if (!d) return;
+
+   for (i = 0; i < cnt; i += DASH_PATTERN_EL_SIZE)
+     {
+        d[i].length = dashPattern[i];
+        d[i].gap = dashPattern[i+1];
+     }
+
+   *dash = d;
+   *length = cnt;
+#else
    efl_gfx_shape_stroke_dash_get(obj, (const Efl_Gfx_Dash **)dash, length);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_dash_set(Evas_Vg_Shape *obj, const Evas_Vg_Dash *dash, unsigned int length)
 {
+#ifdef HAVE_THORVG
+   _dash_set(obj, dash, length);
+#else
    efl_gfx_shape_stroke_dash_set(obj, (const Efl_Gfx_Dash *)dash, length);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI Evas_Vg_Cap
 evas_vg_shape_stroke_cap_get(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   Tvg_Stroke_Cap cap;
+   tvg_shape_get_stroke_cap(_get_tvg_shape(obj), &cap);
+   return (Evas_Vg_Cap)cap;
+#else
    return (Evas_Vg_Cap)efl_gfx_shape_stroke_cap_get(obj);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_cap_set(Evas_Vg_Shape *obj, Evas_Vg_Cap c)
 {
+#ifdef HAVE_THORVG
+   tvg_shape_set_stroke_cap(_get_tvg_shape(obj), (Tvg_Stroke_Cap) c);
+#else
    efl_gfx_shape_stroke_cap_set(obj, (Efl_Gfx_Cap)c);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI Evas_Vg_Join
 evas_vg_shape_stroke_join_get(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   Tvg_Stroke_Join join;
+   tvg_shape_get_stroke_join(_get_tvg_shape(obj), &join);
+   return (Evas_Vg_Join)join;
+#else
    return (Evas_Vg_Join)efl_gfx_shape_stroke_join_get(obj);
+#endif
 }
 
 EAPI void
 evas_vg_shape_stroke_join_set(Evas_Vg_Shape *obj, Evas_Vg_Join j)
 {
+#ifdef HAVE_THORVG
+   tvg_shape_set_stroke_join(_get_tvg_shape(obj), (Tvg_Stroke_Join) j);
+#else
    efl_gfx_shape_stroke_join_set(obj, (Efl_Gfx_Join)j);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_path_set(Evas_Vg_Shape *obj, const Evas_Vg_Path_Command *op, const double *points)
 {
+   //TODO: implement
    efl_gfx_path_set(obj, (const Efl_Gfx_Path_Command *)op, points);
    efl_canvas_vg_node_change(obj);
 }
@@ -336,129 +1081,306 @@ evas_vg_shape_path_set(Evas_Vg_Shape *obj, const Evas_Vg_Path_Command *op, const
 EAPI void
 evas_vg_shape_path_get(Evas_Vg_Shape *obj, const Evas_Vg_Path_Command **op, const double **points)
 {
+   //TODO: implement
    efl_gfx_path_get(obj, (const Efl_Gfx_Path_Command **)op, points);
 }
 
 EAPI void
 evas_vg_shape_path_length_get(Evas_Vg_Shape *obj, unsigned int *commands, unsigned int *points)
 {
+   //TODO: implement
    efl_gfx_path_length_get(obj, commands, points);
 }
 
 EAPI void
 evas_vg_shape_current_get(Evas_Vg_Shape *obj, double *x, double *y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   if (x) *x = sd->curr.x;
+   if (y) *y = sd->curr.y;
+#else
    efl_gfx_path_current_get(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_shape_current_ctrl_get(Evas_Vg_Shape *obj, double *x, double *y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Node_Data *nd = NULL;
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+
+   nd = efl_data_scope_get(obj, EFL_CANVAS_VG_NODE_CLASS);
+   if (!nd) return;
+   sd = nd->data;
+   if (!sd) return;
+
+   if (x) *x = sd->curr_ctrl.x;
+   if (y) *y = sd->curr_ctrl.y;
+#else
    efl_gfx_path_current_ctrl_get(obj, x, y);
+#endif
 }
 
 EAPI void
 evas_vg_shape_dup(Evas_Vg_Shape *obj, Evas_Vg_Shape *dup_from)
 {
+#ifdef HAVE_THORVG
+   _shape_dup(obj, dup_from);
+#else
    efl_gfx_path_copy_from(obj, dup_from);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_reset(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   _shape_reset(obj);
+#else
    efl_gfx_path_reset(obj);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_move_to(Evas_Vg_Shape *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   tvg_shape_move_to(sd->shape, x, y);
+   _assign_current_point(sd, NULL, x, y);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_NO_BEZIER);
+#else
    efl_gfx_path_append_move_to(obj, x, y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_line_to(Evas_Vg_Shape *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   tvg_shape_line_to(sd->shape, x, y);
+   _assign_current_point(sd, NULL, x, y);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_NO_BEZIER);
+#else
    efl_gfx_path_append_line_to(obj, x, y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_quadratic_to(Evas_Vg_Shape *obj, double x, double y, double ctrl_x, double ctrl_y)
 {
+#ifdef HAVE_THORVG
+   _append_quadratic(obj, x, y, ctrl_x, ctrl_y);
+#else
    efl_gfx_path_append_quadratic_to(obj, x, y, ctrl_x, ctrl_y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_squadratic_to(Evas_Vg_Shape *obj, double x, double y)
 {
+#ifdef HAVE_THORVG
+   _append_squadratic(obj, x, y);
+#else
    efl_gfx_path_append_squadratic_to(obj, x, y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_cubic_to(Evas_Vg_Shape *obj, double x, double y, double ctrl_x0, double ctrl_y0, double ctrl_x1, double ctrl_y1)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   tvg_shape_cubic_to(sd->shape, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1, x, y);
+   _assign_current_point(sd, NULL, x, y);
+   _assign_current_ctrl_point(sd, ctrl_x1, ctrl_y1);
+   _assign_command(sd, EFL_TVG_PATH_CMD_TYPE_BEZIER_CUBIC);
+#else
    efl_gfx_path_append_cubic_to(obj, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1, x, y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_scubic_to(Evas_Vg_Shape *obj, double x, double y, double ctrl_x, double ctrl_y)
 {
+#ifdef HAVE_THORVG
+   _append_scubic_to(obj, x, y, ctrl_x, ctrl_y);
+#else
    efl_gfx_path_append_scubic_to(obj, x, y, ctrl_x, ctrl_y);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_arc_to(Evas_Vg_Shape *obj, double x, double y, double rx, double ry, double angle, Eina_Bool large_arc, Eina_Bool sweep)
 {
+#ifdef HAVE_THORVG
+   _append_arc_to(obj, x, y, rx, ry, angle, large_arc, sweep);
+#else
    efl_gfx_path_append_arc_to(obj, x, y, rx, ry, angle, large_arc, sweep);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_arc(Evas_Vg_Shape *obj, double x, double y, double w, double h, double start_angle, double sweep_length)
 {
+#ifndef HAVE_THORVG
    efl_gfx_path_append_arc(obj, x, y, w, h, start_angle, sweep_length);
+#else
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   float radius = fmin(w / 2.0f, h / 2.0f);
+   float cx = x + (w / 2.0f);
+   float cy = y + (h / 2.0f);
+
+   tvg_shape_append_arc(sd->shape, cx, cy, radius, start_angle, sweep_length, 0);
+   _assign_current_point(sd, NULL, cx, cy);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_close(Evas_Vg_Shape *obj)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = _get_shape_data(obj);
+   if (!sd) return;
+
+   tvg_shape_close(sd->shape);
+
+   sd->curr.x = sd->start.x;
+   sd->curr.y = sd->start.y;
+#else
    efl_gfx_path_append_close(obj);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_circle(Evas_Vg_Shape *obj, double x, double y, double radius)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   tvg_shape_append_circle(sd->shape, x, y, radius, radius);
+   _assign_current_point(sd, NULL, x, y - radius);
+#else
    efl_gfx_path_append_circle(obj, x, y, radius);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_rect(Evas_Vg_Shape *obj, double x, double y, double w, double h, double rx, double ry)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = NULL;
+
+   if (!obj) return;
+   sd = _get_shape_data(obj);
+   if (!sd || !sd->shape) return;
+
+   tvg_shape_append_rect(sd->shape, x, y, w, h, rx, ry);
+   _assign_current_point(sd, NULL, x, y);
+#else
    efl_gfx_path_append_rect(obj, x, y, w, h, rx, ry);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI void
 evas_vg_shape_append_svg_path(Evas_Vg_Shape *obj, const char *svg_path_data)
 {
+#ifdef HAVE_THORVG
+   Efl_Canvas_Vg_Shape_Data *sd = _get_shape_data(obj);
+   Efl_Tvg_Shape_Svg_Path svg_path = { 0 };
+
+   double number_array[SVG_PATH_NUM_LEN] = {0};
+   int number_count = 0;
+   double cur_x = 0, cur_y = 0;
+   double cur_ctl_x = 0, cur_ctl_y = 0;
+
+   char pth_cmd = 0;
+   Eina_Bool is_quadratic = EINA_FALSE;
+   char *path = (char*) svg_path_data;
+
+   if (!sd) return;
+
+   while (path[0] != '\0')
+     {
+        path = _next_command(path, &pth_cmd, number_array, &number_count);
+
+        if (!path) break;
+        _process_command(&svg_path, pth_cmd, number_array,
+                         &number_count, &cur_x, &cur_y,
+                         &cur_ctl_x, &cur_ctl_y, &is_quadratic);
+     }
+
+   tvg_shape_append_path(sd->shape, svg_path.cmds, svg_path.cmds_cnt, svg_path.pts, svg_path.pts_cnt);
+
+   if (svg_path.cmds) free(svg_path.cmds);
+   if (svg_path.pts) free(svg_path.pts);
+#else
    efl_gfx_path_append_svg_path(obj, svg_path_data);
+#endif
    efl_canvas_vg_node_change(obj);
 }
 
 EAPI Eina_Bool
 evas_vg_shape_interpolate(Evas_Vg_Shape *obj, const Evas_Vg_Shape *from, const Evas_Vg_Shape *to, double pos_map)
 {
-   Eina_Bool ret = efl_gfx_path_interpolate(obj, from, to, pos_map);
+   Eina_Bool ret = EINA_FALSE;
+#ifdef HAVE_THORVG
+   if (!evas_vg_shape_equal_commands((Evas_Vg_Shape*)from, (Evas_Vg_Shape*)to)) return EINA_FALSE;
+   ret = _shape_interpolate(obj, from, to, pos_map);
+#else
+   ret = efl_gfx_path_interpolate(obj, from, to, pos_map);
+#endif
    efl_canvas_vg_node_change(obj);
    return ret;
 }
@@ -466,7 +1388,30 @@ evas_vg_shape_interpolate(Evas_Vg_Shape *obj, const Evas_Vg_Shape *from, const E
 EAPI Eina_Bool
 evas_vg_shape_equal_commands(Evas_Vg_Shape *obj, const Evas_Vg_Shape *with)
 {
+#ifdef HAVE_THORVG
+   const Tvg_Path_Command *cmds = NULL;
+   const Tvg_Path_Command *cmds_with = NULL;
+
+   uint32_t cmds_count = 0;
+   uint32_t cmds_count_with = 0;
+
+   unsigned int i = 0;
+
+   tvg_shape_get_path_commands(_get_tvg_shape(obj), &cmds, &cmds_count);
+   tvg_shape_get_path_commands(_get_tvg_shape((Evas_Vg_Shape*)with), &cmds_with, &cmds_count_with);
+
+   if (!cmds || !cmds_count) return EINA_FALSE;
+   if (cmds_count != cmds_count_with) return EINA_FALSE;
+
+   for (i = 0; i < cmds_count; ++i)
+     {
+        if (cmds[i] != cmds_with[i]) return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+#else
    return efl_gfx_path_equal_commands(obj, with);
+#endif
 }
 
 EAPI Efl_Canvas_Vg_Shape*
diff --git a/src/lib/evas/canvas/evas_tvg_path_helpers.h b/src/lib/evas/canvas/evas_tvg_path_helpers.h
new file mode 100644 (file)
index 0000000..36535dc
--- /dev/null
@@ -0,0 +1,619 @@
+#ifndef _EVAS_TVG_PATH_HELPERS_H_
+#define _EVAS_TVG_PATH_HELPERS_H_
+
+/*
+ * structs and functions used by evas_vg_shape_append_svg_path/arc_to API,
+ * which will be deprecated at some point
+ */
+
+typedef struct _Efl_Tvg_Shape_Svg_Path  Efl_Tvg_Shape_Svg_Path;
+typedef struct _Arc_To_Init_Variables   Arc_To_Init_Variables;
+
+struct _Efl_Tvg_Shape_Svg_Path {
+   char             *svg_path_data;
+   Tvg_Path_Command *cmds;
+   Tvg_Point        *pts;
+   uint32_t          cmds_cnt;
+   uint32_t          pts_cnt;
+   uint32_t          cmds_reserved;
+   uint32_t          pts_reserved;
+};
+
+struct _Arc_To_Init_Variables {
+   double cx, cy;
+   double theta1;
+   double delta, bcp;
+   double cos_phi_rx, cos_phi_ry;
+   double sin_phi_rx, sin_phi_ry;
+   double cos_theta1, sin_theta1;
+   int    segments;
+};
+
+static char *
+_skipcomma(const char *content)
+{
+   while (*content && isspace(*content)) content++;
+   if (*content == ',') return (char*) content + 1;
+   return (char*) content;
+}
+
+static inline Eina_Bool
+_parse_number(char **content, double *number)
+{
+   char *end = NULL;
+   *number = strtod(*content, &end);
+   if ((*content) == end) return EINA_FALSE;
+   *content = _skipcomma(end);
+   return EINA_TRUE;
+}
+
+static inline Eina_Bool
+_parse_flag(char **content, int *number)
+{
+   char *end = NULL;
+   char *end_test = NULL;
+   char content_test[strlen(*content) + 1];
+   int number_test;
+
+   strcpy(content_test,*content);
+   *number = strtol(*content, &end, 10) ? 1 : 0;
+   if ((*content) == end) return EINA_FALSE;
+   number_test = (int)strtof(content_test, &end_test);
+
+   if ((*number != number_test) || (*end != *end_test))
+     return EINA_FALSE;
+
+   *content = _skipcomma(end);
+   return EINA_TRUE;
+}
+
+static int
+_number_count(char cmd)
+{
+   int count = 0;
+   switch (cmd)
+     {
+      case 'M':
+      case 'm':
+      case 'L':
+      case 'l':
+      case 'T':
+      case 't':
+        {
+           count = 2;
+           break;
+        }
+      case 'C':
+      case 'c':
+      case 'E':
+      case 'e':
+        {
+           count = 6;
+           break;
+        }
+      case 'H':
+      case 'h':
+      case 'V':
+      case 'v':
+        {
+           count = 1;
+           break;
+        }
+      case 'S':
+      case 's':
+      case 'Q':
+      case 'q':
+        {
+           count = 4;
+           break;
+        }
+      case 'A':
+      case 'a':
+        {
+           count = 7;
+           break;
+        }
+      default:
+         break;
+      }
+   return count;
+}
+
+static char*
+_next_command(char *path, char *cmd, double *arr, int *count)
+{
+   int i = 0, large, sweep;
+
+   path = _skipcomma(path);
+   if (isalpha(*path))
+     {
+        *cmd = *path;
+        path++;
+        *count = _number_count(*cmd);
+     }
+   else
+     {
+        if (*cmd == 'm')
+          *cmd = 'l';
+        else if (*cmd == 'M')
+          *cmd = 'L';
+     }
+
+   if (*count == 7)
+     {
+        // special case for arc command
+        if (_parse_number(&path, &arr[0]) &&
+            _parse_number(&path, &arr[1]) &&
+            _parse_number(&path, &arr[2]) &&
+            _parse_flag(&path, &large)    &&
+            _parse_flag(&path, &sweep)    &&
+            _parse_number(&path, &arr[5]) &&
+            _parse_number(&path, &arr[6]))
+           {
+              arr[3] = large;
+              arr[4] = sweep;
+              return path;
+           }
+        *count = 0;
+        return NULL;
+     }
+
+   for (i = 0; i < *count; i++)
+     {
+        if (!_parse_number(&path, &arr[i]))
+          {
+             *count = 0;
+             return NULL;
+          }
+        path = _skipcomma(path);
+     }
+
+   return path;
+}
+
+/*
+ * code adapted from enesim which was adapted from moonlight sources
+ */
+static void
+_arc_to_variables_initialization(double x, double y, double rx, double ry,
+                                 double angle, Eina_Bool large_arc, Eina_Bool sweep,
+                                 double sx, double sy, Arc_To_Init_Variables *var)
+{
+   double cxp, cyp;
+   double cos_phi, sin_phi;
+   double dx2, dy2;
+   double x1p, y1p;
+   double x1p2, y1p2;
+   double rx2, ry2;
+   double lambda;
+   double c;
+   double at;
+   double delta_theta;
+   double nat;
+
+   angle = angle * M_PI / 180.0;
+   cos_phi = cos(angle);
+   sin_phi = sin(angle);
+   dx2 = (sx - x) / 2.0;
+   dy2 = (sy - y) / 2.0;
+   x1p = cos_phi * dx2 + sin_phi * dy2;
+   y1p = cos_phi * dy2 - sin_phi * dx2;
+   x1p2 = x1p * x1p;
+   y1p2 = y1p * y1p;
+   rx2 = rx * rx;
+   ry2 = ry * ry;
+   lambda = (x1p2 / rx2) + (y1p2 / ry2);
+
+   // Correction of out-of-range radii, see F6.6.2 (step 4)
+   if (lambda > 1.0)
+     {
+        // see F6.6.3
+        double lambda_root = sqrt(lambda);
+
+        rx *= lambda_root;
+        ry *= lambda_root;
+        // update rx2 and ry2
+        rx2 = rx * rx;
+        ry2 = ry * ry;
+     }
+
+   c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
+
+   // check if there is no possible solution
+   // (i.e. we can't do a square root of a negative value)
+   if (c < 0.0)
+     {
+        // scale uniformly until we have a single solution
+        // (see F6.2) i.e. when c == 0.0
+        double scale = sqrt(1.0 - c / (rx2 * ry2));
+        rx *= scale;
+        ry *= scale;
+        // update rx2 and ry2
+        rx2 = rx * rx;
+        ry2 = ry * ry;
+
+        // step 2 (F6.5.2) - simplified since c == 0.0
+        cxp = 0.0;
+        cyp = 0.0;
+        // step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
+        var->cx = 0.0;
+        var->cy = 0.0;
+     }
+   else
+     {
+        // complete c calculation
+        c = sqrt(c / ((rx2 * y1p2) + (ry2 * x1p2)));
+        // inverse sign if Fa == Fs
+        if (large_arc == sweep)
+          c = -c;
+
+        // step 2 (F6.5.2)
+        cxp = c * ( rx * y1p / ry);
+        cyp = c * (-ry * x1p / rx);
+
+        // step 3 (F6.5.3 first part)
+        var->cx = cos_phi * cxp - sin_phi * cyp;
+        var->cy = sin_phi * cxp + cos_phi * cyp;
+     }
+
+   // step 3 (F6.5.3 second part) we now have the center point of the ellipse
+   var->cx += (sx + x) / 2.0;
+   var->cy += (sy + y) / 2.0;
+
+   // step 4 (F6.5.4)
+   // we don't use arccos (as per w3c doc), see
+   // http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
+   // note: atan2 (0.0, 1.0) == 0.0
+   at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
+   var->theta1 = (at < 0.0) ? 2.0 * M_PI + at : at;
+
+   nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
+   delta_theta = (nat < at) ? 2.0 * M_PI - at + nat : nat - at;
+
+   if (sweep)
+     {
+        // ensure delta theta < 0 or else add 360 degrees
+        if (delta_theta < 0.0)
+          delta_theta += 2.0 * M_PI;
+     }
+   else
+     {
+        // ensure delta theta > 0 or else substract 360 degrees
+        if (delta_theta > 0.0)
+          delta_theta -= 2.0 * M_PI;
+     }
+
+   // add several cubic bezier to approximate the arc
+   // (smaller than 90 degrees)
+   // we add one extra segment because we want something
+   // smaller than 90deg (i.e. not 90 itself)
+   var->segments = (int) (fabs(delta_theta / M_PI_2)) + 1;
+   var->delta = delta_theta / var->segments;
+
+   // http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
+   var->bcp = 4.0 / 3 * (1 - cos(var->delta / 2)) / sin(var->delta / 2);
+
+   var->cos_phi_rx = cos_phi * rx;
+   var->cos_phi_ry = cos_phi * ry;
+   var->sin_phi_rx = sin_phi * rx;
+   var->sin_phi_ry = sin_phi * ry;
+
+   var->cos_theta1 = cos(var->theta1);
+   var->sin_theta1 = sin(var->theta1);
+}
+
+static Eina_Bool
+_svg_path_grow(Efl_Tvg_Shape_Svg_Path *svg_path, int command_length)
+{
+   if (svg_path->pts_cnt + command_length > svg_path->pts_reserved)
+     {
+        Tvg_Point *pts_tmp = realloc(svg_path->pts, sizeof(Tvg_Point) *
+                             (svg_path->pts_cnt + command_length) * 2);
+        if (!pts_tmp) return EINA_FALSE;
+        svg_path->pts_reserved = (svg_path->pts_cnt + command_length) * 2;
+        svg_path->pts = pts_tmp;
+     }
+
+   if (svg_path->cmds_cnt + 1 > svg_path->cmds_reserved)
+     {
+        Tvg_Path_Command *cmds_tmp = realloc(svg_path->cmds, sizeof(Tvg_Path_Command) *
+                                     (svg_path->cmds_cnt + 1) * 2);
+        if (!cmds_tmp) return EINA_FALSE;
+        svg_path->cmds_reserved = (svg_path->cmds_cnt + 1) * 2;
+        svg_path->cmds = cmds_tmp;
+     }
+
+   svg_path->pts_cnt += command_length;
+   svg_path->cmds_cnt++;
+
+   return EINA_TRUE;
+}
+
+static void
+_process_command(Efl_Tvg_Shape_Svg_Path *svg_path, char c, double *pts,
+                 int *number_array, double *cur_x, double *cur_y,
+                 double *cur_ctl_x, double *cur_ctl_y, Eina_Bool *is_quadratic)
+{
+   int i;
+   switch (c)
+     {
+      case 'm':
+      case 'l':
+      case 'c':
+      case 's':
+      case 'q':
+      case 't':
+        {
+           for(i = 0; i < *number_array - 1; i += 2)
+             {
+                pts[i]   += *cur_x;
+                pts[i+1] += *cur_y;
+             }
+           break;
+        }
+      case 'h':
+        {
+           pts[0] += *cur_x;
+           break;
+        }
+      case 'v':
+        {
+           pts[0] += *cur_y;
+           break;
+        }
+      case 'a':
+        {
+           pts[5] += *cur_x;
+           pts[6] += *cur_y;
+           break;
+        }
+      default:
+         break;
+      }
+
+   switch (c)
+     {
+      case 'm':
+      case 'M':
+        {
+           if (!_svg_path_grow(svg_path, 1)) break;
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[1];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_MOVE_TO;
+           *cur_x = pts[0];
+           *cur_y = pts[1];
+           break;
+        }
+      case 'l':
+      case 'L':
+        {
+           if (!_svg_path_grow(svg_path, 1)) break;
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[1];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_LINE_TO;
+           *cur_x = pts[0];
+           *cur_y = pts[1];
+           break;
+        }
+      case 'c':
+      case 'C':
+        {
+           if (!_svg_path_grow(svg_path, 3)) break;
+           svg_path->pts[svg_path->pts_cnt-3].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-3].y = pts[1];
+           svg_path->pts[svg_path->pts_cnt-2].x = pts[2];
+           svg_path->pts[svg_path->pts_cnt-2].y = pts[3];
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[4];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[5];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CUBIC_TO;
+           *cur_ctl_x = pts[2];
+           *cur_ctl_y = pts[3];
+           *cur_x = pts[4];
+           *cur_y = pts[5];
+           *is_quadratic = EINA_FALSE;
+           break;
+        }
+      case 's':
+      case 'S':
+        {
+           double ctrl_x, ctrl_y;
+           if ((svg_path->cmds_cnt > 1) && (svg_path->cmds[svg_path->cmds_cnt - 1] ==
+                TVG_PATH_COMMAND_CUBIC_TO) && !(*is_quadratic))
+             {
+                ctrl_x = *cur_x * 2 - *cur_ctl_x;
+                ctrl_y = *cur_y * 2 - *cur_ctl_y;
+             }
+           else
+             {
+                ctrl_x = *cur_x;
+                ctrl_y = *cur_y;
+             }
+           if (!_svg_path_grow(svg_path, 3)) break;
+           svg_path->pts[svg_path->pts_cnt-3].x = ctrl_x;
+           svg_path->pts[svg_path->pts_cnt-3].y = ctrl_y;
+           svg_path->pts[svg_path->pts_cnt-2].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-2].y = pts[1];
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[2];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[3];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CUBIC_TO;
+           *cur_ctl_x = pts[0];
+           *cur_ctl_y = pts[1];
+           *cur_x = pts[2];
+           *cur_y = pts[3];
+           *is_quadratic = EINA_FALSE;
+           break;
+        }
+      case 'q':
+      case 'Q':
+        {
+           float ctrl_x0 = (*cur_x + 2 * pts[0]) * (1.0 / 3.0);
+           float ctrl_y0 = (*cur_y + 2 * pts[1]) * (1.0 / 3.0);
+           float ctrl_x1 = (pts[2] + 2 * pts[0]) * (1.0 / 3.0);
+           float ctrl_y1 = (pts[3] + 2 * pts[1]) * (1.0 / 3.0);
+           if (!_svg_path_grow(svg_path, 3)) break;
+           svg_path->pts[svg_path->pts_cnt-3].x = ctrl_x0;
+           svg_path->pts[svg_path->pts_cnt-3].y = ctrl_y0;
+           svg_path->pts[svg_path->pts_cnt-2].x = ctrl_x1;
+           svg_path->pts[svg_path->pts_cnt-2].y = ctrl_y1;
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[2];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[3];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CUBIC_TO;
+           *cur_ctl_x = pts[0];
+           *cur_ctl_y = pts[1];
+           *cur_x = pts[2];
+           *cur_y = pts[3];
+           *is_quadratic = EINA_TRUE;
+           break;
+        }
+      case 't':
+      case 'T':
+       {
+           double ctrl_x, ctrl_y, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1;
+           if ((svg_path->cmds_cnt > 1) && (svg_path->cmds[svg_path->cmds_cnt - 1] ==
+                TVG_PATH_COMMAND_CUBIC_TO) && *is_quadratic)
+             {
+                ctrl_x = *cur_x * 2 - *cur_ctl_x;
+                ctrl_y = *cur_y * 2 - *cur_ctl_y;
+             }
+           else
+             {
+                ctrl_x = *cur_x;
+                ctrl_y = *cur_y;
+             }
+           ctrl_x0 = (*cur_x + 2 * ctrl_x) * (1.0 / 3.0);
+           ctrl_y0 = (*cur_y + 2 * ctrl_y) * (1.0 / 3.0);
+           ctrl_x1 = (pts[0] + 2 * ctrl_x) * (1.0 / 3.0);
+           ctrl_y1 = (pts[1] + 2 * ctrl_y) * (1.0 / 3.0);
+           if (!_svg_path_grow(svg_path, 3)) break;
+           svg_path->pts[svg_path->pts_cnt-3].x = ctrl_x0;
+           svg_path->pts[svg_path->pts_cnt-3].y = ctrl_y0;
+           svg_path->pts[svg_path->pts_cnt-2].x = ctrl_x1;
+           svg_path->pts[svg_path->pts_cnt-2].y = ctrl_y1;
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[1];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CUBIC_TO;
+           *cur_ctl_x = ctrl_x;
+           *cur_ctl_y = ctrl_y;
+           *cur_x = pts[0];
+           *cur_y = pts[1];
+           *is_quadratic = EINA_TRUE;
+           break;
+        }
+      case 'h':
+      case 'H':
+        {
+           if (!_svg_path_grow(svg_path, 1)) break;
+           svg_path->pts[svg_path->pts_cnt-1].x = pts[0];
+           svg_path->pts[svg_path->pts_cnt-1].y = *cur_y;
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_LINE_TO;
+           *cur_x = pts[0];
+           break;
+        }
+      case 'v':
+      case 'V':
+        {
+           if (!_svg_path_grow(svg_path, 1)) break;
+           svg_path->pts[svg_path->pts_cnt-1].x = *cur_x;
+           svg_path->pts[svg_path->pts_cnt-1].y = pts[0];
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_LINE_TO;
+           *cur_y = pts[0];
+           break;
+        }
+      case 'a':
+      case 'A':
+        {
+           Arc_To_Init_Variables var;
+           double x, y, rx, ry;
+           double sx, sy, ex, ey;
+           double c1x, c1y, c2x, c2y;
+           double theta2;
+           double cos_theta2, sin_theta2;
+           int i;
+
+           x = pts[5];
+           y = pts[6];
+           rx = pts[0];
+           ry = pts[1];
+           sx = *cur_x;
+           sy = *cur_y;
+
+           // if start and end points are identical, then no arc is drawn
+           if ((fabs(x - sx) < (1 / 256.0)) && (fabs(y - sy) < (1 / 256.0)))
+             break;
+           rx = fabs(rx);
+           ry = fabs(ry);
+           if ((rx < 0.5) || (ry < 0.5))
+             {
+                _svg_path_grow(svg_path, 1);
+                svg_path->pts[svg_path->pts_cnt-1].x = x;
+                svg_path->pts[svg_path->pts_cnt-1].y = y;
+                svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_LINE_TO;
+                *cur_x = x;
+                *cur_y = y;
+                break;
+             }
+
+           _arc_to_variables_initialization(x, y, rx, ry, pts[2], pts[3], pts[4],
+                                            sx, sy, &var);
+
+           for (i = 0; i < var.segments; ++i)
+             {
+                // end angle (for this segment) = current + delta
+                theta2 = var.theta1 + var.delta;
+                cos_theta2 = cos(theta2);
+                sin_theta2 = sin(theta2);
+
+                // first control point (based on start point sx,sy)
+                c1x = sx - var.bcp * (var.cos_phi_rx * var.sin_theta1 +
+                                      var.sin_phi_ry * var.cos_theta1);
+                c1y = sy + var.bcp * (var.cos_phi_ry * var.cos_theta1 -
+                                      var.sin_phi_rx * var.sin_theta1);
+
+                // end point (for this segment)
+                ex = var.cx + (var.cos_phi_rx * cos_theta2 -
+                               var.sin_phi_ry * sin_theta2);
+                ey = var.cy + (var.sin_phi_rx * cos_theta2 +
+                               var.cos_phi_ry * sin_theta2);
+
+                // second control point (based on end point ex,ey)
+                c2x = ex + var.bcp * (var.cos_phi_rx * sin_theta2 +
+                                      var.sin_phi_ry * cos_theta2);
+                c2y = ey + var.bcp * (var.sin_phi_rx * sin_theta2 -
+                                      var.cos_phi_ry * cos_theta2);
+
+                _svg_path_grow(svg_path, 3);
+                svg_path->pts[svg_path->pts_cnt-3].x = c1x;
+                svg_path->pts[svg_path->pts_cnt-3].y = c1y;
+                svg_path->pts[svg_path->pts_cnt-2].x = c2x;
+                svg_path->pts[svg_path->pts_cnt-2].y = c2y;
+                svg_path->pts[svg_path->pts_cnt-1].x = ex;
+                svg_path->pts[svg_path->pts_cnt-1].y = ey;
+                svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CUBIC_TO;
+
+                // next start point is the current end point (same for angle)
+                sx = ex;
+                sy = ey;
+                var.theta1 = theta2;
+                // avoid recomputations
+                var.cos_theta1 = cos_theta2;
+                var.sin_theta1 = sin_theta2;
+             }
+           *cur_ctl_x = ex;
+           *cur_ctl_y = ey;
+           *cur_x = ex;
+           *cur_y = ey;
+           *is_quadratic = EINA_FALSE;
+           break;
+        }
+      case 'z':
+      case 'Z':
+        {
+           if (!_svg_path_grow(svg_path, 0)) break;
+           svg_path->cmds[svg_path->cmds_cnt-1] = TVG_PATH_COMMAND_CLOSE;
+           break;
+        }
+      default:
+         break;
+     }
+}
+#endif
index b62f1a5..317751e 100644 (file)
@@ -1,11 +1,10 @@
 #ifndef EVAS_VG_PRIVATE_H_
 # define EVAS_VG_PRIVATE_H_
 
-//TODO: Parametrize this include
-#include <Ector.h>
-
 #ifdef HAVE_THORVG
 #include <thorvg_capi.h>
+#else
+#include <Ector.h>
 #endif
 
 typedef struct _Efl_Canvas_Vg_Node_Data             Efl_Canvas_Vg_Node_Data;
@@ -64,7 +63,9 @@ struct _Efl_Canvas_Vg_Object_Data
 
 #ifdef HAVE_THORVG
    Tvg_Canvas *tvg_canvas;
+   Eina_Size2D tvg_canvas_size;
    uint32_t   *tvg_buffer;
+   RGBA_Image *im;
 #endif
 };
 
@@ -73,7 +74,9 @@ struct _Efl_Canvas_Vg_Node_Data
    Eina_Matrix3 *m;
    Efl_Canvas_Vg_Interpolation *intp;
 
+#ifndef HAVE_THORVG
    Ector_Renderer *renderer;
+#endif
 
    Efl_VG *vg_obj;
    Efl_Canvas_Vg_Object_Data *vd;
@@ -86,7 +89,6 @@ struct _Efl_Canvas_Vg_Node_Data
 #ifdef HAVE_THORVG
    void (*render_pre_tvg)(Evas_Object_Protected_Data *vg_pd, Efl_VG *node, Efl_Canvas_Vg_Node_Data *nd, void *canvas);
    void (*render_tvg)(Evas_Object_Protected_Data *vg_pd, Efl_VG *node, Efl_Canvas_Vg_Node_Data *nd, void *canvas);
-   void (*set_color)(Efl_Canvas_Vg_Node_Data *nd, int r, int g, int b, int a);
 #endif
 
    void *data;
@@ -137,6 +139,11 @@ struct _Efl_Canvas_Vg_Gradient_Data
    unsigned int colors_count;
 
    Efl_Gfx_Gradient_Spread spread;
+#ifdef HAVE_THORVG
+   Tvg_Gradient *gradient;
+   Evas_Vg_Shape *shape;    // we need handle to shape to call node_change with it
+   void (*gradient_render_pre_tvg)(Efl_Canvas_Vg_Node *nd, Efl_Canvas_Vg_Gradient_Data *gd, Tvg_Paint *shape);
+#endif
 };
 
 struct _Efl_Canvas_Vg_Interpolation
@@ -194,10 +201,6 @@ _evas_vg_render_pre(Evas_Object_Protected_Data *vg_pd, Efl_VG *child,
    if (nd) nd->render_pre(vg_pd, child, nd,
                           engine, output, context, surface,
                           transform, opacity, comp, comp_method, nd->data);
-#ifdef HAVE_THORVG
-   //TODO: This function probably is not called when HAVE_THORVG is defined
-   if (nd && nd->render_pre_tvg) nd->render_pre_tvg(vg_pd, child, nd, NULL);
-#endif
    return nd;
 }