From 295ca131a4dd975a1d190da50f5fb73768db1a15 Mon Sep 17 00:00:00 2001 From: Michal Szczecinski Date: Fri, 4 Sep 2020 13:28:18 +0200 Subject: [PATCH] evas/canvas/vg: Added integration with thorvg. 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 | 17 +- src/lib/evas/canvas/efl_canvas_vg_gradient.c | 96 ++- .../evas/canvas/efl_canvas_vg_gradient_linear.c | 74 +- .../evas/canvas/efl_canvas_vg_gradient_radial.c | 67 +- src/lib/evas/canvas/efl_canvas_vg_image.c | 17 +- src/lib/evas/canvas/efl_canvas_vg_node.c | 11 +- src/lib/evas/canvas/efl_canvas_vg_object.c | 169 +++- src/lib/evas/canvas/efl_canvas_vg_shape.c | 957 ++++++++++++++++++++- src/lib/evas/canvas/evas_tvg_path_helpers.h | 619 +++++++++++++ src/lib/evas/canvas/evas_vg_private.h | 19 +- 10 files changed, 2008 insertions(+), 38 deletions(-) create mode 100644 src/lib/evas/canvas/evas_tvg_path_helpers.h diff --git a/src/lib/evas/canvas/efl_canvas_vg_container.c b/src/lib/evas/canvas/efl_canvas_vg_container.c index 7dea694..c4d2c55 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_container.c +++ b/src/lib/evas/canvas/efl_canvas_vg_container.c @@ -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 * diff --git a/src/lib/evas/canvas/efl_canvas_vg_gradient.c b/src/lib/evas/canvas/efl_canvas_vg_gradient.c index 940cc64..214ed96 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_gradient.c +++ b/src/lib/evas/canvas/efl_canvas_vg_gradient.c @@ -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" diff --git a/src/lib/evas/canvas/efl_canvas_vg_gradient_linear.c b/src/lib/evas/canvas/efl_canvas_vg_gradient_linear.c index bd8cceb..750e7b7 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_gradient_linear.c +++ b/src/lib/evas/canvas/efl_canvas_vg_gradient_linear.c @@ -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 * diff --git a/src/lib/evas/canvas/efl_canvas_vg_gradient_radial.c b/src/lib/evas/canvas/efl_canvas_vg_gradient_radial.c index 2634286..f423f50 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_gradient_radial.c +++ b/src/lib/evas/canvas/efl_canvas_vg_gradient_radial.c @@ -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 diff --git a/src/lib/evas/canvas/efl_canvas_vg_image.c b/src/lib/evas/canvas/efl_canvas_vg_image.c index eb5c306..bc95ad2 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_image.c +++ b/src/lib/evas/canvas/efl_canvas_vg_image.c @@ -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; } diff --git a/src/lib/evas/canvas/efl_canvas_vg_node.c b/src/lib/evas/canvas/efl_canvas_vg_node.c index f2bead5..68e8f4c 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_node.c +++ b/src/lib/evas/canvas/efl_canvas_vg_node.c @@ -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) diff --git a/src/lib/evas/canvas/efl_canvas_vg_object.c b/src/lib/evas/canvas/efl_canvas_vg_object.c index 0b246450..2897ac4 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_object.c +++ b/src/lib/evas/canvas/efl_canvas_vg_object.c @@ -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) diff --git a/src/lib/evas/canvas/efl_canvas_vg_shape.c b/src/lib/evas/canvas/efl_canvas_vg_shape.c index ce83370..66bfd02 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_shape.c +++ b/src/lib/evas/canvas/efl_canvas_vg_shape.c @@ -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 index 0000000..36535dc --- /dev/null +++ b/src/lib/evas/canvas/evas_tvg_path_helpers.h @@ -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 diff --git a/src/lib/evas/canvas/evas_vg_private.h b/src/lib/evas/canvas/evas_vg_private.h index b62f1a5..317751e 100644 --- a/src/lib/evas/canvas/evas_vg_private.h +++ b/src/lib/evas/canvas/evas_vg_private.h @@ -1,11 +1,10 @@ #ifndef EVAS_VG_PRIVATE_H_ # define EVAS_VG_PRIVATE_H_ -//TODO: Parametrize this include -#include - #ifdef HAVE_THORVG #include +#else +#include #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; } -- 2.7.4