From a6bb9688f98b336f936c6019d7bf74a9be658135 Mon Sep 17 00:00:00 2001 From: Subhransu Mohanty Date: Fri, 22 Jul 2016 15:55:13 +0900 Subject: [PATCH] svg : backported svg support from opensource. Change-Id: I8f2abb2d4cecf504537c230f5d7991d98649be4f --- src/Makefile_Edje.am | 5 +- src/Makefile_Evas.am | 8 + src/bin/edje/edje_cc.h | 2 + src/bin/edje/edje_cc_handlers.c | 119 ++ src/bin/edje/edje_cc_out.c | 59 + src/bin/edje/edje_convert.c | 3 + src/bin/edje/edje_inspector.c | 2 + src/bin/edje/edje_svg_loader.c | 2026 ++++++++++++++++++++ src/lib/edje/Edje_Common.h | 3 +- src/lib/edje/edje_cache.c | 2 + src/lib/edje/edje_calc.c | 34 + src/lib/edje/edje_convert.c | 3 + src/lib/edje/edje_data.c | 29 +- src/lib/edje/edje_edit.c | 7 + src/lib/edje/edje_load.c | 14 +- src/lib/edje/edje_lua.c | 1 + src/lib/edje/edje_private.h | 44 +- src/lib/evas/Evas_Legacy.h | 2 + src/lib/evas/canvas/evas_main.c | 4 + src/lib/evas/canvas/evas_object_vg.c | 109 +- src/lib/evas/include/evas_private.h | 11 + src/lib/evas/vg/evas_vg_cache.c | 146 ++ src/lib/evas/vg/evas_vg_cache.h | 37 + src/lib/evas/vg/evas_vg_common.h | 317 +++ src/lib/evas/vg/evas_vg_eet_handler.c | 789 ++++++++ src/modules/evas/engines/gl_generic/evas_engine.c | 47 + .../evas/engines/software_generic/evas_engine.c | 72 +- 27 files changed, 3881 insertions(+), 14 deletions(-) create mode 100644 src/bin/edje/edje_svg_loader.c create mode 100644 src/lib/evas/vg/evas_vg_cache.c create mode 100644 src/lib/evas/vg/evas_vg_cache.h create mode 100644 src/lib/evas/vg/evas_vg_common.h create mode 100644 src/lib/evas/vg/evas_vg_eet_handler.c diff --git a/src/Makefile_Edje.am b/src/Makefile_Edje.am index 1e5059f..1eabd14 100644 --- a/src/Makefile_Edje.am +++ b/src/Makefile_Edje.am @@ -151,8 +151,9 @@ bin/edje/edje_cc_parse.c \ bin/edje/edje_cc_mem.c \ bin/edje/edje_cc_handlers.c \ bin/edje/edje_cc_sources.c \ -bin/edje/edje_multisense_convert.c -bin_edje_edje_cc_CPPFLAGS = -I$(top_builddir)/src/lib/efl $(EDJE_COMMON_CPPFLAGS) +bin/edje/edje_multisense_convert.c \ +bin/edje/edje_svg_loader.c +bin_edje_edje_cc_CPPFLAGS = -I$(top_builddir)/src/lib/efl -I$(top_srcdir)/src/lib/evas/vg $(EDJE_COMMON_CPPFLAGS) bin_edje_edje_cc_LDADD = $(USE_EDJE_BIN_LIBS) bin_edje_edje_cc_DEPENDENCIES = \ @USE_EDJE_INTERNAL_LIBS@ \ diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 4e605fb..45115bc 100755 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -173,6 +173,13 @@ lib/evas/canvas/render2/evas_render2_th_main.c \ lib/evas/canvas/render2/region.c \ lib/evas/canvas/render2/region.h +# Vg +lib_evas_libevas_la_SOURCES += \ +lib/evas/vg/evas_vg_eet_handler.c \ +lib/evas/vg/evas_vg_cache.c \ +lib/evas/vg/evas_vg_cache.h \ +lib/evas/vg/evas_vg_common.h + # Cache lib_evas_libevas_la_SOURCES += \ lib/evas/cache/evas_cache_image.c \ @@ -350,6 +357,7 @@ lib_evas_libevas_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -I$(top_srcdir)/src/lib/evas/include \ -I$(top_srcdir)/src/static_libs/libunibreak \ -I$(top_builddir)/src/lib/evas/canvas \ +-I$(top_builddir)/src/lib/evas/vg \ -I$(top_builddir)/src/modules/evas/engines/software_generic \ -I$(top_builddir)/src/modules/evas/engines/gl_generic \ -DPACKAGE_BIN_DIR=\"$(bindir)\" \ diff --git a/src/bin/edje/edje_cc.h b/src/bin/edje/edje_cc.h index cadc37e..eae4685 100644 --- a/src/bin/edje/edje_cc.h +++ b/src/bin/edje/edje_cc.h @@ -250,6 +250,8 @@ void edje_cc_handlers_hierarchy_free(void); void edje_cc_handlers_pop_notify(const char *token); int get_param_index(char *str); +struct _Svg_Node* _svg_load(Eina_File *f, const char *key EINA_UNUSED); + /* global vars */ extern Eina_List *ext_dirs; extern Eina_List *img_dirs; diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c index 6171396..e5cb7eb 100644 --- a/src/bin/edje/edje_cc_handlers.c +++ b/src/bin/edje/edje_cc_handlers.c @@ -512,6 +512,9 @@ static void st_collections_group_nomouse(void); static void st_collections_group_broadcast(void); static void st_collections_group_nobroadcast(void); +static void st_images_vector(void); +static void _handle_vector_image(void); + // TIZEN_ONLY(20150110): Add plugin keyword. #ifdef PLUGIN static void ob_plugins_plug(void); @@ -525,6 +528,7 @@ static void st_collections_plugins_plugin_param(void); #define IMAGE_STATEMENTS(PREFIX) \ {PREFIX"images.image", st_images_image}, \ + {PREFIX"images.vector", st_images_vector}, \ {PREFIX"images.set.name", st_images_set_name}, \ {PREFIX"images.set.image.image", st_images_set_image_image}, \ {PREFIX"images.set.image.size", st_images_set_image_size}, \ @@ -1311,6 +1315,7 @@ New_Object_Handler object_handlers[] = proxy{} spacer{} snapshot{} + vector{} part { desc { } @@ -1338,6 +1343,7 @@ New_Object_Handler object_handlers_short[] = {"collections.group.parts.proxy", ob_collections_group_parts_part_short}, {"collections.group.parts.spacer", ob_collections_group_parts_part_short}, {"collections.group.parts.part.desc", ob_collections_group_parts_part_desc}, + {"collections.group.parts.vector", ob_collections_group_parts_part_short}, }; New_Nested_Handler nested_handlers[] = { @@ -1356,6 +1362,7 @@ New_Nested_Handler nested_handlers_short[] = { {"collections.group.parts", "external", NULL, edje_cc_handlers_hierarchy_pop }, {"collections.group.parts", "proxy", NULL, edje_cc_handlers_hierarchy_pop }, {"collections.group.parts", "spacer", NULL, edje_cc_handlers_hierarchy_pop }, + {"collections.group.parts", "vector", NULL, edje_cc_handlers_hierarchy_pop }, }; /*****/ @@ -1562,6 +1569,15 @@ _edje_part_description_alloc(unsigned char type, const char *collection, const c result = &ed->common; break; } + case EDJE_PART_TYPE_VECTOR: + { + Edje_Part_Description_Vector *ed; + + ed = mem_alloc(SZ(Edje_Part_Description_Vector)); + + result = &ed->common; + break; + } } if (!result) @@ -1932,6 +1948,96 @@ st_images_image(void) } } +static void +_handle_vector_image(void) +{ + Edje_Part_Description_Vector *ed; + unsigned int i = 0; + char *name; + + ed = (Edje_Part_Description_Vector*) current_desc; + + name = parse_str(0); + + ed->vg.id = -1; + + for (i = 0; i < edje_file->image_dir->vectors_count; ++i) + { + if (!strcmp(edje_file->image_dir->vectors[i].entry, name)) + { + ed->vg.set = EINA_TRUE; + ed->vg.id = edje_file->image_dir->vectors[i].id; + break; + } + } + free(name); +} + +/** @edcsubsection{toplevel_images, + * Images} */ + +/** + @page edcref + + @block + images + @context + vector { + vector: "filename1.svg"; + vector: "filename2.svg"; + vector: "filename3.svg"; + .. + } + @description + The "vector" context in the "images" block is used to list each svg image file that will be used in + the theme. + @endblock + + @property + vector + @parameters + [image file] + @endproperty + */ +static void +st_images_vector(void) +{ + Edje_Vector_Directory_Entry *vector; + const char *tmp; + unsigned int i; + + check_min_arg_count(1); + + if (!edje_file->image_dir) + edje_file->image_dir = mem_alloc(SZ(Edje_Image_Directory)); + + tmp = parse_str(0); + + for (i = 0; i < edje_file->image_dir->vectors_count; ++i) + if (!strcmp(edje_file->image_dir->vectors[i].entry, tmp)) + { + free((char*) tmp); + return; + } + + edje_file->image_dir->vectors_count++; + vector = realloc(edje_file->image_dir->vectors, + sizeof (Edje_Vector_Directory_Entry) * edje_file->image_dir->vectors_count); + if (!vector) + { + ERR("No enough memory."); + exit(-1); + } + edje_file->image_dir->vectors = vector; + memset(edje_file->image_dir->vectors + edje_file->image_dir->vectors_count - 1, + 0, sizeof (Edje_Vector_Directory_Entry)); + + vector = edje_file->image_dir->vectors + edje_file->image_dir->vectors_count - 1; + + vector->entry = tmp; + vector->id = edje_file->image_dir->vectors_count; +} + /** @edcsubsection{toplevel_models,model} */ @@ -5021,6 +5127,7 @@ ob_collections_group_parts_part_short(void) "proxy", EDJE_PART_TYPE_PROXY, "spacer", EDJE_PART_TYPE_SPACER, "snapshot", EDJE_PART_TYPE_SNAPSHOT, + "vector", EDJE_PART_TYPE_VECTOR, NULL); stack_pop_quick(EINA_TRUE, EINA_TRUE); @@ -5453,6 +5560,7 @@ st_collections_group_parts_part_type(void) "PROXY", EDJE_PART_TYPE_PROXY, "SPACER", EDJE_PART_TYPE_SPACER, "SNAPSHOT", EDJE_PART_TYPE_SNAPSHOT, + "VECTOR", EDJE_PART_TYPE_VECTOR, NULL); _part_type_set(type); @@ -7323,6 +7431,11 @@ st_collections_group_parts_part_description_inherit(void) } break; } + case EDJE_PART_TYPE_VECTOR: + { + // TODO + break; + } } #undef STRDUP @@ -8242,6 +8355,7 @@ st_collections_group_parts_part_description_rel2_to_y(void) .. image { normal: "filename.ext"; + normal: "filename.svg"; tween: "filename2.ext"; .. tween: "filenameN.ext"; @@ -8271,6 +8385,11 @@ st_collections_group_parts_part_description_image_normal(void) check_arg_count(1); + if (current_part->type == EDJE_PART_TYPE_VECTOR) + { + return _handle_vector_image(); + } + if (current_part->type != EDJE_PART_TYPE_IMAGE) { ERR("parse error %s:%i. " diff --git a/src/bin/edje/edje_cc_out.c b/src/bin/edje/edje_cc_out.c index 1282c62..6c728e8 100644 --- a/src/bin/edje/edje_cc_out.c +++ b/src/bin/edje/edje_cc_out.c @@ -21,6 +21,7 @@ #include "edje_cc.h" #include "edje_convert.h" #include "edje_multisense_convert.h" +#include "evas_vg_common.h" #include #include @@ -168,6 +169,13 @@ struct _Sound_Write int i; }; +struct _Vector_Write +{ + Eet_File *ef; + Svg_Node *root; + int i; +}; + struct _Mo_Write { Eet_File *ef; @@ -1126,6 +1134,54 @@ data_image_preload_done(void *data, Evas *e EINA_UNUSED, Evas_Object *o, void *e } static void +data_write_vectors(Eet_File *ef, int *vector_num) +{ + unsigned int i; + Svg_Node *root; + Eet_Data_Descriptor *svg_node_eet; + Eina_List *ll; + char *s; + Eina_File *f = NULL; + Edje_Vector_Directory_Entry *vector; + Eina_Strbuf *buf; + Eina_Bool found = EINA_FALSE; + + if (!((edje_file) && (edje_file->image_dir))) return; + + svg_node_eet = _evas_vg_svg_node_eet(); + buf = eina_strbuf_new(); + for (i = 0; i < edje_file->image_dir->vectors_count; i++) + { + + vector = &edje_file->image_dir->vectors[i]; + EINA_LIST_FOREACH(img_dirs, ll, s) + { + eina_strbuf_reset(buf); + eina_strbuf_append_printf(buf, "%s/%s", s, vector->entry); + f = eina_file_open(eina_strbuf_string_get(buf), EINA_FALSE); + if (!f) continue; + root = _svg_load(f, NULL); + if(!root) + error_and_abort(ef, "Failed to parse svg : %s", vector->entry); + eina_strbuf_reset(buf); + eina_strbuf_append_printf(buf, "edje/vectors/%i", vector->id); + if(!eet_data_write(ef, svg_node_eet, eina_strbuf_string_get(buf), root, compress_mode)) + error_and_abort(ef, "Failed to write data in Eet for svg :%s", vector->entry); + *vector_num += 1; + eina_file_close(f); + found = EINA_TRUE; + _evas_vg_svg_node_free(root); + break; + } + if (!found) + error_and_abort(ef, "Unable to find the svg :%s", vector->entry); + found = EINA_FALSE; + } + eina_strbuf_free(buf); + _evas_vg_svg_node_eet_destroy(); +} + +static void data_write_images(Eet_File *ef, int *image_num) { int i; @@ -2334,6 +2390,7 @@ data_write(void) int vibration_num = 0; int font_num = 0; int collection_num = 0; + int vector_num = 0; double t; if (!edje_file) @@ -2388,6 +2445,8 @@ data_write(void) INF("fontmap: %3.5f", ecore_time_get() - t); t = ecore_time_get(); data_write_images(ef, &image_num); INF("images: %3.5f", ecore_time_get() - t); t = ecore_time_get(); + data_write_vectors(ef, &vector_num); + INF("vectors: %3.5f", ecore_time_get() - t); t = ecore_time_get(); data_write_fonts(ef, &font_num); INF("fonts: %3.5f", ecore_time_get() - t); t = ecore_time_get(); data_write_sounds(ef, &sound_num); diff --git a/src/bin/edje/edje_convert.c b/src/bin/edje/edje_convert.c index 25afe10..1aff944 100644 --- a/src/bin/edje/edje_convert.c +++ b/src/bin/edje/edje_convert.c @@ -232,6 +232,7 @@ _edje_collection_convert(Eet_File *ef, Edje_Part_Collection_Directory_Entry *ce, CSP(BOX, ce); CSP(TABLE, ce); CSP(EXTERNAL, ce); + CSP(VECTOR, ce); default: count = &dummy; break; @@ -256,6 +257,7 @@ _edje_collection_convert(Eet_File *ef, Edje_Part_Collection_Directory_Entry *ce, CONVERT_EMN(TABLE, Edje_Part_Description_Table, ce); CONVERT_EMN(EXTERNAL, Edje_Part_Description_External, ce); CONVERT_EMN(part, Edje_Part, ce); + CONVERT_EMN(VECTOR, Edje_Part_Description_Vector, ce); /* Change structure layout */ edc = calloc(1, sizeof (Edje_Part_Collection)); @@ -449,6 +451,7 @@ _edje_description_convert(int type, CONVERT_ALLOC_POOL(BOX, Box, box); CONVERT_ALLOC_POOL(TABLE, Table, table); CONVERT_ALLOC_POOL(EXTERNAL, External, external_params); + CONVERT_ALLOC_POOL(VECTOR, Vector, vector); } if (result) diff --git a/src/bin/edje/edje_inspector.c b/src/bin/edje/edje_inspector.c index c6f8bd3..b071034 100644 --- a/src/bin/edje/edje_inspector.c +++ b/src/bin/edje/edje_inspector.c @@ -208,6 +208,8 @@ part_type_name_get(Edje_Part_Type t) return "EXTERNAL"; case EDJE_PART_TYPE_SPACER: return "SPACER"; + case EDJE_PART_TYPE_VECTOR: + return "VECTOR"; case EDJE_PART_TYPE_NONE: case EDJE_PART_TYPE_LAST: diff --git a/src/bin/edje/edje_svg_loader.c b/src/bin/edje/edje_svg_loader.c new file mode 100644 index 0000000..a6377f6 --- /dev/null +++ b/src/bin/edje/edje_svg_loader.c @@ -0,0 +1,2026 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include "evas_vg_common.h" + +#ifndef A_VAL +#ifndef WORDS_BIGENDIAN +/* x86 */ +#define A_VAL(p) (((uint8_t *)(p))[3]) +#define R_VAL(p) (((uint8_t *)(p))[2]) +#define G_VAL(p) (((uint8_t *)(p))[1]) +#define B_VAL(p) (((uint8_t *)(p))[0]) +#define AR_VAL(p) ((uint16_t *)(p)[1]) +#define GB_VAL(p) ((uint16_t *)(p)[0]) +#else +/* ppc */ +#define A_VAL(p) (((uint8_t *)(p))[0]) +#define R_VAL(p) (((uint8_t *)(p))[1]) +#define G_VAL(p) (((uint8_t *)(p))[2]) +#define B_VAL(p) (((uint8_t *)(p))[3]) +#define AR_VAL(p) ((uint16_t *)(p)[0]) +#define GB_VAL(p) ((uint16_t *)(p)[1]) +#endif +#endif + +#define ARGB_JOIN(a,r,g,b) \ + (((a) << 24) + ((r) << 16) + ((g) << 8) + (b)) + + +typedef Svg_Node *(*Factory_Method)(Svg_Node *parent, const char *buf, unsigned buflen); + +typedef Svg_Style_Gradient *(*Gradient_Factory_Method)(const char *buf, unsigned buflen); + +typedef struct _Evas_SVG_Loader Evas_SVG_Loader; +struct _Evas_SVG_Loader +{ + Eina_Array *stack; + Svg_Node *doc; + Svg_Node *def; + Svg_Style_Gradient *gradient; + int level; + Eina_Bool result:1; +}; + +char * +_skip_space(const char *str, const char *end) +{ + while (((end != NULL && str < end) || (end == NULL && *str != '\0')) && isspace(*str)) + ++str; + return (char *)str; +} + +static inline Eina_Stringshare * +_copy_id(const char* str) +{ + if (str == NULL) return NULL; + + return eina_stringshare_add(str); +} + +static const char * +_skipcomma(const char *content) +{ + content = _skip_space(content, NULL); + if (*content == ',') return content + 1; + return content; +} + +static inline Eina_Bool +_parse_number(const char **content, double *number) +{ + char *end = NULL; + + *number = strtod(*content, &end); + // if the start of string is not number + if ((*content) == end) return EINA_FALSE; + //skip comma if any + *content = _skipcomma(end); + return EINA_TRUE; +} + +static inline double +_to_double(const char *str) +{ + char *end = NULL; + + return strtod(str, &end); +} + +static inline int +_to_opacity(const char *str) +{ + char *end = NULL; + int a = 0; + double opacity = strtod(str, &end); + + if (*end == '\0') + a = lrint(opacity * 255); + return a; +} + +#define _PARSE_TAG(Type, Short_Name, Tags_Array, Default) \ + static Type _to_##Short_Name(const char *str) \ + { \ + unsigned int i; \ + \ + for (i = 0; i < sizeof (Tags_Array) / sizeof (Tags_Array[0]); i++) \ + if (!strcmp(str, Tags_Array[i].tag)) \ + return Tags_Array[i].Short_Name; \ + return Default; \ + } +/* parse the line cap used during stroking a path. + * Value: butt | round | square | inherit + * Initial: butt + * https://www.w3.org/TR/SVG/painting.html + */ +static struct { + Efl_Gfx_Cap line_cap; + const char *tag; +} line_cap_tags[] = { + { EFL_GFX_CAP_BUTT, "butt" }, + { EFL_GFX_CAP_ROUND, "round" }, + { EFL_GFX_CAP_SQUARE, "square" } +}; + +_PARSE_TAG(Efl_Gfx_Cap, line_cap, line_cap_tags, EFL_GFX_CAP_LAST); + +/* parse the line join used during stroking a path. + * Value: miter | round | bevel | inherit + * Initial: miter + * https://www.w3.org/TR/SVG/painting.html + */ +static struct { + Efl_Gfx_Join line_join; + const char *tag; +} line_join_tags[] = { + { EFL_GFX_JOIN_MITER, "miter" }, + { EFL_GFX_JOIN_ROUND, "round" }, + { EFL_GFX_JOIN_BEVEL, "bevel" } +}; + +_PARSE_TAG(Efl_Gfx_Join, line_join, line_join_tags, EFL_GFX_JOIN_LAST); + +/* parse the fill rule used during filling a path. + * Value: nonzero | evenodd | inherit + * Initial: nonzero + * https://www.w3.org/TR/SVG/painting.html + */ + +static struct { + Efl_Gfx_Fill_Rule fill_rule; + const char *tag; +} fill_rule_tags[] = { + { EFL_GFX_FILL_RULE_ODD_EVEN, "evenodd" } +}; + +_PARSE_TAG(Efl_Gfx_Fill_Rule, fill_rule, fill_rule_tags, EFL_GFX_FILL_RULE_WINDING); + +#if 0 +// unused at the moment +/* parse the dash pattern used during stroking a path. + * Value: none | | inherit + * Initial: none + * https://www.w3.org/TR/SVG/painting.html + */ +static inline void +_parse_dash_array(const char *str, Efl_Gfx_Dash** dash, int *length) +{ + double tmp[30]; + char *end = NULL; + int leni, gapi, count = 0, index = 0; + + while (*str) + { + // skip white space, comma + str = _skipcomma(str); + tmp[count++] = strtod(str, &end); + str = _skipcomma(end); + } + + if (count & 0x1) + { // odd case. + *length = count; + *dash = calloc(*length, sizeof(Efl_Gfx_Dash)); + while (index < count) + { + leni = (2 * index) % count; + gapi = (2 * index + 1) % count; + (*dash)[index].length = tmp[leni]; + (*dash)[index].gap = tmp[gapi]; + } + } + else + { // even case + *length = count/2; + *dash = calloc(*length, sizeof(Efl_Gfx_Dash)); + while (index < count) + { + (*dash)[index].length = tmp[2 * index]; + (*dash)[index].gap = tmp[2 * index + 1]; + } + } +} +#endif + +static Eina_Stringshare * + _id_from_url(const char *url) +{ + char tmp[50]; + int i = 0; + + url = _skip_space(url, NULL); + if ((*url) == '(') + ++url; + url = _skip_space(url, NULL); + if ((*url) == '#') + ++url; + + while ((*url) != ')') + { + tmp[i++] = *url; + ++url; + } + tmp[i] = '\0'; + + return eina_stringshare_add(tmp); +} + +static unsigned char +_color_parser(const char *value, char **end) +{ + double r; + + r = strtod(value + 4, end); + *end = _skip_space(*end, NULL); + if (**end == '%') + r = 255 * r / 100; + *end = _skip_space(*end, NULL); + + if (r < 0 || r > 255) + { + *end = NULL; + return 0; + } + + return lrint(r); +} + +static const struct { + const char *name; + unsigned int value; +} colors[] = { + { "aliceblue", 0xfff0f8ff }, + { "antiquewhite", 0xfffaebd7 }, + { "aqua", 0xff00ffff }, + { "aquamarine", 0xff7fffd4 }, + { "azure", 0xfff0ffff }, + { "beige", 0xfff5f5dc }, + { "bisque", 0xffffe4c4 }, + { "black", 0xff000000 }, + { "blanchedalmond", 0xffffebcd }, + { "blue", 0xff0000ff }, + { "blueviolet", 0xff8a2be2 }, + { "brown", 0xffa52a2a }, + { "burlywood", 0xffdeb887 }, + { "cadetblue", 0xff5f9ea0 }, + { "chartreuse", 0xff7fff00 }, + { "chocolate", 0xffd2691e }, + { "coral", 0xffff7f50 }, + { "cornflowerblue", 0xff6495ed }, + { "cornsilk", 0xfffff8dc }, + { "crimson", 0xffdc143c }, + { "cyan", 0xff00ffff }, + { "darkblue", 0xff00008b }, + { "darkcyan", 0xff008b8b }, + { "darkgoldenrod", 0xffb8860b }, + { "darkgray", 0xffa9a9a9 }, + { "darkgrey", 0xffa9a9a9 }, + { "darkgreen", 0xff006400 }, + { "darkkhaki", 0xffbdb76b }, + { "darkmagenta", 0xff8b008b }, + { "darkolivegreen", 0xff556b2f }, + { "darkorange", 0xffff8c00 }, + { "darkorchid", 0xff9932cc }, + { "darkred", 0xff8b0000 }, + { "darksalmon", 0xffe9967a }, + { "darkseagreen", 0xff8fbc8f }, + { "darkslateblue", 0xff483d8b }, + { "darkslategray", 0xff2f4f4f }, + { "darkslategrey", 0xff2f4f4f }, + { "darkturquoise", 0xff00ced1 }, + { "darkviolet", 0xff9400d3 }, + { "deeppink", 0xffff1493 }, + { "deepskyblue", 0xff00bfff }, + { "dimgray", 0xff696969 }, + { "dimgrey", 0xff696969 }, + { "dodgerblue", 0xff1e90ff }, + { "firebrick", 0xffb22222 }, + { "floralwhite", 0xfffffaf0 }, + { "forestgreen", 0xff228b22 }, + { "fuchsia", 0xffff00ff }, + { "gainsboro", 0xffdcdcdc }, + { "ghostwhite", 0xfff8f8ff }, + { "gold", 0xffffd700 }, + { "goldenrod", 0xffdaa520 }, + { "gray", 0xff808080 }, + { "grey", 0xff808080 }, + { "green", 0xff008000 }, + { "greenyellow", 0xffadff2f }, + { "honeydew", 0xfff0fff0 }, + { "hotpink", 0xffff69b4 }, + { "indianred", 0xffcd5c5c }, + { "indigo", 0xff4b0082 }, + { "ivory", 0xfffffff0 }, + { "khaki", 0xfff0e68c }, + { "lavender", 0xffe6e6fa }, + { "lavenderblush", 0xfffff0f5 }, + { "lawngreen", 0xff7cfc00 }, + { "lemonchiffon", 0xfffffacd }, + { "lightblue", 0xffadd8e6 }, + { "lightcoral", 0xfff08080 }, + { "lightcyan", 0xffe0ffff }, + { "lightgoldenrodyellow", 0xfffafad2 }, + { "lightgray", 0xffd3d3d3 }, + { "lightgrey", 0xffd3d3d3 }, + { "lightgreen", 0xff90ee90 }, + { "lightpink", 0xffffb6c1 }, + { "lightsalmon", 0xffffa07a }, + { "lightseagreen", 0xff20b2aa }, + { "lightskyblue", 0xff87cefa }, + { "lightslategray", 0xff778899 }, + { "lightslategrey", 0xff778899 }, + { "lightsteelblue", 0xffb0c4de }, + { "lightyellow", 0xffffffe0 }, + { "lime", 0xff00ff00 }, + { "limegreen", 0xff32cd32 }, + { "linen", 0xfffaf0e6 }, + { "magenta", 0xffff00ff }, + { "maroon", 0xff800000 }, + { "mediumaquamarine", 0xff66cdaa }, + { "mediumblue", 0xff0000cd }, + { "mediumorchid", 0xffba55d3 }, + { "mediumpurple", 0xff9370d8 }, + { "mediumseagreen", 0xff3cb371 }, + { "mediumslateblue", 0xff7b68ee }, + { "mediumspringgreen", 0xff00fa9a }, + { "mediumturquoise", 0xff48d1cc }, + { "mediumvioletred", 0xffc71585 }, + { "midnightblue", 0xff191970 }, + { "mintcream", 0xfff5fffa }, + { "mistyrose", 0xffffe4e1 }, + { "moccasin", 0xffffe4b5 }, + { "navajowhite", 0xffffdead }, + { "navy", 0xff000080 }, + { "oldlace", 0xfffdf5e6 }, + { "olive", 0xff808000 }, + { "olivedrab", 0xff6b8e23 }, + { "orange", 0xffffa500 }, + { "orangered", 0xffff4500 }, + { "orchid", 0xffda70d6 }, + { "palegoldenrod", 0xffeee8aa }, + { "palegreen", 0xff98fb98 }, + { "paleturquoise", 0xffafeeee }, + { "palevioletred", 0xffd87093 }, + { "papayawhip", 0xffffefd5 }, + { "peachpuff", 0xffffdab9 }, + { "peru", 0xffcd853f }, + { "pink", 0xffffc0cb }, + { "plum", 0xffdda0dd }, + { "powderblue", 0xffb0e0e6 }, + { "purple", 0xff800080 }, + { "red", 0xffff0000 }, + { "rosybrown", 0xffbc8f8f }, + { "royalblue", 0xff4169e1 }, + { "saddlebrown", 0xff8b4513 }, + { "salmon", 0xfffa8072 }, + { "sandybrown", 0xfff4a460 }, + { "seagreen", 0xff2e8b57 }, + { "seashell", 0xfffff5ee }, + { "sienna", 0xffa0522d }, + { "silver", 0xffc0c0c0 }, + { "skyblue", 0xff87ceeb }, + { "slateblue", 0xff6a5acd }, + { "slategray", 0xff708090 }, + { "slategrey", 0xff708090 }, + { "snow", 0xfffffafa }, + { "springgreen", 0xff00ff7f }, + { "steelblue", 0xff4682b4 }, + { "tan", 0xffd2b48c }, + { "teal", 0xff008080 }, + { "thistle", 0xffd8bfd8 }, + { "tomato", 0xffff6347 }, + { "turquoise", 0xff40e0d0 }, + { "violet", 0xffee82ee }, + { "wheat", 0xfff5deb3 }, + { "white", 0xffffffff }, + { "whitesmoke", 0xfff5f5f5 }, + { "yellow", 0xffffff00 }, + { "yellowgreen", 0xff9acd32 } +}; + +static inline void +_to_color(const char *str, int *r, int *g, int *b, Eina_Stringshare** ref) +{ + unsigned int i, len = strlen(str); + char *red, *green, *blue; + unsigned char tr, tg, tb; + + if (len == 4 && str[0] == '#') + { + // case for "#456" should be interprete as "#445566" + if (isxdigit(str[1]) && + isxdigit(str[2]) && + isxdigit(str[3])) + { + char tmp[3] = { '\0', '\0', '\0' }; + tmp[0] = str[1]; tmp[1] = str[1]; *r = strtol(tmp, NULL, 16); + tmp[0] = str[2]; tmp[1] = str[2]; *g = strtol(tmp, NULL, 16); + tmp[0] = str[3]; tmp[1] = str[3]; *b = strtol(tmp, NULL, 16); + } + } + else if (len == 7 && str[0] == '#') + { + if (isxdigit(str[1]) && + isxdigit(str[2]) && + isxdigit(str[3]) && + isxdigit(str[4]) && + isxdigit(str[5]) && + isxdigit(str[6])) + { + char tmp[3] = { '\0', '\0', '\0' }; + tmp[0] = str[1]; tmp[1] = str[2]; *r = strtol(tmp, NULL, 16); + tmp[0] = str[3]; tmp[1] = str[4]; *g = strtol(tmp, NULL, 16); + tmp[0] = str[5]; tmp[1] = str[6]; *b = strtol(tmp, NULL, 16); + } + } + else if (len >= 10 && + (str[0] == 'r' || str[0] == 'R') && + (str[1] == 'g' || str[1] == 'G') && + (str[2] == 'b' || str[2] == 'B') && + str[3] == '(' && + str[len - 1] == ')') + { + tr = _color_parser(str + 4, &red); + if (red && *red == ',') + { + tg = _color_parser(red + 1, &green); + if (green && *green == ',') + { + tb = _color_parser(green + 1, &blue); + if (blue && blue[0] == ')' && blue[1] == '\0') + { + *r = tr; *g = tg; *b = tb; + } + } + } + } + else if (len >= 3 && !strncmp(str, "url",3)) + { + *ref = _id_from_url(str+3); + } + else + { + //handle named color + for (i = 0; i < (sizeof (colors) / sizeof (colors[0])); i++) + if (!strcasecmp(colors[i].name, str)) + { + *r = R_VAL(&(colors[i].value)); + *g = G_VAL(&(colors[i].value)); + *b = B_VAL(&(colors[i].value)); + } + } +} + +static inline char * +parse_numbers_array(char *str, double *points, int *pt_count) +{ + int count = 0; + char *end = NULL; + + str = _skip_space(str, NULL); + while (isdigit(*str) || + *str == '-' || + *str == '+' || + *str == '.') + { + points[count++] = strtod(str, &end); + str = end; + str = _skip_space(str, NULL); + if (*str == ',') + ++str; + //eat the rest of space + str = _skip_space(str, NULL); + } + *pt_count = count; + return str; +} + +typedef enum _Matrix_State +{ + SVG_MATRIX_UNKNOWN, + SVG_MATRIX_MATRIX, + SVG_MATRIX_TRANSLATE, + SVG_MATRIX_ROTATE, + SVG_MATRIX_SCALE, + SVG_MATRIX_SKEWX, + SVG_MATRIX_SKEWY +} Matrix_State; + +#define MATRIX_DEF(Name, Value) \ + { #Name, sizeof (#Name), Value} + +static const struct { + const char *tag; + int sz; + Matrix_State state; +} matrix_tags[] = { + MATRIX_DEF(matrix, SVG_MATRIX_MATRIX), + MATRIX_DEF(translate, SVG_MATRIX_TRANSLATE), + MATRIX_DEF(rotate, SVG_MATRIX_ROTATE), + MATRIX_DEF(scale, SVG_MATRIX_SCALE), + MATRIX_DEF(skewX, SVG_MATRIX_SKEWX), + MATRIX_DEF(skewY, SVG_MATRIX_SKEWY) +}; + +/* parse transform attribute + * https://www.w3.org/TR/SVG/coords.html#TransformAttribute + */ +static Eina_Matrix3 * +_parse_transformation_matrix(const char *value) +{ + unsigned int i; + double points[8]; + int pt_count = 0; + double sx, sy; + Matrix_State state = SVG_MATRIX_UNKNOWN; + Eina_Matrix3 *matrix = calloc(1, sizeof(Eina_Matrix3)); + char *str = (char *)value; + char *end = str + strlen(str); + + eina_matrix3_identity(matrix); + while (str < end) + { + if (isspace(*str) || (*str == ',')) + { + ++str; + continue; + } + for (i = 0; i < sizeof (matrix_tags) / sizeof(matrix_tags[0]); i++) + if (!strncmp(matrix_tags[i].tag, str, matrix_tags[i].sz -1)) + { + state = matrix_tags[i].state; + str += (matrix_tags[i].sz -1); + } + if ( state == SVG_MATRIX_UNKNOWN) + goto error; + + str = _skip_space(str, end); + if (*str != '(') + goto error; + ++str; + str = parse_numbers_array(str, points, &pt_count); + if (*str != ')') + goto error; + ++str; + + if (state == SVG_MATRIX_MATRIX) + { + Eina_Matrix3 tmp; + + if (pt_count != 6) goto error; + + eina_matrix3_identity(&tmp); + eina_matrix3_values_set(&tmp, + points[0], points[2], points[4], + points[1], points[3], points[5], + 0, 0, 1); + eina_matrix3_compose(matrix, &tmp, matrix); + } + else if (state == SVG_MATRIX_TRANSLATE) + { + if (pt_count == 1) + eina_matrix3_translate(matrix, points[0], 0); + else if (pt_count == 2) + eina_matrix3_translate(matrix, points[0], points[1]); + else + goto error; + } + else if (state == SVG_MATRIX_ROTATE) + { + if (pt_count == 1) + { + eina_matrix3_rotate(matrix, points[0] * (M_PI/180.0)); + } + else if (pt_count == 3) + { + eina_matrix3_translate(matrix, points[1], points[2]); + eina_matrix3_rotate(matrix, points[0] * (M_PI/180.0)); + eina_matrix3_translate(matrix, -points[1], -points[2]); + } + else + { + goto error; + } + } + else if (state == SVG_MATRIX_SCALE) + { + if (pt_count < 1 || pt_count > 2) goto error; + + sx = points[0]; + sy = sx; + if (pt_count == 2) + sy = points[1]; + eina_matrix3_scale(matrix, sx, sy); + } + } + error: + return matrix; +} + +#define LENGTH_DEF(Name, Value) \ + { #Name, sizeof (#Name), Value} + +static const struct { + const char *tag; + int sz; + Svg_Length_Type type; +} length_tags[] = { + LENGTH_DEF(%, SVG_LT_PERCENT), + LENGTH_DEF(px, SVG_LT_PX), + LENGTH_DEF(pc, SVG_LT_PC), + LENGTH_DEF(pt, SVG_LT_PT), + LENGTH_DEF(mm, SVG_LT_MM), + LENGTH_DEF(cm, SVG_LT_CM), + LENGTH_DEF(in, SVG_LT_IN) +}; + +static double +parse_length(const char *str, Svg_Length_Type *type) +{ + unsigned int i; + double value; + int sz = strlen(str); + + *type = SVG_LT_PX; + for (i = 0; i < sizeof (length_tags) / sizeof(length_tags[0]); i++) + if (length_tags[i].sz - 1 == sz && !strncmp(length_tags[i].tag, str, sz)) + { + *type = length_tags[i].type; + } + value = strtod(str, NULL); + return value; +} + +static Eina_Bool _parse_style_attr(void *data, const char *key, const char *value); +static Eina_Bool _attr_style_node(void *data, const char *str); + +static Eina_Bool +_attr_parse_svg_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Doc_Node *doc = &(node->node.doc); + Svg_Length_Type type; + + // @TODO handle lenght unit. + if (!strcmp(key, "width")) + { + doc->width = parse_length(value, &type); + } + else if (!strcmp(key, "height")) + { + doc->height = parse_length(value, &type); + } + else if (!strcmp(key, "viewBox")) + { + if (_parse_number(&value, &doc->vx)) + if (_parse_number(&value, &doc->vy)) + if (_parse_number(&value, &doc->vw)) + _parse_number(&value, &doc->vh); + } + else if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + +//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint +static void +_handle_paint_attr(Svg_Paint* paint, const char *value) +{ + if (!strcmp(value, "none")) + { + // no paint property + paint->none = EINA_TRUE; + return; + } + paint->none = EINA_FALSE; + if (!strcmp(value, "currentColor")) + { + paint->cur_color = EINA_TRUE; + return; + } + _to_color(value, &paint->r, &paint->g, &paint->b, &paint->url); +} + +static void +_handle_color_attr(Svg_Node* node, const char *value) +{ + Svg_Style_Property *style = node->style; + _to_color(value, &style->r, &style->g, &style->b, NULL); +} + +static void +_handle_fill_attr(Svg_Node* node, const char *value) +{ + Svg_Style_Property *style = node->style; + style->fill.flags |= SVG_FILL_FLAGS_PAINT; + _handle_paint_attr(&style->fill.paint, value); +} + +static void +_handle_stroke_attr(Svg_Node* node, const char *value) +{ + Svg_Style_Property *style = node->style; + style->stroke.flags |= SVG_STROKE_FLAGS_PAINT; + _handle_paint_attr(&style->stroke.paint, value); +} + +static void +_handle_stroke_opacity_attr(Svg_Node* node, const char *value) +{ + node->style->stroke.flags |= SVG_STROKE_FLAGS_OPACITY; + node->style->stroke.opacity = _to_opacity(value); +} + +static void +_handle_stroke_width_attr(Svg_Node* node, const char *value) +{ + node->style->stroke.flags |= SVG_STROKE_FLAGS_WIDTH; + node->style->stroke.width = _to_double(value); +} + +static void +_handle_stroke_linecap_attr(Svg_Node* node, const char *value) +{ + node->style->stroke.flags |= SVG_STROKE_FLAGS_CAP; + node->style->stroke.cap = _to_line_cap(value); +} + +static void +_handle_stroke_linejoin_attr(Svg_Node* node, const char *value) +{ + node->style->stroke.flags |= SVG_STROKE_FLAGS_JOIN; + node->style->stroke.join = _to_line_join(value); +} + +static void +_handle_fill_rule_attr(Svg_Node* node, const char *value) +{ + node->style->fill.flags |= SVG_FILL_FLAGS_FILL_RULE; + node->style->fill.fill_rule = _to_fill_rule(value); +} + +static void +_handle_fill_opacity_attr(Svg_Node* node, const char *value) +{ + node->style->fill.flags |= SVG_FILL_FLAGS_OPACITY; + node->style->fill.opacity = _to_opacity(value); +} + +static void +_handle_transform_attr(Svg_Node* node, const char *value) +{ + node->transform = _parse_transformation_matrix(value); +} + + +typedef void (*Style_Method)(Svg_Node *node, const char *value); + +#define STYLE_DEF(Name, Name1) \ + { #Name, sizeof (#Name), _handle_##Name1##_attr} + +static const struct { + const char *tag; + int sz; + Style_Method tag_handler;; +} style_tags[] = { + STYLE_DEF(color, color), + STYLE_DEF(fill, fill), + STYLE_DEF(fill-rule, fill_rule), + STYLE_DEF(fill-opacity, fill_opacity), + STYLE_DEF(stroke, stroke), + STYLE_DEF(stroke-width, stroke_width), + STYLE_DEF(stroke-linejoin, stroke_linejoin), + STYLE_DEF(stroke-linecap, stroke_linecap), + STYLE_DEF(stroke-opacity, stroke_opacity), + STYLE_DEF(transform, transform) +}; + +static Eina_Bool +_parse_style_attr(void *data, const char *key, const char *value) +{ + Svg_Node* node = data; + unsigned int i; + int sz; + + // trim the white space + key = _skip_space(key, NULL); + + value = _skip_space(value, NULL); + + sz = strlen(key); + for (i = 0; i < sizeof (style_tags) / sizeof(style_tags[0]); i++) + if (style_tags[i].sz - 1 == sz && !strncmp(style_tags[i].tag, key, sz)) + { + style_tags[i].tag_handler(node, value); + return EINA_TRUE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_attr_style_node(void *data, const char *str) +{ + eina_simple_xml_attribute_w3c_parse(str, + _parse_style_attr, data); + return EINA_TRUE; +} + +/* parse g node + * https://www.w3.org/TR/SVG/struct.html#Groups + */ +static Eina_Bool +_attr_parse_g_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + + if (!strcmp(key, "style")) + { + return _attr_style_node(node, value); + } + else if (!strcmp(key, "transform")) + { + node->transform = _parse_transformation_matrix(value); + } + else if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + + +static Svg_Node * +_create_node(Svg_Node *parent, Svg_Node_Type type) +{ + Svg_Node *node = calloc(1, sizeof(Svg_Node)); + + // default fill property + node->style = calloc(1, sizeof(Svg_Style_Property)); + + // update the default value of stroke and fill + //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint + // default fill color is black + node->style->fill.paint.r = 0; + node->style->fill.paint.g = 0; + node->style->fill.paint.b = 0; + node->style->fill.paint.none = EINA_FALSE; + // default fill opacity is 1 + node->style->fill.opacity = 255; + + // default fill rule is nonzero + node->style->fill.fill_rule = EFL_GFX_FILL_RULE_WINDING; + + // default stroke is none + node->style->stroke.paint.none = EINA_TRUE; + // default stroke opacity is 1 + node->style->stroke.opacity = 255; + // default stroke width is 1 + node->style->stroke.width = 1; + // default line cap is butt + node->style->stroke.cap = EFL_GFX_CAP_BUTT; + // default line join is miter + node->style->stroke.join = EFL_GFX_JOIN_MITER; + node->style->stroke.scale = 1.0; + + node->parent = parent; + node->type = type; + node->child = NULL; + + if (parent) + parent->child = eina_list_append(parent->child, node); + return node; +} + +static Svg_Node * +_create_defs_node(Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED) +{ + Svg_Node *node = _create_node(NULL, SVG_NODE_DEFS); + eina_simple_xml_attributes_parse(buf, buflen, + NULL, node); + return node; +} + +static Svg_Node * +_create_g_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_G); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_g_node, node); + return node; +} + +static Svg_Node * +_create_svg_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_DOC); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_svg_node, node); + return node; +} + +static Svg_Node * +_create_switch_node(Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED) +{ + return NULL; +} + +static Eina_Bool +_attr_parse_path_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Path_Node *path = &(node->node.path); + + if (!strcmp(key, "d")) + { + path->path = eina_stringshare_add(value); + } + else if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + +static Svg_Node * +_create_path_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_PATH); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_path_node, node); + return node; +} + +#define CIRCLE_DEF(Name, Field) \ + { #Name, sizeof (#Name), offsetof(Svg_Circle_Node, Field)} + +static const struct { + const char *tag; + int sz; + size_t offset; +} circle_tags[] = { + CIRCLE_DEF(cx, cx), + CIRCLE_DEF(cy, cy), + CIRCLE_DEF(r, r) +}; + +/* parse the attributes for a circle element. + * https://www.w3.org/TR/SVG/shapes.html#CircleElement + */ +static Eina_Bool +_attr_parse_circle_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Circle_Node *circle = &(node->node.circle); + unsigned int i; + unsigned char *array; + int sz = strlen(key); + + array = (unsigned char*) circle; + for (i = 0; i < sizeof (circle_tags) / sizeof(circle_tags[0]); i++) + if (circle_tags[i].sz - 1 == sz && !strncmp(circle_tags[i].tag, key, sz)) + { + *((double*) (array + circle_tags[i].offset)) = _to_double(value); + return EINA_TRUE; + } + + if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + +static Svg_Node * +_create_circle_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_CIRCLE); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_circle_node, node); + return node; +} + +#define ELLIPSE_DEF(Name, Field) \ + { #Name, sizeof (#Name), offsetof(Svg_Ellipse_Node, Field)} + +static const struct { + const char *tag; + int sz; + size_t offset; +} ellipse_tags[] = { + ELLIPSE_DEF(cx,cx), + ELLIPSE_DEF(cy,cy), + ELLIPSE_DEF(rx,rx), + ELLIPSE_DEF(ry,ry) +}; + +/* parse the attributes for an ellipse element. + * https://www.w3.org/TR/SVG/shapes.html#EllipseElement + */ +static Eina_Bool +_attr_parse_ellipse_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Ellipse_Node *ellipse = &(node->node.ellipse); + unsigned int i; + unsigned char *array; + int sz = strlen(key); + + array = (unsigned char*) ellipse; + for (i = 0; i < sizeof (ellipse_tags) / sizeof(ellipse_tags[0]); i++) + if (ellipse_tags[i].sz - 1 == sz && !strncmp(ellipse_tags[i].tag, key, sz)) + { + *((double*) (array + ellipse_tags[i].offset)) = _to_double(value); + return EINA_TRUE; + } + + if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + +static Svg_Node * +_create_ellipse_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_ELLIPSE); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_ellipse_node, node); + return node; +} + +static void +_attr_parse_polygon_points(const char *str, double **points, int *point_count) +{ + double tmp[50]; + int tmp_count=0; + int count = 0; + double num; + double *point_array = NULL; + + while (_parse_number(&str, &num)) + { + tmp[tmp_count++] = num; + if (tmp_count == 50) + { + point_array = realloc(point_array, (count + tmp_count) * sizeof(double)); + memcpy(&point_array[count], tmp, tmp_count * sizeof(double)); + count += tmp_count; + } + } + + if (tmp_count > 0) + { + point_array = realloc(point_array, (count + tmp_count) * sizeof(double)); + memcpy(&point_array[count], tmp, tmp_count * sizeof(double)); + count += tmp_count; + } + *point_count = count; + *points = point_array; +} + +/* parse the attributes for a polygon element. + * https://www.w3.org/TR/SVG/shapes.html#PolylineElement + */ +static Eina_Bool +_attr_parse_polygon_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Polygon_Node *polygon = &(node->node.polygon); + + if (!strcmp(key, "points")) + { + _attr_parse_polygon_points(value, &polygon->points, &polygon->points_count); + } + else if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else + { + _parse_style_attr(node, key, value); + } + return EINA_TRUE; +} + +static Svg_Node * +_create_polygon_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_POLYGON); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_polygon_node, node); + return node; +} + +static Svg_Node * +_create_polyline_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_POLYLINE); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_polygon_node, node); + return node; +} + +#define RECT_DEF(Name, Field) \ + { #Name, sizeof (#Name), offsetof(Svg_Rect_Node, Field)} + +static const struct { + const char *tag; + int sz; + size_t offset; +} rect_tags[] = { + RECT_DEF(x,x), + RECT_DEF(y, y), + RECT_DEF(width, w), + RECT_DEF(height, h), + RECT_DEF(rx, rx), + RECT_DEF(ry, ry) +}; + +/* parse the attributes for a rect element. + * https://www.w3.org/TR/SVG/shapes.html#RectElement + */ +static Eina_Bool +_attr_parse_rect_node(void *data, const char *key, const char *value) +{ + Svg_Node *node = data; + Svg_Rect_Node *rect = & (node->node.rect); + unsigned int i; + unsigned char *array; + int sz = strlen(key); + + array = (unsigned char*) rect; + for (i = 0; i < sizeof (rect_tags) / sizeof(rect_tags[0]); i++) + if (rect_tags[i].sz - 1 == sz && !strncmp(rect_tags[i].tag, key, sz)) + { + *((double*) (array + rect_tags[i].offset)) = _to_double(value); + return EINA_TRUE; + } + + if (!strcmp(key, "id")) + { + node->id = _copy_id(value); + } + else if (!strcmp(key, "style")) + { + _attr_style_node(node, value); + } + else + { + _parse_style_attr(node, key, value); + } + + if (rect->rx != 0 && rect->ry == 0) rect->ry = rect->rx; + if (rect->ry != 0 && rect->rx == 0) rect->rx = rect->ry; + + return EINA_TRUE; +} + +static Svg_Node * +_create_rect_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_RECT); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_rect_node, node); + return node; +} + +static Eina_Stringshare * +_id_from_href(const char *href) +{ + href = _skip_space(href, NULL); + if ((*href) == '#') + href++; + return eina_stringshare_add(href); +} + +static Svg_Node* +_get_defs_node(Svg_Node *node) +{ + if (!node) return NULL; + + while (node->parent != NULL) + { + node = node->parent; + } + + if (node->type == SVG_NODE_DOC) + return node->node.doc.defs; + + return NULL; +} + +static Svg_Node* +_find_child_by_id(Svg_Node *node, const char *id) +{ + Eina_List *l; + Svg_Node *child; + + if (!node) return NULL; + + EINA_LIST_FOREACH(node->child, l, child) + { + if ((child->id != NULL) && !strcmp(child->id, id)) + return child; + } + return NULL; +} + +static Eina_List * +_clone_grad_stops(Eina_List *from) +{ + Efl_Gfx_Gradient_Stop *stop; + Eina_List *l; + Eina_List *res = NULL; + + EINA_LIST_FOREACH(from, l, stop) + { + Efl_Gfx_Gradient_Stop *new_stop; + + new_stop = calloc(1, sizeof(Efl_Gfx_Gradient_Stop)); + memcpy(new_stop, stop, sizeof(Efl_Gfx_Gradient_Stop)); + res = eina_list_append(res, new_stop); + } + + return res; +} + +static Svg_Style_Gradient * +_clone_gradient(Svg_Style_Gradient *from) +{ + Svg_Style_Gradient *grad; + + if (!from) return NULL; + + grad= calloc(1, sizeof(Svg_Style_Gradient)); + grad->type = from->type; + grad->id = _copy_id(from->id); + grad->ref = _copy_id(from->ref); + grad->spread = from->spread; + grad->stops = _clone_grad_stops(from->stops); + if (grad->type == SVG_LINEAR_GRADIENT) + { + grad->linear = calloc(1, sizeof(Svg_Linear_Gradient)); + memcpy(grad->linear, from->linear, sizeof(Svg_Linear_Gradient)); + } + else if (grad->type == SVG_RADIAL_GRADIENT) + { + grad->radial = calloc(1, sizeof(Svg_Radial_Gradient)); + memcpy(grad->radial, from->radial, sizeof(Svg_Radial_Gradient)); + } + + return grad; +} + +static void +_copy_attribute(Svg_Node *to, Svg_Node *from) +{ + // copy matrix attribute + if (from->transform) + { + to->transform = calloc(1, sizeof(Eina_Matrix3)); + eina_matrix3_copy(to->transform, from->transform); + } + // copy style attribute; + memcpy(to->style, from->style, sizeof(Svg_Style_Property)); + + // copy node attribute + switch (from->type) + { + case SVG_NODE_CIRCLE: + to->node.circle.cx = from->node.circle.cx; + to->node.circle.cy = from->node.circle.cy; + to->node.circle.r = from->node.circle.r; + break; + case SVG_NODE_ELLIPSE: + to->node.ellipse.cx = from->node.ellipse.cx; + to->node.ellipse.cy = from->node.ellipse.cy; + to->node.ellipse.rx = from->node.ellipse.rx; + to->node.ellipse.ry = from->node.ellipse.ry; + break; + case SVG_NODE_RECT: + to->node.rect.x = from->node.rect.x; + to->node.rect.y = from->node.rect.y; + to->node.rect.w = from->node.rect.w; + to->node.rect.h = from->node.rect.h; + to->node.rect.rx = from->node.rect.rx; + to->node.rect.ry = from->node.rect.ry; + break; + case SVG_NODE_PATH: + to->node.path.path = eina_stringshare_add(from->node.path.path); + break; + case SVG_NODE_POLYGON: + to->node.polygon.points_count = from->node.polygon.points_count; + to->node.polygon.points = calloc(to->node.polygon.points_count, sizeof(double)); + break; + default: + break; + } + +} + +static void +_clone_node(Svg_Node *from, Svg_Node *parent) +{ + Svg_Node *new_node; + Eina_List *l; + Svg_Node *child; + + if (!from) return; + + new_node = _create_node(parent, from->type); + _copy_attribute(new_node, from); + + EINA_LIST_FOREACH(from->child, l, child) + { + _clone_node(child, new_node); + } + +} + +static Eina_Bool +_attr_parse_use_node(void *data, const char *key, const char *value) +{ + Svg_Node *defs, *node_from, *node = data; + Eina_Stringshare *id; + + if (!strcmp(key, "xlink:href")) + { + id = _id_from_href(value); + defs = _get_defs_node(node); + node_from = _find_child_by_id(defs, id); + _clone_node(node_from, node); + eina_stringshare_del(id); + } + else + { + _attr_parse_g_node(data, key, value); + } + return EINA_TRUE; +} + +static Svg_Node * +_create_use_node(Svg_Node *parent, const char *buf, unsigned buflen) +{ + Svg_Node *node = _create_node(parent, SVG_NODE_G); + + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_use_node, node); + return node; +} + +#define TAG_DEF(Name) \ + { #Name, sizeof (#Name), _create_##Name##_node } + +static const struct { + const char *tag; + int sz; + Factory_Method tag_handler; +} graphics_tags[] = { + TAG_DEF(use), + TAG_DEF(circle), + TAG_DEF(ellipse), + TAG_DEF(path), + TAG_DEF(polygon), + TAG_DEF(rect), + TAG_DEF(polyline), +}; + +static const struct { + const char *tag; + int sz; + Factory_Method tag_handler; +} group_tags[] = { + TAG_DEF(defs), + TAG_DEF(g), + TAG_DEF(svg), + TAG_DEF(switch) +}; + +#define FIND_FACTORY(Short_Name, Tags_Array) \ + static Factory_Method \ + _find_##Short_Name##_factory(const char *name) \ + { \ + unsigned int i; \ + int sz = strlen(name); \ + \ + for (i = 0; i < sizeof (Tags_Array) / sizeof(Tags_Array[0]); i++) \ + if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) \ + { \ + return Tags_Array[i].tag_handler; \ + } \ + return NULL; \ + } + +FIND_FACTORY(group, group_tags); +FIND_FACTORY(graphics, graphics_tags); + +Efl_Gfx_Gradient_Spread +_parse_spread_value(const char *value) +{ + Efl_Gfx_Gradient_Spread spread = EFL_GFX_GRADIENT_SPREAD_PAD; + + if (!strcmp(value, "reflect")) + { + spread = EFL_GFX_GRADIENT_SPREAD_REFLECT; + } + else if (!strcmp(value, "repeat")) + { + spread = EFL_GFX_GRADIENT_SPREAD_REPEAT; + } + + return spread; +} + +static void +_handle_radial_cx_attr(Svg_Radial_Gradient* radial, const char *value) +{ + radial->cx = _to_double(value); +} + +static void +_handle_radial_cy_attr(Svg_Radial_Gradient* radial, const char *value) +{ + radial->cy = _to_double(value); +} + +static void +_handle_radial_fx_attr(Svg_Radial_Gradient* radial, const char *value) +{ + radial->fx = _to_double(value); +} + +static void +_handle_radial_fy_attr(Svg_Radial_Gradient* radial, const char *value) +{ + radial->fy = _to_double(value); +} + +static void +_handle_radial_r_attr(Svg_Radial_Gradient* radial, const char *value) +{ + radial->r = _to_double(value); +} + + +typedef void (*Radial_Method)(Svg_Radial_Gradient *radial, const char *value); + +#define RADIAL_DEF(Name) \ + { #Name, sizeof (#Name), _handle_radial_##Name##_attr} + +static const struct { + const char *tag; + int sz; + Radial_Method tag_handler;; +} radial_tags[] = { + RADIAL_DEF(cx), + RADIAL_DEF(cy), + RADIAL_DEF(fx), + RADIAL_DEF(fy), + RADIAL_DEF(r) +}; + +static Eina_Bool +_attr_parse_radial_gradient_node(void *data, const char *key, const char *value) +{ + Svg_Style_Gradient *grad = data; + Svg_Radial_Gradient *radial = grad->radial; + unsigned int i; + int sz = strlen(key); + + for (i = 0; i < sizeof (radial_tags) / sizeof(radial_tags[0]); i++) + if (radial_tags[i].sz - 1 == sz && !strncmp(radial_tags[i].tag, key, sz)) + { + radial_tags[i].tag_handler(radial, value); + return EINA_TRUE; + } + + if (!strcmp(key, "id")) + { + grad->id = _copy_id(value); + } + else if (!strcmp(key, "spreadMethod")) + { + grad->spread = _parse_spread_value(value); + } + else if (!strcmp(key, "xlink:href")) + { + grad->ref = _id_from_href(value); + } + + return EINA_TRUE; +} + +static Svg_Style_Gradient * +_create_radialGradient(const char *buf, unsigned buflen) +{ + Svg_Style_Gradient *grad = calloc(1, sizeof(Svg_Style_Gradient)); + + grad->type = SVG_RADIAL_GRADIENT; + grad->radial = calloc(1, sizeof(Svg_Radial_Gradient)); + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_radial_gradient_node, grad); + return grad; + +} + +static Eina_Bool +_attr_parse_stops(void *data, const char *key, const char *value) +{ + Efl_Gfx_Gradient_Stop *stop = data; + + if (!strcmp(key, "offset")) + { + stop->offset = _to_double(value); + } + else if (!strcmp(key, "stop-opacity")) + { + stop->a = _to_opacity(value); + } + else if (!strcmp(key, "stop-color")) + { + _to_color(value, &stop->r, &stop->g, &stop->b, NULL); + } + else if (!strcmp(key, "style")) + { + eina_simple_xml_attribute_w3c_parse(value, + _attr_parse_stops, data); + } + + return EINA_TRUE; +} + +static void +_handle_linear_x1_attr(Svg_Linear_Gradient* linear, const char *value) +{ + linear->x1 = _to_double(value); +} + +static void +_handle_linear_y1_attr(Svg_Linear_Gradient* linear, const char *value) +{ + linear->y1 = _to_double(value); +} + +static void +_handle_linear_x2_attr(Svg_Linear_Gradient* linear, const char *value) +{ + linear->x2 = _to_double(value); +} + +static void +_handle_linear_y2_attr(Svg_Linear_Gradient* linear, const char *value) +{ + linear->y2 = _to_double(value); +} + + +typedef void (*Linear_Method)(Svg_Linear_Gradient *linear, const char *value); + +#define LINEAR_DEF(Name) \ + { #Name, sizeof (#Name), _handle_linear_##Name##_attr} + +static const struct { + const char *tag; + int sz; + Linear_Method tag_handler;; +} linear_tags[] = { + LINEAR_DEF(x1), + LINEAR_DEF(y1), + LINEAR_DEF(x2), + LINEAR_DEF(y2) +}; + +static Eina_Bool +_attr_parse_linear_gradient_node(void *data, const char *key, const char *value) +{ + Svg_Style_Gradient *grad = data; + Svg_Linear_Gradient *linear = grad->linear; + unsigned int i; + int sz = strlen(key); + + for (i = 0; i < sizeof (linear_tags) / sizeof(linear_tags[0]); i++) + if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) + { + linear_tags[i].tag_handler(linear, value); + return EINA_TRUE; + } + + if (!strcmp(key, "id")) + { + grad->id = _copy_id(value); + } + else if (!strcmp(key, "spreadMethod")) + { + grad->spread = _parse_spread_value(value); + } + else if (!strcmp(key, "xlink:href")) + { + grad->ref = _id_from_href(value); + } + + return EINA_TRUE; +} + +static Svg_Style_Gradient * +_create_linearGradient(const char *buf, unsigned buflen) +{ + Svg_Style_Gradient *grad = calloc(1, sizeof(Svg_Style_Gradient)); + + grad->type = SVG_LINEAR_GRADIENT; + grad->linear = calloc(1, sizeof(Svg_Linear_Gradient)); + eina_simple_xml_attributes_parse(buf, buflen, + _attr_parse_linear_gradient_node, grad); + return grad; + +} + +#define GRADIENT_DEF(Name) \ + { #Name, sizeof (#Name), _create_##Name } + +static const struct { + const char *tag; + int sz; + Gradient_Factory_Method tag_handler; +} gradient_tags[] = { + GRADIENT_DEF(linearGradient), + GRADIENT_DEF(radialGradient) +}; + +static Gradient_Factory_Method +_find_gradient_factory(const char *name) +{ + unsigned int i; + int sz = strlen(name); + + for (i = 0; i < sizeof (gradient_tags) / sizeof(gradient_tags[0]); i++) + if (gradient_tags[i].sz - 1 == sz && !strncmp(gradient_tags[i].tag, name, sz)) + { + return gradient_tags[i].tag_handler; + } + return NULL; +} + +static void +_evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader, + const char *content, unsigned int length) +{ + const char *attrs = NULL; + int attrs_length = 0; + int sz = length; + char tag_name[20]; + Factory_Method method; + Gradient_Factory_Method gradient_method; + Svg_Node *node = NULL, *parent; + loader->level++; + attrs = eina_simple_xml_tag_attributes_find(content, length); + + if (!attrs) + { + // parse the empty tag + attrs = content; + while ((attrs != NULL) && *attrs != '>') + attrs++; + } + + if (attrs) + { + // find out the tag name starting from content till sz length + sz = attrs - content; + attrs_length = length - sz; + while ((sz > 0) && (isspace(content[sz - 1]))) + sz--; + strncpy(tag_name, content, sz); + tag_name[sz] = '\0'; + } + + if ((method = _find_group_factory(tag_name))) + { + //group + if (!loader->doc) + { + if (strcmp(tag_name, "svg")) + return; // Not a valid svg document + node = method(NULL, attrs, attrs_length); + loader->doc = node; + } + else + { + parent = eina_array_data_get(loader->stack, eina_array_count(loader->stack) - 1); + node = method(parent, attrs, attrs_length); + } + eina_array_push(loader->stack, node); + + if (node->type == SVG_NODE_DEFS) + { + loader->doc->node.doc.defs = node; + loader->def = node; + } + } + else if ((method = _find_graphics_factory(tag_name))) + { + parent = eina_array_data_get(loader->stack, eina_array_count(loader->stack) - 1); + node = method(parent, attrs, attrs_length); + } + else if ((gradient_method = _find_gradient_factory(tag_name))) + { + Svg_Style_Gradient *gradient; + gradient = gradient_method(attrs, attrs_length); + if (loader->doc->node.doc.defs) + { + loader->def->node.defs.gradients = eina_list_append(loader->def->node.defs.gradients, gradient); + } + loader->gradient = gradient; + } + else if (!strcmp(tag_name, "stop")) + { + Efl_Gfx_Gradient_Stop *stop = calloc(1, sizeof(Efl_Gfx_Gradient_Stop)); + eina_simple_xml_attributes_parse(attrs, attrs_length, + _attr_parse_stops, stop); + if (loader->gradient) + loader->gradient->stops = eina_list_append(loader->gradient->stops, stop); + } + +} + +#define POP_TAG(Tag) \ + { #Tag, sizeof (#Tag) } + +static const struct { + const char *tag; + size_t sz; +} pop_array[] = { + POP_TAG(g), + POP_TAG(svg), + POP_TAG(defs) +}; + +static void +_evas_svg_loader_xml_close_parser(Evas_SVG_Loader *loader, + const char *content, + unsigned int length EINA_UNUSED) +{ + unsigned int i; + + content = _skip_space(content, NULL); + + for (i = 0; i < sizeof (pop_array) / sizeof (pop_array[0]); i++) + if (!strncmp(content, pop_array[i].tag, pop_array[i].sz - 1)) + { + eina_array_pop(loader->stack); + break ; + } + + loader->level--; +} + +static Eina_Bool +_evas_svg_loader_parser(void *data, Eina_Simple_XML_Type type, + const char *content, + unsigned int offset EINA_UNUSED, unsigned int length) +{ + Evas_SVG_Loader *loader = data; + + switch (type) + { + case EINA_SIMPLE_XML_OPEN: + _evas_svg_loader_xml_open_parser(loader, content, length); + break; + case EINA_SIMPLE_XML_OPEN_EMPTY: + _evas_svg_loader_xml_open_parser(loader, content, length); + case EINA_SIMPLE_XML_CLOSE: + _evas_svg_loader_xml_close_parser(loader, content, length); + break; + case EINA_SIMPLE_XML_DATA: + case EINA_SIMPLE_XML_CDATA: + case EINA_SIMPLE_XML_DOCTYPE_CHILD: + break; + case EINA_SIMPLE_XML_IGNORED: + case EINA_SIMPLE_XML_COMMENT: + case EINA_SIMPLE_XML_DOCTYPE: + break; + + default: + break; + } + + return EINA_TRUE; +} + +static void +_inherit_style(Svg_Style_Property *child, Svg_Style_Property *parent) +{ + if (parent == NULL) + return; + // inherit the property of parent if not present in child. + // fill + if (!(child->fill.flags & SVG_FILL_FLAGS_PAINT)) + { + child->fill.paint.r = parent->fill.paint.r; + child->fill.paint.g = parent->fill.paint.g; + child->fill.paint.b = parent->fill.paint.b; + child->fill.paint.none = parent->fill.paint.none; + child->fill.paint.cur_color = parent->fill.paint.cur_color; + child->fill.paint.url = _copy_id(parent->fill.paint.url); + } + if (!(child->fill.flags & SVG_FILL_FLAGS_OPACITY)) + { + child->fill.opacity = parent->fill.opacity; + } + if (!(child->fill.flags & SVG_FILL_FLAGS_FILL_RULE)) + { + child->fill.fill_rule = parent->fill.fill_rule; + } + // stroke + if (!(child->stroke.flags & SVG_STROKE_FLAGS_PAINT)) + { + child->stroke.paint.r = parent->stroke.paint.r; + child->stroke.paint.g = parent->stroke.paint.g; + child->stroke.paint.b = parent->stroke.paint.b; + child->stroke.paint.none = parent->stroke.paint.none; + child->stroke.paint.cur_color = parent->stroke.paint.cur_color; + child->stroke.paint.url = _copy_id(parent->stroke.paint.url); + } + if (!(child->stroke.flags & SVG_STROKE_FLAGS_OPACITY)) + { + child->stroke.opacity = parent->stroke.opacity; + } + if (!(child->stroke.flags & SVG_STROKE_FLAGS_WIDTH)) + { + child->stroke.width = parent->stroke.width; + } + if (!(child->stroke.flags & SVG_STROKE_FLAGS_CAP)) + { + child->stroke.cap = parent->stroke.cap; + } + if (!(child->stroke.flags & SVG_STROKE_FLAGS_JOIN)) + { + child->stroke.join = parent->stroke.join; + } +} + +void +_update_style(Svg_Node *node, Svg_Style_Property *parent_style) +{ + Eina_List *l; + Svg_Node *child; + + _inherit_style(node->style, parent_style); + + EINA_LIST_FOREACH(node->child, l, child) + { + _update_style(child, node->style); + } +} + +static Svg_Style_Gradient* +_dup_gradient(Eina_List *grad_list, const char *id) +{ + Svg_Style_Gradient *grad; + Svg_Style_Gradient *result = NULL; + Eina_List *l; + + EINA_LIST_FOREACH(grad_list, l, grad) + { + if (!strcmp(grad->id, id)) + { + result = _clone_gradient(grad); + break; + } + } + + if (result && result->ref) + { + EINA_LIST_FOREACH(grad_list, l, grad) + { + if (!strcmp(grad->id, result->ref)) + { + if (!result->stops) + { + result->stops = _clone_grad_stops(grad->stops); + } + //TODO properly inherit other property + break; + } + } + } + + return result; +} + +void +_update_gradient(Svg_Node *node, Eina_List *grad_list) +{ + Eina_List *l; + Svg_Node *child; + + if (node->child) + { + EINA_LIST_FOREACH(node->child, l, child) + { + _update_gradient(child, grad_list); + } + } + else + { + if (node->style->fill.paint.url) + { + node->style->fill.paint.gradient = _dup_gradient(grad_list, node->style->fill.paint.url); + } + else if (node->style->stroke.paint.url) + { + node->style->stroke.paint.gradient = _dup_gradient(grad_list, node->style->stroke.paint.url); + } + } +} + +Svg_Node * +_svg_load(Eina_File *f, const char *key EINA_UNUSED) +{ + Evas_SVG_Loader loader = { + NULL, NULL, NULL, NULL, 0, EINA_FALSE + }; + const char *content; + unsigned int length; + Svg_Node *defs; + + if (!f) return NULL; + + length = eina_file_size_get(f); + content = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (content) + { + loader.stack = eina_array_new(8); + eina_simple_xml_parse(content, length, EINA_TRUE, + _evas_svg_loader_parser, &loader); + + eina_array_free(loader.stack); + eina_file_map_free(f, (void*) content); + } + + if (loader.doc) + { + _update_style(loader.doc, NULL); + defs = loader.doc->node.doc.defs; + if (defs) + _update_gradient(loader.doc, defs->node.defs.gradients); + } + + return loader.doc; +} diff --git a/src/lib/edje/Edje_Common.h b/src/lib/edje/Edje_Common.h index 2e433e6..d5f1c26 100644 --- a/src/lib/edje/Edje_Common.h +++ b/src/lib/edje/Edje_Common.h @@ -1236,7 +1236,8 @@ typedef enum _Edje_Part_Type EDJE_PART_TYPE_LIGHT = 14, EDJE_PART_TYPE_CAMERA = 15, EDJE_PART_TYPE_SNAPSHOT = 16, /**< Snapshot @since 1.16 */ - EDJE_PART_TYPE_LAST = 17 /**< Last type value */ + EDJE_PART_TYPE_VECTOR = 17, /**< Vector @since 1.18 */ + EDJE_PART_TYPE_LAST = 18 /**< Last type value */ } Edje_Part_Type; /** * @} diff --git a/src/lib/edje/edje_cache.c b/src/lib/edje/edje_cache.c index 3806221..1724b32 100644 --- a/src/lib/edje/edje_cache.c +++ b/src/lib/edje/edje_cache.c @@ -34,6 +34,7 @@ edje_cache_emp_alloc(Edje_Part_Collection_Directory_Entry *ce) INIT_EMP_BOTH(EXTERNAL, Edje_Part_Description_External, ce); INIT_EMP_BOTH(SPACER, Edje_Part_Description_Common, ce); INIT_EMP_BOTH(SNAPSHOT, Edje_Part_Description_Snapshot, ce); + INIT_EMP_BOTH(VECTOR, Edje_Part_Description_Vector, ce); INIT_EMP(part, Edje_Part, ce); } @@ -53,6 +54,7 @@ edje_cache_emp_free(Edje_Part_Collection_Directory_Entry *ce) eina_mempool_del(ce->mp.EXTERNAL); eina_mempool_del(ce->mp.SPACER); eina_mempool_del(ce->mp.SNAPSHOT); + eina_mempool_del(ce->mp.VECTOR); eina_mempool_del(ce->mp.part); memset(&ce->mp, 0, sizeof (ce->mp)); diff --git a/src/lib/edje/edje_calc.c b/src/lib/edje/edje_calc.c index ef47b81..9737002 100644 --- a/src/lib/edje/edje_calc.c +++ b/src/lib/edje/edje_calc.c @@ -413,6 +413,7 @@ case EDJE_PART_TYPE_##Short: \ EDIT_ALLOC_POOL_RTL(CAMERA, Camera, camera); EDIT_ALLOC_POOL_RTL(LIGHT, Light, light); EDIT_ALLOC_POOL_RTL(MESH_NODE, Mesh_Node, mesh_node); + EDIT_ALLOC_POOL_RTL(VECTOR, Vector, vector); } if (desc_rtl) @@ -2858,6 +2859,7 @@ _edje_part_recalc_single(Edje *ed, case EDJE_PART_TYPE_LIGHT: case EDJE_PART_TYPE_CAMERA: case EDJE_PART_TYPE_SNAPSHOT: + case EDJE_PART_TYPE_VECTOR: break; case EDJE_PART_TYPE_GRADIENT: @@ -3112,6 +3114,7 @@ _edje_proxy_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3, Edj case EDJE_PART_TYPE_TABLE: case EDJE_PART_TYPE_PROXY: case EDJE_PART_TYPE_SNAPSHOT: + case EDJE_PART_TYPE_VECTOR: evas_object_image_source_set(ep->object, pp->object); break; @@ -3208,6 +3211,32 @@ _edje_image_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3, Edj evas_object_image_border_center_fill_set(ep->object, EVAS_BORDER_FILL_SOLID); } +static void +_edje_svg_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EINA_UNUSED, Edje_Part_Description_Vector *chosen_desc, FLOAT_T pos) +{ + int new_svg = 0; + int w, h; + + evas_object_geometry_get(ep->object, NULL, NULL, &w, &h); + if( (w == 0) || (h == 0)) return; + + if (ep->param2) + { + Edje_Part_Description_Vector *next_state = (Edje_Part_Description_Vector *)ep->param2->description; + if (chosen_desc->vg.id != next_state->vg.id) + { + new_svg = next_state->vg.id; + } + else + { + new_svg = 0; + pos = 0; + } + + } + evas_object_vg_path_set(ep->object, ed->file->path, chosen_desc->vg.id, new_svg, pos); +} + static Edje_Real_Part * _edje_real_part_state_get(Edje *ed, Edje_Real_Part *ep, int flags, int id, int *state) { @@ -4494,6 +4523,7 @@ _edje_part_recalc(Edje *ed, Edje_Real_Part *ep, int flags, Edje_Calc_Params *sta case EDJE_PART_TYPE_BOX: case EDJE_PART_TYPE_TABLE: case EDJE_PART_TYPE_SNAPSHOT: + case EDJE_PART_TYPE_VECTOR: evas_object_color_set(ep->object, (pf->color.r * pf->color.a) / 255, (pf->color.g * pf->color.a) / 255, @@ -4824,6 +4854,10 @@ _edje_part_recalc(Edje *ed, Edje_Real_Part *ep, int flags, Edje_Calc_Params *sta _edje_textblock_recalc_apply(ed, ep, pf, (Edje_Part_Description_Text *)chosen_desc); break; + case EDJE_PART_TYPE_VECTOR: + _edje_svg_recalc_apply(ed, ep, pf, (Edje_Part_Description_Vector *)chosen_desc, pos); + break; + case EDJE_PART_TYPE_EXTERNAL: case EDJE_PART_TYPE_RECTANGLE: case EDJE_PART_TYPE_SWALLOW: diff --git a/src/lib/edje/edje_convert.c b/src/lib/edje/edje_convert.c index 4316004..a491fd3 100644 --- a/src/lib/edje/edje_convert.c +++ b/src/lib/edje/edje_convert.c @@ -285,6 +285,7 @@ case EDJE_PART_TYPE_##Tp: \ CSP(BOX, ce); CSP(TABLE, ce); CSP(EXTERNAL, ce); + CSP(VECTOR, ce); default: count = &dummy; @@ -308,6 +309,7 @@ case EDJE_PART_TYPE_##Tp: \ CONVERT_EMN(TABLE, Edje_Part_Description_Table, ce); CONVERT_EMN(EXTERNAL, Edje_Part_Description_External, ce); CONVERT_EMN(part, Edje_Part, ce); + CONVERT_EMN(VECTOR, Edje_Part_Description_Vector, ce); /* Change structure layout */ edc = calloc(1, sizeof (Edje_Part_Collection)); @@ -492,6 +494,7 @@ case EDJE_PART_TYPE_##Short: CONVERT_ALLOC_POOL(BOX, Box, box); CONVERT_ALLOC_POOL(TABLE, Table, table); CONVERT_ALLOC_POOL(EXTERNAL, External, external_params); + CONVERT_ALLOC_POOL(VECTOR, Vector, vector); } *result = oed->common; diff --git a/src/lib/edje/edje_data.c b/src/lib/edje/edje_data.c index e00d011..b8e52df 100644 --- a/src/lib/edje/edje_data.c +++ b/src/lib/edje/edje_data.c @@ -84,6 +84,16 @@ Eet_Data_Descriptor *_edje_edd_edje_map_colors = NULL; Eet_Data_Descriptor *_edje_edd_edje_map_colors_pointer = NULL; Eet_Data_Descriptor *_edje_edd_edje_filter = NULL; Eet_Data_Descriptor *_edje_edd_edje_filter_directory = NULL; +Eet_Data_Descriptor *_edje_edd_edje_part_description_vector = NULL; +Eet_Data_Descriptor *_edje_edd_edje_part_description_vector_pointer = NULL; + + +#define FREE_DESCRIPTOR(eed) \ + if (eed) \ + { \ + eet_data_descriptor_free((eed)); \ + (eed) = NULL; \ + } /* allocate a description struct. * this initializes clip_to_id as this field will not be present in most @@ -125,6 +135,7 @@ EMP(MESH_NODE, mesh_node) EMP(LIGHT, light) EMP(CAMERA, camera) EMP(SNAPSHOT, snapshot) +EMP(VECTOR, vector) #undef EMP EAPI Eina_Mempool *_emp_part = NULL; @@ -174,7 +185,8 @@ struct { EDJE_PART_TYPE_MESH_NODE, "mesh_node" }, { EDJE_PART_TYPE_LIGHT, "light" }, { EDJE_PART_TYPE_CAMERA, "camera" }, - { EDJE_PART_TYPE_SNAPSHOT, "snapshot" } + { EDJE_PART_TYPE_SNAPSHOT, "snapshot" }, + { EDJE_PART_TYPE_VECTOR, "vector" } }; static const char * @@ -514,6 +526,7 @@ _edje_edd_init(void) EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection_directory_entry, Edje_Part_Collection_Directory_Entry, "count.CAMERA", count.CAMERA, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection_directory_entry, Edje_Part_Collection_Directory_Entry, "count.part", count.part, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection_directory_entry, Edje_Part_Collection_Directory_Entry, "group_alias", group_alias, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection_directory_entry, Edje_Part_Collection_Directory_Entry, "count.VECTOR", count.VECTOR, EET_T_INT); EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Style_Tag); _edje_edd_edje_style_tag = @@ -895,6 +908,17 @@ _edje_edd_init(void) eet_data_descriptor_file_new(&eddc); EDJE_DATA_DESCRIPTOR_DESCRIPTION_COMMON(_edje_edd_edje_part_description_rectangle, Edje_Part_Description_Common); + // SVG, since 1.18 + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Description_Vector); + eddc.func.mem_free = mem_free_vector; + eddc.func.mem_alloc = mem_alloc_vector; + _edje_edd_edje_part_description_vector = + eet_data_descriptor_file_new(&eddc); + EDJE_DATA_DESCRIPTOR_DESCRIPTION_COMMON_SUB(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, common); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.id", vg.id, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.set", vg.set, EET_T_UCHAR); + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Description_Common); eddc.func.mem_free = mem_free_spacer; eddc.func.mem_alloc = mem_alloc_spacer; @@ -1180,6 +1204,7 @@ _edje_edd_init(void) EDJE_DEFINE_POINTER_TYPE(Part_Description_Mesh_Node, part_description_mesh_node); EDJE_DEFINE_POINTER_TYPE(Part_Description_Camera, part_description_camera); EDJE_DEFINE_POINTER_TYPE(Part_Description_Light, part_description_light); + EDJE_DEFINE_POINTER_TYPE(Part_Description_Vector, part_description_vector); eddc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION; eddc.func.type_get = _edje_description_variant_type_get; @@ -1201,6 +1226,7 @@ _edje_edd_init(void) EET_DATA_DESCRIPTOR_ADD_MAPPING(_edje_edd_edje_part_description_variant, "mesh_node", _edje_edd_edje_part_description_mesh_node); EET_DATA_DESCRIPTOR_ADD_MAPPING(_edje_edd_edje_part_description_variant, "light", _edje_edd_edje_part_description_light); EET_DATA_DESCRIPTOR_ADD_MAPPING(_edje_edd_edje_part_description_variant, "camera", _edje_edd_edje_part_description_camera); + EET_DATA_DESCRIPTOR_ADD_MAPPING(_edje_edd_edje_part_description_variant, "vector", _edje_edd_edje_part_description_vector); #define EDJE_ADD_ARRAY_MAPPING(Variant, Type, Minus) \ { \ @@ -1230,6 +1256,7 @@ _edje_edd_init(void) EDJE_ADD_ARRAY_MAPPING(_edje_edd_edje_part_description_variant_list, "mesh_node", mesh_node); EDJE_ADD_ARRAY_MAPPING(_edje_edd_edje_part_description_variant_list, "light", light); EDJE_ADD_ARRAY_MAPPING(_edje_edd_edje_part_description_variant_list, "camera", camera); + EDJE_ADD_ARRAY_MAPPING(_edje_edd_edje_part_description_variant_list, "vector", vector); EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Pack_Element); _edje_edd_edje_pack_element = diff --git a/src/lib/edje/edje_edit.c b/src/lib/edje/edje_edit.c index 2fa00ed..bdb96a5 100644 --- a/src/lib/edje/edje_edit.c +++ b/src/lib/edje/edje_edit.c @@ -3103,6 +3103,12 @@ _edje_edit_real_part_add(Evas_Object *obj, const char *name, Edje_Part_Type type if (ep->type == EDJE_PART_TYPE_RECTANGLE) rp->object = evas_object_rectangle_add(ed->base->evas); + else if (ep->type == EDJE_PART_TYPE_VECTOR) + { + rp->type = EDJE_PART_TYPE_VECTOR; + rp->typedata.vector = calloc(1, sizeof(Edje_Real_Part_Vector)); + rp->object = evas_object_vg_add(ed->base->evas); + } else if (ep->type == EDJE_PART_TYPE_IMAGE || ep->type == EDJE_PART_TYPE_PROXY) rp->object = evas_object_image_add(ed->base->evas); else if (ep->type == EDJE_PART_TYPE_TEXT) @@ -5491,6 +5497,7 @@ case EDJE_PART_TYPE_##Short: \ EDIT_ALLOC_POOL(BOX, Box, box); EDIT_ALLOC_POOL(TABLE, Table, table); EDIT_ALLOC_POOL(EXTERNAL, External, external_params); + EDIT_ALLOC_POOL(VECTOR, Vector, vector); } return pd; diff --git a/src/lib/edje/edje_load.c b/src/lib/edje/edje_load.c index 0a04ec2..9ada9c0 100644 --- a/src/lib/edje/edje_load.c +++ b/src/lib/edje/edje_load.c @@ -670,6 +670,12 @@ _edje_object_file_set_internal(Evas_Object *obj, const Eina_File *file, const ch if (!rp->typedata.text) memerr = EINA_TRUE; break; + case EDJE_PART_TYPE_VECTOR: + rp->type = EDJE_PART_TYPE_VECTOR; + rp->typedata.vector = calloc(1, sizeof(Edje_Real_Part_Vector)); + if (!rp->typedata.vector) memerr = EINA_TRUE; + break; + case EDJE_PART_TYPE_GROUP: case EDJE_PART_TYPE_SWALLOW: case EDJE_PART_TYPE_EXTERNAL: @@ -715,6 +721,10 @@ _edje_object_file_set_internal(Evas_Object *obj, const Eina_File *file, const ch rp->object = evas_object_rectangle_add(ed->base->evas); break; + case EDJE_PART_TYPE_VECTOR: + rp->object = evas_object_vg_add(ed->base->evas); + break; + case EDJE_PART_TYPE_PROXY: case EDJE_PART_TYPE_IMAGE: rp->object = evas_object_image_add(ed->base->evas); @@ -1475,7 +1485,6 @@ _edje_file_del(Edje *ed, Eina_Bool reuse_ic) _edje_message_del(ed); _edje_block_violate(ed); _edje_var_shutdown(ed); - if (!((ed->file) && (ed->collection))) { if (tev) @@ -2225,3 +2234,6 @@ _cb_signal_repeat(void *data, Evas_Object *obj, const char *sig, const char *sou EDJE_MESSAGE_SIGNAL, 0, &emsg); } + + + diff --git a/src/lib/edje/edje_lua.c b/src/lib/edje/edje_lua.c index 9253dbe..63021a2 100644 --- a/src/lib/edje/edje_lua.c +++ b/src/lib/edje/edje_lua.c @@ -4982,6 +4982,7 @@ _edje_lua_open(lua_State *L) _edje_lua_new_const(L, "PART_TYPE_GRADIENT", EDJE_PART_TYPE_GRADIENT); _edje_lua_new_const(L, "PART_TYPE_GROUP", EDJE_PART_TYPE_GROUP); _edje_lua_new_const(L, "PART_TYPE_BOX", EDJE_PART_TYPE_BOX); + _edje_lua_new_const(L, "PART_TYPE_VECTOR", EDJE_PART_TYPE_VECTOR); _edje_lua_new_const(L, "TEXT_EFFECT_NONE", EDJE_TEXT_EFFECT_NONE); _edje_lua_new_const(L, "TEXT_EFFECT_PLAIN", EDJE_TEXT_EFFECT_PLAIN); diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h index 2424f58..0b8c613 100644 --- a/src/lib/edje/edje_private.h +++ b/src/lib/edje/edje_private.h @@ -333,6 +333,7 @@ typedef struct _Edje_Mo Edje_Mo; typedef struct _Edje_Mo_Directory Edje_Mo_Directory; typedef struct _Edje_Gfx_Filter Edje_Gfx_Filter; typedef struct _Edje_Gfx_Filter_Directory Edje_Gfx_Filter_Directory; +typedef struct _Edje_Vector_Directory_Entry Edje_Vector_Directory_Entry; typedef struct _Edje_Vibration_Sample Edje_Vibration_Sample; typedef struct _Edje_Vibration_Directory Edje_Vibration_Directory; @@ -373,6 +374,10 @@ typedef struct _Edje_Physics_Face Edje_Physics_Face; typedef struct _Edje_Patterns Edje_Patterns; typedef struct _Edje_Part_Box_Animation Edje_Part_Box_Animation; typedef struct _Edje_Part_Limit Edje_Part_Limit; +typedef struct _Edje_Part_Description_Vector Edje_Part_Description_Vector; +typedef struct _Edje_Part_Description_Spec_Svg Edje_Part_Description_Spec_Svg; +typedef struct _Edje_Real_Part_Vector Edje_Real_Part_Vector; +typedef struct _Edje_Vector_Data Edje_Vector_Data; typedef struct _Edje Edje; typedef struct _Edje_Real_Part_Text Edje_Real_Part_Text; @@ -630,7 +635,15 @@ struct _Edje_Image_Directory unsigned int entries_count; Edje_Image_Directory_Set *sets; /* an array of Edje_Image_Directory_Set */ + Edje_Vector_Directory_Entry *vectors; /* an array of Edje_Image_Directory_Entry */ unsigned int sets_count; + unsigned int vectors_count; +}; + +struct _Edje_Vector_Directory_Entry +{ + const char *entry; /* the nominal name of the vector image - if any */ + int id; /* the id no. of the image */ }; struct _Edje_Image_Directory_Entry @@ -865,7 +878,8 @@ struct _Edje_Limit TYPE MESH_NODE; \ TYPE LIGHT; \ TYPE CAMERA; \ - TYPE SNAPSHOT; + TYPE SNAPSHOT; \ + TYPE VECTOR; struct _Edje_Part_Collection_Directory_Entry { @@ -1518,6 +1532,12 @@ struct _Edje_Part_Description_Spec_Camera } orientation; }; +struct _Edje_Part_Description_Spec_Svg +{ + int id; /* the svg id to use */ + Eina_Bool set; /* if vg condition it's content */ +}; + struct _Edje_Part_Description_Image { Edje_Part_Description_Common common; @@ -1581,6 +1601,12 @@ struct _Edje_Part_Description_Camera Edje_Part_Description_Spec_Camera camera; }; +struct _Edje_Part_Description_Vector +{ + Edje_Part_Description_Common common; + Edje_Part_Description_Spec_Svg vg; +}; + /*----------*/ struct _Edje_Signal_Source_Char @@ -1620,6 +1646,7 @@ struct _Edje Eina_List *subobjs; Eina_List *text_insert_filter_callbacks; Eina_List *markup_filter_callbacks; + Eina_List *vector_cache; /* list of Edje_Vector_Data */ Eina_List *groups; @@ -1896,6 +1923,19 @@ struct _Edje_Real_Part_Swallow } swallow_params; // 28 // FIXME: only if type SWALLOW }; +struct _Edje_Vector_Data +{ + int svg_id; + double x, y, w, h; + Eina_Bool preserve_aspect; + Efl_VG *vg; +}; + +struct _Edje_Real_Part_Vector +{ + Edje_Vector_Data cur; +}; + struct _Edje_Real_Part { Edje_Real_Part_State param1; // 32 @@ -1918,6 +1958,7 @@ struct _Edje_Real_Part Edje_Real_Part_Text *text; Edje_Real_Part_Container *container; Edje_Real_Part_Swallow *swallow; + Edje_Real_Part_Vector *vector; } typedata; // 4 FLOAT_T description_pos; // 8 Edje_Rectangle req; // 16 @@ -2285,6 +2326,7 @@ EAPI extern Eina_Mempool *_emp_LIGHT; EAPI extern Eina_Mempool *_emp_CAMERA; EAPI extern Eina_Mempool *_emp_SNAPSHOT; EAPI extern Eina_Mempool *_emp_part; +EAPI extern Eina_Mempool *_emp_VECTOR; void _edje_part_pos_set(Edje *ed, Edje_Real_Part *ep, int mode, FLOAT_T pos, FLOAT_T v1, FLOAT_T v2, FLOAT_T v3, FLOAT_T v4); diff --git a/src/lib/evas/Evas_Legacy.h b/src/lib/evas/Evas_Legacy.h index 2100b64..9743636 100644 --- a/src/lib/evas/Evas_Legacy.h +++ b/src/lib/evas/Evas_Legacy.h @@ -1792,6 +1792,8 @@ EAPI Evas_Object *evas_object_rectangle_add(Evas *e) EINA_WARN_UNUSED_RESULT EIN */ EAPI Evas_Object *evas_object_vg_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; +EAPI void evas_object_vg_path_set(Evas_Object *vg, const char *path, int src_vg, int dest_vg, float pos) EINA_ARG_NONNULL(1); + #include "canvas/evas_vg.eo.legacy.h" /** diff --git a/src/lib/evas/canvas/evas_main.c b/src/lib/evas/canvas/evas_main.c index e5b184d..90df47e 100644 --- a/src/lib/evas/canvas/evas_main.c +++ b/src/lib/evas/canvas/evas_main.c @@ -5,6 +5,8 @@ #include "evas_cs2_private.h" #endif +#include "evas_vg_cache.h" + #define MY_CLASS EVAS_CANVAS_CLASS #ifdef LKDEBUG @@ -63,6 +65,7 @@ evas_init(void) #endif _evas_preload_thread_init(); evas_filter_init(); + evas_cache_svg_init(); evas_thread_init(); @@ -104,6 +107,7 @@ evas_shutdown(void) if (evas_cserve2_use_get()) evas_cserve2_shutdown(); #endif + evas_cache_svg_shutdown(); evas_font_path_global_clear(); eina_cow_del(evas_object_proxy_cow); diff --git a/src/lib/evas/canvas/evas_object_vg.c b/src/lib/evas/canvas/evas_object_vg.c index 6fcca7b..84de3c0 100644 --- a/src/lib/evas/canvas/evas_object_vg.c +++ b/src/lib/evas/canvas/evas_object_vg.c @@ -4,6 +4,8 @@ #include "evas_vg_private.h" #include "efl_vg_root_node.eo.h" +#include "evas_vg_cache.h" + #define MY_CLASS EVAS_VG_CLASS /* private magic number for rectangle objects */ @@ -18,7 +20,7 @@ struct _Evas_VG_Data { void *engine_data; Efl_VG *root; - + Svg_Entry *svg; Eina_Rectangle fill; unsigned int width, height; @@ -90,6 +92,29 @@ evas_object_vg_add(Evas *e) return eo_obj; } +EAPI void +evas_object_vg_path_set(Eo *obj, const char *path, int src_vg, + int dest_vg, float pos) +{ + int w, h; + Evas_VG_Data *pd; + Svg_Entry *entry; + + if (!obj) return ; + + evas_object_geometry_get(obj, NULL, NULL, &w, &h); + pd = eo_data_scope_get(obj, MY_CLASS); + entry = evas_cache_svg_find(path, src_vg, dest_vg, pos, w, h); + if (entry != pd->svg) + { + if (pd->svg) + { + evas_cache_svg_entry_del(pd->svg); + } + pd->svg = entry; + } +} + Efl_VG * _evas_vg_root_node_get(Eo *obj EINA_UNUSED, Evas_VG_Data *pd) { @@ -194,6 +219,68 @@ _evas_vg_render(Evas_Object_Protected_Data *obj, Evas_VG_Data *vd, } static void +_svg_data_render(Evas_Object_Protected_Data *obj, + Evas_VG_Data *vd, + void *output, void *context, void *surface, + int x, int y, Eina_Bool do_async) +{ + Svg_Entry *svg = vd->svg; + Efl_VG *root; + void *buffer; + Ector_Surface *ector; + RGBA_Draw_Context *ct; + + // if the size changed in between path set and the draw call; + if (!(svg->w == obj->cur->geometry.w && + svg->h == obj->cur->geometry.h)) + { + evas_cache_svg_entry_del(svg); + svg = evas_cache_svg_find(svg->file, svg->src_vg, svg->dest_vg, + svg->key_frame, obj->cur->geometry.w, obj->cur->geometry.h); + vd->svg = svg; + } + // if the buffer is not created yet + buffer = obj->layer->evas->engine.func->ector_surface_cache_get(output, svg->key); + if (!buffer) + { + root = evas_cache_svg_vg_tree_get(svg); + if (!root) return; + // manual render the vg tree + ector = evas_ector_get(obj->layer->evas); + if (!ector) return; + //1. render pre + _evas_vg_render_pre(root, ector, NULL); + // 2. create surface + buffer = obj->layer->evas->engine.func->ector_surface_create(output, + NULL, + svg->w, + svg->h); + //3. draw into the buffer + ct = evas_common_draw_context_new(); + evas_common_draw_context_set_render_op(ct, _EVAS_RENDER_COPY); + evas_common_draw_context_set_color(ct, 255, 255, 255, 255); + obj->layer->evas->engine.func->ector_begin(output, ct, + ector, buffer, + 0, 0, + do_async); + _evas_vg_render(obj, vd, + output, ct, buffer, + root, NULL, + do_async); + obj->layer->evas->engine.func->ector_end(output, ct, ector, buffer, do_async); + + obj->layer->evas->engine.func->ector_surface_cache_set(output, svg->key, buffer); + evas_common_draw_context_free(ct); + } + // draw the buffer as image to canvas + obj->layer->evas->engine.func->image_draw(output, context, surface, + buffer, 0, 0, + obj->cur->geometry.w, obj->cur->geometry.h, obj->cur->geometry.x + x, + obj->cur->geometry.y + y, obj->cur->geometry.w, obj->cur->geometry.h, + EINA_TRUE, do_async); +} + +static void evas_object_vg_render(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, void *type_private_data, @@ -202,11 +289,7 @@ evas_object_vg_render(Evas_Object *eo_obj EINA_UNUSED, { Evas_VG_Data *vd = type_private_data; Ector_Surface *ector = evas_ector_get(obj->layer->evas); - if (vd->content_changed || !vd->backing_store) - vd->backing_store = obj->layer->evas->engine.func->ector_surface_create(output, - vd->backing_store, - obj->cur->geometry.w, - obj->cur->geometry.h); + // FIXME: Set context (that should affect Ector_Surface) and // then call Ector_Renderer render from bottom to top. Get the // Ector_Surface that match the output from Evas engine API. @@ -231,6 +314,20 @@ evas_object_vg_render(Evas_Object *eo_obj EINA_UNUSED, obj->cur->anti_alias); obj->layer->evas->engine.func->context_render_op_set(output, context, obj->cur->render_op); + + if (vd->svg) + { + _svg_data_render(obj, vd, output, + context, surface, + x, y, do_async); + return; + } + + if (vd->content_changed || !vd->backing_store) + vd->backing_store = obj->layer->evas->engine.func->ector_surface_create(output, + vd->backing_store, + obj->cur->geometry.w, + obj->cur->geometry.h); if (!vd->backing_store) { obj->layer->evas->engine.func->ector_begin(output, context, ector, surface, diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index b925b7a..97597f2 100755 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -527,6 +527,15 @@ typedef enum _Evas_Font_Weight Evas_Font_Weight; typedef enum _Evas_Font_Width Evas_Font_Width; typedef enum _Evas_Font_Spacing Evas_Font_Spacing; +// ector cache +typedef struct _Ector_Surface_Cache Ector_Surface_Cache; + +struct _Ector_Surface_Cache +{ + Eina_Hash *suface_hash; + void *output; +}; + /* General types - used for script type chceking */ #define OPAQUE_TYPE(type) struct __##type { int a; }; \ typedef struct __##type type @@ -1463,6 +1472,8 @@ struct _Evas_Func void (*ector_renderer_draw) (void *data, void *context, void *surface, Ector_Renderer *r, Eina_Array *clips, Eina_Bool do_async); void (*ector_end) (void *data, void *context, Ector_Surface *ector, void *surface, Eina_Bool do_async); void *(*ector_surface_create) (void *data, void *surface, int w, int h); + void (*ector_surface_cache_set) (void *data, const char *key, void *surface); + void *(*ector_surface_cache_get) (void *data, const char *key); }; struct _Evas_Image_Save_Func diff --git a/src/lib/evas/vg/evas_vg_cache.c b/src/lib/evas/vg/evas_vg_cache.c new file mode 100644 index 0000000..e84a738 --- /dev/null +++ b/src/lib/evas/vg/evas_vg_cache.c @@ -0,0 +1,146 @@ +#include "evas_vg_cache.h" +#include "evas_vg_common.h" + +static Evas_Cache_Svg* svg_cache = NULL; + +static void +_evas_cache_vg_data_free_cb(void *data) +{ + Vg_Data *val = data; + + eo_del(val->vg); + free(val); +} + +static void +_evas_cache_svg_entry_free_cb(void *data) +{ + Svg_Entry *entry = data; + + eina_stringshare_del(entry->file); + free(entry->key); + eo_del(entry->root); + free(entry); +} + +void +evas_cache_svg_init(void) +{ + if (svg_cache) + { + svg_cache->ref++; + return; + } + + svg_cache = calloc(1, sizeof(Evas_Cache_Svg)); + svg_cache->vg_hash = eina_hash_string_superfast_new(_evas_cache_vg_data_free_cb); + svg_cache->active = eina_hash_string_superfast_new(_evas_cache_svg_entry_free_cb); + svg_cache->ref++; +} + +void +evas_cache_svg_shutdown(void) +{ + if (!svg_cache) return; + svg_cache->ref--; + if (svg_cache->ref) return; + _evas_vg_svg_node_eet_destroy(); + eina_hash_free(svg_cache->vg_hash); + eina_hash_free(svg_cache->active); + svg_cache = NULL; +} + +static Vg_Data * +_evas_cache_vg_data_find(const char *path, int vg_id) +{ + Vg_Data *vd; + Eina_Strbuf *key; + + key = eina_strbuf_new(); + eina_strbuf_append_printf(key, "%s/%d", path, vg_id); + vd = eina_hash_find(svg_cache->vg_hash, eina_strbuf_string_get(key)); + if (!vd) + { + vd = _evas_vg_load_vg_data(path, vg_id); + eina_hash_add(svg_cache->vg_hash, eina_strbuf_string_get(key), vd); + } + eina_strbuf_free(key); + return vd; +} + +static void +_evas_cache_svg_vg_tree_update(Svg_Entry *entry) +{ + Vg_Data *src_vg = NULL, *dst_vg = NULL; + if(!entry) return; + + if (!entry->src_vg) + { + entry->root = NULL; + return; + } + + if (!entry->dest_vg) + { + src_vg = _evas_cache_vg_data_find(entry->file, entry->src_vg); + } + else + { + dst_vg = _evas_cache_vg_data_find(entry->file, entry->dest_vg); + } + entry->root = _evas_vg_dup_vg_tree(src_vg, dst_vg, entry->key_frame, entry->w, entry->h); + eina_stringshare_del(entry->file); + entry->file = NULL; +} + +Svg_Entry* +evas_cache_svg_find(const char *path, + int src_vg, int dest_vg, + float key_frame, int w, int h) +{ + Svg_Entry* se; + Eina_Strbuf *key; + + if (!svg_cache) return NULL; + + key = eina_strbuf_new(); + eina_strbuf_append_printf(key, "%s/%d/%d/%.2f/%d/%d", + path, src_vg, dest_vg, key_frame, w, h); + se = eina_hash_find(svg_cache->active, eina_strbuf_string_get(key)); + if (!se) + { + se = calloc(1, sizeof(Svg_Entry)); + se->file = eina_stringshare_add(path); + se->key_frame = key_frame; + se->src_vg = src_vg; + se->dest_vg = dest_vg; + se->w = w; + se->h = h; + se->key = eina_strbuf_string_steal(key); + eina_hash_direct_add(svg_cache->active, se->key, se); + } + eina_strbuf_free(key); + se->ref++; + return se; +} + +Efl_VG* +evas_cache_svg_vg_tree_get(Svg_Entry *entry) +{ + if (entry->root) return entry->root; + + if (entry->file) + { + _evas_cache_svg_vg_tree_update(entry); + } + return entry->root; +} + +void +evas_cache_svg_entry_del(Svg_Entry *svg_entry) +{ + if (!svg_entry) return; + + svg_entry->ref--; +} + diff --git a/src/lib/evas/vg/evas_vg_cache.h b/src/lib/evas/vg/evas_vg_cache.h new file mode 100644 index 0000000..2d41cac --- /dev/null +++ b/src/lib/evas/vg/evas_vg_cache.h @@ -0,0 +1,37 @@ +#ifndef _EVAS_CACHE_SVG_H +#define _EVAS_CACHE_SVG_H + +#include + +typedef struct _Svg_Entry Svg_Entry; +typedef struct _Evas_Cache_Svg Evas_Cache_Svg; + +struct _Evas_Cache_Svg +{ + Eina_Hash *vg_hash; + Eina_Hash *active; + int ref; +}; + +struct _Svg_Entry +{ + Eina_Stringshare *file; + char *key; + int src_vg; + int dest_vg; + float key_frame; + int w; + int h; + Efl_VG *root; + int ref; +}; + +void evas_cache_svg_init(void); +void evas_cache_svg_shutdown(void); +Svg_Entry* evas_cache_svg_find(const char *path, + int src_svg, int dest_svg, + float key_frame, int w, int h); +Efl_VG* evas_cache_svg_vg_tree_get(Svg_Entry *svg_entry); +void evas_cache_svg_entry_del(Svg_Entry *svg_entry); + +#endif /* _EVAS_CACHE_SVG_H */ \ No newline at end of file diff --git a/src/lib/evas/vg/evas_vg_common.h b/src/lib/evas/vg/evas_vg_common.h new file mode 100644 index 0000000..8052e4f --- /dev/null +++ b/src/lib/evas/vg/evas_vg_common.h @@ -0,0 +1,317 @@ +#ifndef EVAS_VG_COMMON_H_ +# define EVAS_VG_COMMON_H_ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + + + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_EVAS_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVAS_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +// Svg Node +typedef enum _Svg_Node_Type Svg_Node_Type; +typedef enum _Svg_Length_Type Svg_Length_Type; + +typedef struct _Svg_Node Svg_Node; +typedef struct _Svg_Doc_Node Svg_Doc_Node; +typedef struct _Svg_G_Node Svg_G_Node; +typedef struct _Svg_Defs_Node Svg_Defs_Node; +typedef struct _Svg_Arc_Node Svg_Arc_Node; +typedef struct _Svg_Circle_Node Svg_Circle_Node; +typedef struct _Svg_Ellipse_Node Svg_Ellipse_Node; +typedef struct _Svg_Polygon_Node Svg_Polygon_Node; +typedef struct _Svg_Rect_Node Svg_Rect_Node; +typedef struct _Svg_Path_Node Svg_Path_Node; +typedef struct _Svg_Style_Property Svg_Style_Property; + +typedef struct _Svg_Style_Stroke Svg_Style_Stroke; +typedef struct _Svg_Style_Fill Svg_Style_Fill; +typedef enum _Svg_Fill_Flags Svg_Fill_Flags; +typedef enum _Svg_Stroke_Flags Svg_Stroke_Flags; + + +typedef enum _Svg_Gradient_Type Svg_Gradient_Type; +typedef struct _Svg_Style_Gradient Svg_Style_Gradient; +typedef struct _Svg_Linear_Gradient Svg_Linear_Gradient; +typedef struct _Svg_Radial_Gradient Svg_Radial_Gradient; +typedef struct _Svg_Paint Svg_Paint; + +typedef struct _Vg_Data Vg_Data; + +enum _Svg_Node_Type +{ + SVG_NODE_DOC, + SVG_NODE_G, + SVG_NODE_DEFS, + SVG_NODE_SWITCH, + SVG_NODE_ANIMATION, + SVG_NODE_ARC, + SVG_NODE_CIRCLE, + SVG_NODE_ELLIPSE, + SVG_NODE_IMAGE, + SVG_NODE_LINE, + SVG_NODE_PATH, + SVG_NODE_POLYGON, + SVG_NODE_POLYLINE, + SVG_NODE_RECT, + SVG_NODE_TEXT, + SVG_NODE_TEXTAREA, + SVG_NODE_TSPAN, + SVG_NODE_USE, + SVG_NODE_VIDEO, + SVG_NODE_UNKNOWN +}; + +enum _Svg_Length_Type +{ + SVG_LT_PERCENT, + SVG_LT_PX, + SVG_LT_PC, + SVG_LT_PT, + SVG_LT_MM, + SVG_LT_CM, + SVG_LT_IN, +}; + +struct _Svg_Doc_Node +{ + double width; + double height; + double vx; + double vy; + double vw; + double vh; + Svg_Node *defs; +}; + +struct _Svg_G_Node +{ + +}; + +struct _Svg_Defs_Node +{ + Eina_List *gradients; +}; + +struct _Svg_Arc_Node +{ + +}; + +struct _Svg_Ellipse_Node +{ + double cx; + double cy; + double rx; + double ry; +}; + +struct _Svg_Circle_Node +{ + double cx; + double cy; + double r; +}; + +struct _Svg_Rect_Node +{ + double x; + double y; + double w; + double h; + double rx; + double ry; +}; + +struct _Svg_Path_Node +{ + Eina_Stringshare *path; +}; + +struct _Svg_Polygon_Node +{ + int points_count; + double *points; +}; + + +enum _Svg_Gradient_Type +{ + SVG_LINEAR_GRADIENT, + SVG_RADIAL_GRADIENT +}; +struct _Svg_Linear_Gradient +{ + double x1; + double y1; + double x2; + double y2; +}; + +struct _Svg_Radial_Gradient +{ + double cx; + double cy; + double fx; + double fy; + double r; +}; + +struct _Svg_Style_Gradient +{ + Svg_Gradient_Type type; + Eina_Stringshare *id; + Eina_Stringshare *ref; + Efl_Gfx_Gradient_Spread spread; + Eina_List *stops; // Efl_Gfx_Gradient_Stop + Svg_Radial_Gradient *radial; + Svg_Linear_Gradient *linear; +}; + +struct _Svg_Paint +{ + int r; + int g; + int b; + Eina_Bool none; + Eina_Bool cur_color; + Svg_Style_Gradient *gradient; + Eina_Stringshare *url; +}; + +enum _Svg_Fill_Flags +{ + SVG_FILL_FLAGS_PAINT = 0x1, + SVG_FILL_FLAGS_OPACITY = 0x2, + SVG_FILL_FLAGS_GRADIENT = 0x4, + SVG_FILL_FLAGS_FILL_RULE = 0x8 +}; + +enum _Svg_Stroke_Flags +{ + SVG_STROKE_FLAGS_PAINT = 0x1, + SVG_STROKE_FLAGS_OPACITY = 0x2, + SVG_STROKE_FLAGS_GRADIENT = 0x4, + SVG_STROKE_FLAGS_SCALE = 0x8, + SVG_STROKE_FLAGS_WIDTH = 0x10, + SVG_STROKE_FLAGS_CAP = 0x20, + SVG_STROKE_FLAGS_JOIN = 0x40, + SVG_STROKE_FLAGS_DASH = 0x80, +}; + +struct _Svg_Style_Fill +{ + Svg_Fill_Flags flags; + Svg_Paint paint; + int opacity; + Efl_Gfx_Fill_Rule fill_rule; +}; + +struct _Svg_Style_Stroke +{ + Svg_Stroke_Flags flags; + Svg_Paint paint; + int opacity; + double scale; + double width; + double centered; + Efl_Gfx_Cap cap; + Efl_Gfx_Join join; + Efl_Gfx_Dash *dash; + int dash_count; +}; + +struct _Svg_Style_Property +{ + Svg_Style_Fill fill; + Svg_Style_Stroke stroke; + // the color property indirectly + // used by fill and stroke + int r; + int g; + int b; +}; + +struct _Svg_Node +{ + Svg_Node_Type type; + Svg_Node *parent; + Eina_List *child; + Eina_Stringshare *id; + Svg_Style_Property *style; + Eina_Matrix3 *transform; + union + { + Svg_G_Node g; + Svg_Doc_Node doc; + Svg_Defs_Node defs; + Svg_Arc_Node arc; + Svg_Circle_Node circle; + Svg_Ellipse_Node ellipse; + Svg_Polygon_Node polygon; + Svg_Rect_Node rect; + Svg_Path_Node path; + }node; +}; + +enum _Svg_Style_Type +{ + SVG_STYLE_QUALITY, + SVG_STYLE_FILL, + SVG_STYLE_VIEWPORT_FILL, + SVG_STYLE_FONT, + SVG_STYLE_STROKE, + SVG_STYLE_SOLID_COLOR, + SVG_STYLE_GRADIENT, + SVG_STYLE_TRANSFORM, + SVG_STYLE_OPACITY, + SVG_STYLE_COMP_OP +}; + +struct _Vg_Data +{ + int svg_id; + double x, y, w, h; + Eina_Bool preserve_aspect; + Efl_VG *vg; +}; + +EAPI Eet_Data_Descriptor * _evas_vg_svg_node_eet(void); +EAPI void _evas_vg_svg_node_eet_destroy(void); +EAPI void _evas_vg_svg_node_free(Svg_Node *node); +Vg_Data * _evas_vg_load_vg_data(Eina_Stringshare *path, int svg_id); +Efl_VG * _evas_vg_dup_vg_tree(Vg_Data *src, Vg_Data *dst, float pos, double w, double h); + +#undef EAPI +#define EAPI + +#endif //EVAS_VG_COMMON_H_ \ No newline at end of file diff --git a/src/lib/evas/vg/evas_vg_eet_handler.c b/src/lib/evas/vg/evas_vg_eet_handler.c new file mode 100644 index 0000000..df9ec5f --- /dev/null +++ b/src/lib/evas/vg/evas_vg_eet_handler.c @@ -0,0 +1,789 @@ +#include "evas_vg_common.h" + +Eet_Data_Descriptor *_edje_edd_edje_rect_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_circle_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_ellipse_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_gradient_stops_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_linear_gradient_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_radial_gradient_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_style_gradient_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_style_property_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_matrix3_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_doc_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_defs_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_g_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_arc_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_path_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_polygon_node = NULL; +Eet_Data_Descriptor *_edje_edd_edje_vg_node = NULL; + +#define FREE_DESCRIPTOR(eed) \ + if (eed) \ + { \ + eet_data_descriptor_free((eed)); \ + (eed) = NULL; \ + } + +static inline Eet_Data_Descriptor* +_eet_for_rect_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Rect_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "x", x, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "y", y, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "w", w, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "h", h, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "rx", rx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Rect_Node, "ry", ry, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_circle_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Circle_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Circle_Node, "cx", cx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Circle_Node, "cy", cy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Circle_Node, "r", r, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_ellipse_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Ellipse_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Ellipse_Node, "cx", cx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Ellipse_Node, "cy", cy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Ellipse_Node, "rx", rx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Ellipse_Node, "ry", ry, EET_T_DOUBLE); + return eet; +} + + +static inline Eet_Data_Descriptor* +_eet_for_gradient_stops(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Efl_Gfx_Gradient_Stop); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Efl_Gfx_Gradient_Stop, "offset", offset, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Efl_Gfx_Gradient_Stop, "r", r, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Efl_Gfx_Gradient_Stop, "g", g, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Efl_Gfx_Gradient_Stop, "b", b, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Efl_Gfx_Gradient_Stop, "a", a, EET_T_INT); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_linear_gradient(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Linear_Gradient); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Linear_Gradient, "x1", x1, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Linear_Gradient, "y1", y1, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Linear_Gradient, "x2", x2, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Linear_Gradient, "y2", y2, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_radial_gradient(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Radial_Gradient); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Radial_Gradient, "cx", cx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Radial_Gradient, "cy", cy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Radial_Gradient, "fx", fx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Radial_Gradient, "fy", fy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Radial_Gradient, "r", r, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_style_gradient(void) +{ + Eet_Data_Descriptor_Class eetc; + + if (_edje_edd_edje_style_gradient_node) return _edje_edd_edje_style_gradient_node; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Style_Gradient); + _edje_edd_edje_style_gradient_node = eet_data_descriptor_stream_new(&eetc); + _edje_edd_edje_gradient_stops_node = _eet_for_gradient_stops(); + _edje_edd_edje_linear_gradient_node = _eet_for_linear_gradient(); + _edje_edd_edje_radial_gradient_node = _eet_for_radial_gradient(); + + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "type", type, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "id", id, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "spread", spread, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_LIST(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "stops", stops, _edje_edd_edje_gradient_stops_node); + EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "radial", radial, _edje_edd_edje_radial_gradient_node); + EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_style_gradient_node, Svg_Style_Gradient, "linear", linear, _edje_edd_edje_linear_gradient_node); + + return _edje_edd_edje_style_gradient_node; +} + +static inline Eet_Data_Descriptor* +_eet_for_style_property(void) +{ + Eet_Data_Descriptor *eet, *eet_gradient, *eet_dash; + Eet_Data_Descriptor_Class eetc, eetc_dash; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Style_Property); + eet = eet_data_descriptor_stream_new(&eetc); + eet_gradient = _eet_for_style_gradient(); + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc_dash, Efl_Gfx_Dash); + eet_dash = eet_data_descriptor_stream_new(&eetc_dash); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet_dash, Efl_Gfx_Dash, "length", length, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet_dash, Efl_Gfx_Dash, "gap", gap, EET_T_DOUBLE); + + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "r", r, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "g", g, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "b", b, EET_T_INT); + // for fill + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.flags", fill.flags, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.r", fill.paint.r, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.g", fill.paint.g, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.b", fill.paint.b, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.none", fill.paint.none, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.cur_color", fill.paint.cur_color, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_SUB(eet, Svg_Style_Property, "fill.paint.gradient", fill.paint.gradient, eet_gradient); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.paint.url", fill.paint.url, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.opacity", fill.opacity, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "fill.fill_rule", fill.fill_rule, EET_T_INT); + + // for stroke + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.flags", stroke.flags, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.r", stroke.paint.r, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.g", stroke.paint.g, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.b", stroke.paint.b, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.none", stroke.paint.none, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.cur_color", stroke.paint.cur_color, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_SUB(eet, Svg_Style_Property, "stroke.paint.gradient", stroke.paint.gradient, eet_gradient); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.paint.url", stroke.paint.url, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.opacity", stroke.opacity, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.scale", stroke.scale, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.width", stroke.width, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.centered", stroke.centered, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.cap", stroke.cap, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.join", stroke.join, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(eet, Svg_Style_Property, "stroke.dash", stroke.dash, eet_dash); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Style_Property, "stroke.dash_count", stroke.dash_count, EET_T_INT); + + return eet; +} + +static Eet_Data_Descriptor* +_eet_for_eina_matrix3(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Eina_Matrix3); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "xx", xx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "xy", xy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "xz", xz, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "yx", yx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "yy", yy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "yz", yz, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "zx", zx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "zy", zy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Eina_Matrix3, "zz", zz, EET_T_DOUBLE); + + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_doc_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Doc_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "width", width, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "height", height, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "vx", vx, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "vy", vy, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "vw", vw, EET_T_DOUBLE); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Doc_Node, "vh", vh, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_defs_node(void) +{ + Eet_Data_Descriptor *eet, *eet_gradient; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Defs_Node); + eet = eet_data_descriptor_stream_new(&eetc); + eet_gradient = _eet_for_style_gradient(); + + EET_DATA_DESCRIPTOR_ADD_LIST(eet, Svg_Defs_Node, "gradients", gradients, eet_gradient); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_g_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_G_Node); + eet = eet_data_descriptor_stream_new(&eetc); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_arc_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Arc_Node); + eet = eet_data_descriptor_stream_new(&eetc); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_polygon_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Polygon_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC_VAR_ARRAY(eet, Svg_Polygon_Node, "points", points, EET_T_DOUBLE); + return eet; +} + +static inline Eet_Data_Descriptor* +_eet_for_path_node(void) +{ + Eet_Data_Descriptor *eet; + Eet_Data_Descriptor_Class eetc; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Path_Node); + eet = eet_data_descriptor_stream_new(&eetc); + EET_DATA_DESCRIPTOR_ADD_BASIC(eet, Svg_Path_Node, "path", path, EET_T_STRING); + return eet; +} + +struct +{ + Svg_Node_Type u; + const char *name; +} eet_mapping[] = { + { SVG_NODE_DOC, "doc" }, + { SVG_NODE_G, "g" }, + { SVG_NODE_DEFS, "defs" }, + { SVG_NODE_ARC, "arc" }, + { SVG_NODE_CIRCLE, "circle" }, + { SVG_NODE_ELLIPSE, "ellipse" }, + { SVG_NODE_POLYGON, "polygon" }, + { SVG_NODE_POLYLINE, "polygon" }, + { SVG_NODE_RECT, "rect" }, + { SVG_NODE_PATH, "path" }, + { SVG_NODE_UNKNOWN, NULL } +}; + +static const char * +/* union + type_get() */ +_union_type_get(const void *data, + Eina_Bool *unknow) +{ + const Svg_Node_Type *u = data; + int i; + + if (unknow) + *unknow = EINA_FALSE; + + for (i = 0; eet_mapping[i].name != NULL; ++i) + if (*u == eet_mapping[i].u) + return eet_mapping[i].name; + + if (unknow) + *unknow = EINA_TRUE; + return NULL; +} /* _union_type_get */ + +static Eina_Bool +_union_type_set(const char *type, + void *data, + Eina_Bool unknow) +{ + Svg_Node_Type *u = data; + int i; + + if (unknow) + return EINA_FALSE; + + for (i = 0; eet_mapping[i].name != NULL; ++i) + if (strcmp(eet_mapping[i].name, type) == 0) + { + *u = eet_mapping[i].u; + return EINA_TRUE; + } + return EINA_FALSE; +} /* _union_type_set */ + +EAPI Eet_Data_Descriptor * +_evas_vg_svg_node_eet(void) +{ + Eet_Data_Descriptor *eet_union; + Eet_Data_Descriptor_Class eetc; + + if (_edje_edd_edje_vg_node) return _edje_edd_edje_vg_node; + + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eetc, Svg_Node); + _edje_edd_edje_vg_node = eet_data_descriptor_stream_new(&eetc); + + eetc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION; + eetc.func.type_get = _union_type_get; + eetc.func.type_set = _union_type_set; + eet_union = eet_data_descriptor_stream_new(&eetc); + + _edje_edd_edje_doc_node = _eet_for_doc_node(); + _edje_edd_edje_g_node = _eet_for_g_node(); + _edje_edd_edje_defs_node = _eet_for_defs_node(); + _edje_edd_edje_arc_node = _eet_for_arc_node(); + _edje_edd_edje_circle_node = _eet_for_circle_node(); + _edje_edd_edje_ellipse_node = _eet_for_ellipse_node(); + _edje_edd_edje_rect_node = _eet_for_rect_node(); + _edje_edd_edje_path_node = _eet_for_path_node(); + _edje_edd_edje_polygon_node = _eet_for_polygon_node(); + _edje_edd_edje_style_property_node = _eet_for_style_property(); + _edje_edd_edje_matrix3_node = _eet_for_eina_matrix3(); + + + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "doc", _edje_edd_edje_doc_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "g", _edje_edd_edje_g_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "defs", _edje_edd_edje_defs_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "arc", _edje_edd_edje_arc_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "circle", _edje_edd_edje_circle_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "ellipse", _edje_edd_edje_ellipse_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "rect", _edje_edd_edje_rect_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "path", _edje_edd_edje_path_node); + EET_DATA_DESCRIPTOR_ADD_MAPPING(eet_union, "polygon", _edje_edd_edje_polygon_node); + + EET_DATA_DESCRIPTOR_ADD_UNION(_edje_edd_edje_vg_node, Svg_Node, "node", node, type, eet_union); + + + EET_DATA_DESCRIPTOR_ADD_LIST(_edje_edd_edje_vg_node, Svg_Node, "child", child, _edje_edd_edje_vg_node); + EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Svg_Node, "id", id, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_vg_node, Svg_Node, "style", style, _edje_edd_edje_style_property_node); + EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_vg_node, Svg_Node, "transform", transform, _edje_edd_edje_matrix3_node); + + return _edje_edd_edje_vg_node; +} + +EAPI void +_evas_vg_svg_node_eet_destroy(void) +{ + FREE_DESCRIPTOR(_edje_edd_edje_rect_node); + FREE_DESCRIPTOR(_edje_edd_edje_circle_node); + FREE_DESCRIPTOR(_edje_edd_edje_ellipse_node); + FREE_DESCRIPTOR(_edje_edd_edje_gradient_stops_node); + FREE_DESCRIPTOR(_edje_edd_edje_linear_gradient_node); + FREE_DESCRIPTOR(_edje_edd_edje_radial_gradient_node); + FREE_DESCRIPTOR(_edje_edd_edje_style_gradient_node); + FREE_DESCRIPTOR(_edje_edd_edje_style_property_node); + FREE_DESCRIPTOR(_edje_edd_edje_matrix3_node); + FREE_DESCRIPTOR(_edje_edd_edje_doc_node); + FREE_DESCRIPTOR(_edje_edd_edje_defs_node); + FREE_DESCRIPTOR(_edje_edd_edje_g_node); + FREE_DESCRIPTOR(_edje_edd_edje_arc_node); + FREE_DESCRIPTOR(_edje_edd_edje_path_node); + FREE_DESCRIPTOR(_edje_edd_edje_polygon_node); + FREE_DESCRIPTOR(_edje_edd_edje_vg_node); +} + + + +static Efl_VG * +_apply_gradient_property(Svg_Style_Gradient *g) +{ + Efl_VG *grad_obj = NULL; + Efl_Gfx_Gradient_Stop *stops, *stop; + int stop_count = 0, i = 0; + Eina_List *l; + + if (g->type == SVG_LINEAR_GRADIENT) + { + grad_obj = eo_add(EFL_VG_GRADIENT_LINEAR_CLASS, NULL); + evas_vg_gradient_linear_start_set(grad_obj, g->linear->x1, g->linear->y1); + evas_vg_gradient_linear_end_set(grad_obj, g->linear->x2, g->linear->y2); + } + else if (g->type == SVG_RADIAL_GRADIENT) + { + grad_obj = eo_add(EFL_VG_GRADIENT_RADIAL_CLASS, NULL); + evas_vg_gradient_radial_center_set(grad_obj, g->radial->cx, g->radial->cy); + evas_vg_gradient_radial_radius_set(grad_obj, g->radial->r); + evas_vg_gradient_radial_focal_set(grad_obj, g->radial->fx, g->radial->fy); + } + else + { + // not a known gradient + return NULL; + } + // apply common prperty + evas_vg_gradient_spread_set(grad_obj, g->spread); + // update the stops + stop_count = eina_list_count(g->stops); + if (stop_count) + { + stops = calloc(stop_count, sizeof(Efl_Gfx_Gradient_Stop)); + i = 0; + EINA_LIST_FOREACH(g->stops, l, stop) + { + stops[i].r = stop->r; + stops[i].g = stop->g; + stops[i].b = stop->b; + stops[i].a = stop->a; + stops[i].offset = stop->offset; + i++; + } + evas_vg_gradient_stop_set(grad_obj, stops, stop_count); + free(stops); + } + return grad_obj; +} + +// vg tree creation +static void +_apply_vg_property(Svg_Node *node, Efl_VG *vg) +{ + Svg_Style_Property *style = node->style; + + // update the vg name + if (node->id) + evas_vg_node_name_set(vg, node->id); + + // apply the transformation + if (node->transform) + evas_vg_node_transformation_set(vg, node->transform); + + if (node->type == SVG_NODE_G) return; + + // apply the fill style property + eo_do(vg, efl_gfx_shape_fill_rule_set(style->fill.fill_rule)); + // if fill property is NULL then do nothing + if (style->fill.paint.none) + { + //do nothing + } + else if (style->fill.paint.gradient) + { + // if the fill has gradient then apply. + evas_vg_shape_fill_set(vg, _apply_gradient_property(style->fill.paint.gradient)); + } + else if (style->fill.paint.cur_color) + { + // apply the current style color + evas_vg_node_color_set(vg, style->r, style->g, + style->b, style->fill.opacity); + } + else + { + // apply the fill color + evas_vg_node_color_set(vg, style->fill.paint.r, style->fill.paint.g, + style->fill.paint.b, style->fill.opacity); + } + + // apply the stroke style property + evas_vg_shape_stroke_width_set(vg, style->stroke.width); + evas_vg_shape_stroke_cap_set(vg, style->stroke.cap); + evas_vg_shape_stroke_join_set(vg, style->stroke.join); + evas_vg_shape_stroke_scale_set(vg, style->stroke.scale); + // if stroke property is NULL then do nothing + if (style->stroke.paint.none) + { + //do nothing + } + else if (style->stroke.paint.gradient) + { + // if the fill has gradient then apply. + evas_vg_shape_stroke_fill_set(vg, _apply_gradient_property(style->stroke.paint.gradient)); + } + else if (style->stroke.paint.url) + { + // apply the color pointed by url + // TODO + } + else if (style->stroke.paint.cur_color) + { + // apply the current style color + evas_vg_shape_stroke_color_set(vg, style->r, style->g, + style->b, style->stroke.opacity); + } + else + { + // apply the stroke color + evas_vg_shape_stroke_color_set(vg, style->stroke.paint.r, style->stroke.paint.g, + style->stroke.paint.b, style->stroke.opacity); + } +} + +static void +_add_polyline(Efl_VG *vg, double *array, int size, Eina_Bool polygon) +{ + int i; + + if (size < 2) return; + + evas_vg_shape_shape_append_move_to(vg, array[0], array[1]); + for (i=2; i < size; i+=2) + evas_vg_shape_shape_append_line_to(vg, array[i], array[i+1]); + + if (polygon) + evas_vg_shape_shape_append_close(vg); +} + +static void +_create_vg_node(Svg_Node *node, Efl_VG *parent) +{ + Efl_VG *vg = NULL; + Svg_Node *child; + Eina_List *l; + + switch (node->type) + { + case SVG_NODE_G: + { + vg = evas_vg_container_add(parent); + _apply_vg_property(node, vg); + EINA_LIST_FOREACH(node->child, l, child) + { + _create_vg_node(child, vg); + } + return; + } + break; + case SVG_NODE_PATH: + vg = evas_vg_shape_add(parent); + evas_vg_shape_shape_append_svg_path(vg, node->node.path.path); + break; + case SVG_NODE_POLYGON: + vg = evas_vg_shape_add(parent); + _add_polyline(vg, node->node.polygon.points, node->node.polygon.points_count, EINA_TRUE); + break; + case SVG_NODE_POLYLINE: + _add_polyline(vg, node->node.polygon.points, node->node.polygon.points_count, EINA_FALSE); + break; + case SVG_NODE_ELLIPSE: + vg = evas_vg_shape_add(parent); + evas_vg_shape_shape_append_arc(vg, node->node.ellipse.cx - node->node.ellipse.rx, + node->node.ellipse.cy - node->node.ellipse.ry, + 2*node->node.ellipse.rx, 2*node->node.ellipse.ry, 0, 360); + evas_vg_shape_shape_append_close(vg); + break; + case SVG_NODE_CIRCLE: + vg = evas_vg_shape_add(parent); + evas_vg_shape_shape_append_circle(vg, node->node.circle.cx, node->node.circle.cy, node->node.circle.r); + break; + case SVG_NODE_RECT: + vg = evas_vg_shape_add(parent); + evas_vg_shape_shape_append_rect(vg, node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, + node->node.rect.rx, node->node.rect.ry); + break; + default: + break; + } + _apply_vg_property(node, vg); +} + +Vg_Data * +_evas_vg_load_vg_data(Eina_Stringshare *path, int svg_id) +{ + Eina_List *l; + Vg_Data *vector; + char svg_key[20]; + Eet_Data_Descriptor *svg_node_eet; + Svg_Node *child; + Svg_Node *node; + Eet_File *ef; + Efl_VG *root = NULL; + + ef = eet_open(path, EET_FILE_MODE_READ); + if (!ef) return NULL; + + // create and put it in the cache. + vector = calloc(1, sizeof(Vg_Data)); + vector->svg_id = svg_id; + + snprintf(svg_key, sizeof(svg_key), "edje/vectors/%i", svg_id); + svg_node_eet = _evas_vg_svg_node_eet(); + node = eet_data_read(ef, svg_node_eet, svg_key); + + if (!node || (node->type != SVG_NODE_DOC)) + { + root = NULL; + } + else + { + root = evas_vg_container_add(NULL); + EINA_LIST_FOREACH(node->child, l, child) + { + _create_vg_node(child, root); + } + vector->x = node->node.doc.vx; + vector->y = node->node.doc.vy; + vector->w = node->node.doc.vw; + vector->h = node->node.doc.vh; + } + vector->vg = root; + _evas_vg_svg_node_free(node); + eet_close(ef); + + return vector; +} + +Efl_VG * +_evas_vg_dup_vg_tree(Vg_Data *src, Vg_Data *dest, float pos, double w, double h) +{ + double sx, sy; + Efl_VG *root; + Eina_Matrix3 matrix; + + if (!src) return NULL; + if (w==0 || h ==0 ) return NULL; + if (!dest) + { + root = evas_vg_container_add(NULL); + evas_vg_node_dup(root, src->vg); + sx = w/src->w; + sy = h/src->h; + eina_matrix3_identity(&matrix); + eina_matrix3_scale(&matrix, sx, sy); + eina_matrix3_translate(&matrix, -src->x, -src->y); + evas_vg_node_transformation_set(root, &matrix); + } + else + { + root = evas_vg_container_add(NULL); + evas_vg_node_dup(root, src->vg); + + // for start vector + sx = w/src->w; + sy = h/src->h; + eina_matrix3_identity(&matrix); + eina_matrix3_scale(&matrix, sx, sy); + eina_matrix3_translate(&matrix, -src->x, -src->y); + evas_vg_node_transformation_set(src->vg, &matrix); + + // for end vector + sx = w/dest->w; + sy = h/dest->h; + eina_matrix3_identity(&matrix); + eina_matrix3_scale(&matrix, sx, sy); + eina_matrix3_translate(&matrix, -dest->x, -dest->y); + evas_vg_node_transformation_set(dest->vg, &matrix); + + // do the interpolation + if (!evas_vg_node_interpolate(root, src->vg, dest->vg, pos)) + { + ;//ERR(" Can't interpolate check the svg file \n"); + } + // performance hack + // instead of duplicating the tree and applying the transformation + // i just updated the transformation matrix and reset it back to null. + // assumption is that the root vg will never have a transformation + eina_matrix3_identity(&matrix); + evas_vg_node_transformation_set(src->vg, &matrix); + evas_vg_node_transformation_set(dest->vg, &matrix); + } + return root; +} + +static void +_svg_style_gradient_free(Svg_Style_Gradient *grad) +{ + Efl_Gfx_Gradient_Stop *stop; + + if (!grad) return; + + eina_stringshare_del(grad->id); + eina_stringshare_del(grad->ref); + free(grad->radial); + free(grad->linear); + + EINA_LIST_FREE(grad->stops, stop) + { + free(stop); + } + free(grad); +} + +static void +_node_style_free(Svg_Style_Property *style) +{ + if (!style) return; + + _svg_style_gradient_free(style->fill.paint.gradient); + eina_stringshare_del(style->fill.paint.url); + _svg_style_gradient_free(style->stroke.paint.gradient); + eina_stringshare_del(style->stroke.paint.url); + free(style); +} + +EAPI void +_evas_vg_svg_node_free(Svg_Node *node) +{ + Svg_Node *child; + Svg_Style_Gradient *grad; + + if (!node) return; + + EINA_LIST_FREE(node->child, child) + { + _evas_vg_svg_node_free(child); + } + + eina_stringshare_del(node->id); + free(node->transform); + _node_style_free(node->style); + switch (node->type) + { + case SVG_NODE_PATH: + eina_stringshare_del(node->node.path.path); + break; + case SVG_NODE_POLYGON: + case SVG_NODE_POLYLINE: + free(node->node.polygon.points); + break; + case SVG_NODE_DOC: + _evas_vg_svg_node_free(node->node.doc.defs); + break; + case SVG_NODE_DEFS: + EINA_LIST_FREE(node->node.defs.gradients, grad) + { + _svg_style_gradient_free(grad); + } + break; + default: + break; + } + free(node); +} diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c index 0ccea59..0ae044c 100644 --- a/src/modules/evas/engines/gl_generic/evas_engine.c +++ b/src/modules/evas/engines/gl_generic/evas_engine.c @@ -2552,6 +2552,51 @@ eng_ector_end(void *data EINA_UNUSED, void *context EINA_UNUSED, Ector_Surface * } } +static Ector_Surface_Cache *gl_cache = NULL; + +static void +_ector_surface_cache_free_cb(void *data) +{ + eng_image_free(gl_cache->output, data); +} + +static void +_ector_surface_cache_init(void *output) +{ + if (!gl_cache) + { + gl_cache = calloc(1, sizeof(Ector_Surface_Cache)); + gl_cache->output = output; + gl_cache->suface_hash = eina_hash_string_superfast_new(_ector_surface_cache_free_cb); + } +} + +// static void +// _ector_surface_cache_dump(void) +// { +// if (gl_cache) +// { +// eina_hash_free(gl_cache->suface_hash); +// free(gl_cache); +// gl_cache = NULL; +// } +// } + +static void +eng_ector_surface_cache_set(void *data, const char *key, void *surface) +{ + _ector_surface_cache_init(data); + eina_hash_add(gl_cache->suface_hash, key, surface); +} + +static void * +eng_ector_surface_cache_get(void *data, const char *key) +{ + _ector_surface_cache_init(data); + return eina_hash_find(gl_cache->suface_hash, key); +} + + static Evas_Func func, pfunc; static int @@ -2701,6 +2746,8 @@ module_open(Evas_Module *em) ORD(ector_renderer_draw); ORD(ector_end); ORD(ector_surface_create); + ORD(ector_surface_cache_set); + ORD(ector_surface_cache_get); /* now advertise out own api */ em->functions = (void *)(&func); diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index 3857dd1..3a65998 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -434,11 +434,14 @@ static int _evas_soft_gen_log_dom = -1; //#define QCMD evas_thread_cmd_enqueue #define QCMD evas_thread_queue_flush +static void _ector_surface_cache_dump(void); + static void eng_output_dump(void *data EINA_UNUSED) { evas_common_image_image_all_unload(); evas_common_font_font_all_unload(); + _ector_surface_cache_dump(); } static void * @@ -3889,9 +3892,72 @@ _draw_thread_ector_surface_set(void *data) static void * eng_ector_surface_create(void *data EINA_UNUSED, void *surface EINA_UNUSED, int width EINA_UNUSED, int height EINA_UNUSED) { - return NULL; + RGBA_Image *im; + + if (!surface) + { + surface = eng_image_new_from_copied_data(data, width, height, NULL, EINA_TRUE, EVAS_COLORSPACE_ARGB8888); + } + else + { + int cur_w , cur_h; + im = surface; + eng_image_size_get(data, im, &cur_w, &cur_h); + if (width != cur_w || height != cur_h) + { + eng_image_free(data, surface); + surface = eng_image_new_from_copied_data(data, width, height, NULL, EINA_TRUE, EVAS_COLORSPACE_ARGB8888); + } + } + return surface; } +static Ector_Surface_Cache *sw_cache = NULL; + +static void +_ector_surface_cache_free_cb(void *data) +{ + eng_image_free(sw_cache->output, data); +} + +static void +_ector_surface_cache_init(void *output) +{ + if (!sw_cache) + { + printf("engine cache init\n"); + sw_cache = calloc(1, sizeof(Ector_Surface_Cache)); + sw_cache->output = output; + sw_cache->suface_hash = eina_hash_string_superfast_new(_ector_surface_cache_free_cb); + } +} + +static void +_ector_surface_cache_dump(void) +{ + if (sw_cache) + { + eina_hash_free(sw_cache->suface_hash); + free(sw_cache); + sw_cache = NULL; + } +} + +static void +eng_ector_surface_cache_set(void *data, const char *key, void *surface) +{ + _ector_surface_cache_init(data); + eina_hash_add(sw_cache->suface_hash, key, surface); +} + +static void * +eng_ector_surface_cache_get(void *data, const char *key) +{ + _ector_surface_cache_init(data); + return eina_hash_find(sw_cache->suface_hash, key); +} + + static void eng_ector_begin(void *data EINA_UNUSED, void *context EINA_UNUSED, Ector_Surface *ector, void *surface, int x, int y, Eina_Bool do_async) { @@ -4156,7 +4222,9 @@ static Evas_Func func = eng_ector_begin, eng_ector_renderer_draw, eng_ector_end, - eng_ector_surface_create + eng_ector_surface_create, + eng_ector_surface_cache_set, + eng_ector_surface_cache_get /* FUTURE software generic calls go here */ }; -- 2.7.4