From 5b0c43cbbc9f5f1e91390be8453637d42d897437 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 9 Jan 2019 16:22:52 +0900 Subject: [PATCH] evas json: introduce lottie animation in efl. lottie animation is a new format of animation data that works based on vector graphics and key frames. lottie-player is a standalone library that manipulate lottie animation scenes. Since lottie animation file format is json, evas json loader is added here and it parses json data using lottie player and construct vector nodes graphs by accessing lottie-player interfaces. Since evas vector is designed for static image, here changes vg object interfaces to newly afford animation controller by expanding Gfx.Image.Animation_Controller and expand vg cache routines for caching first and last frame images which is mostly used repeatedly in common scenarios. @feature --- configure.ac | 3 +- m4/evas_check_loader.m4 | 34 +- meson_options.txt | 2 +- src/Makefile_Evas.am | 30 ++ src/lib/evas/canvas/efl_canvas_vg_object.c | 340 ++++++++++++++++-- src/lib/evas/canvas/efl_canvas_vg_object.eo | 19 +- src/lib/evas/canvas/evas_vg_private.h | 18 +- src/lib/evas/file/evas_module.c | 4 + src/lib/evas/include/evas_private.h | 16 +- src/lib/evas/vg/evas_vg_cache.c | 137 +++++-- src/modules/evas/meson.build | 2 +- .../evas/vg_loaders/eet/evas_vg_load_eet.c | 6 +- .../evas/vg_loaders/json/evas_vg_load_json.c | 145 ++++++++ src/modules/evas/vg_loaders/meson.build | 35 +- .../evas/vg_loaders/svg/evas_vg_load_svg.c | 21 +- src/static_libs/vg_common/meson.build | 9 + src/static_libs/vg_common/vg_common.h | 6 + src/static_libs/vg_common/vg_common_json.c | 290 +++++++++++++++ 18 files changed, 1016 insertions(+), 101 deletions(-) create mode 100644 src/modules/evas/vg_loaders/json/evas_vg_load_json.c create mode 100644 src/static_libs/vg_common/vg_common_json.c diff --git a/configure.ac b/configure.ac index 576a1afc71..039ce2d92b 100644 --- a/configure.ac +++ b/configure.ac @@ -2585,7 +2585,7 @@ esac ARG_ENABLE_EVAS_VG_LOADER(SVG, static) ARG_ENABLE_EVAS_VG_LOADER(EET, static) - +ARG_ENABLE_EVAS_VG_LOADER(JSON, auto) ARG_ENABLE_EVAS_IMAGE_LOADER(BMP, static) ARG_ENABLE_EVAS_IMAGE_LOADER(Eet, static) @@ -2876,6 +2876,7 @@ AM_CONDITIONAL([BUILD_ENGINE_WAYLAND_COMMON], [test "x${have_evas_engine_wayland EVAS_CHECK_VG_LOADER([SVG], [${want_evas_vg_loader_svg}]) EVAS_CHECK_VG_LOADER([EET], [${want_evas_vg_loader_eet}]) +EVAS_CHECK_VG_LOADER([JSON], [${want_evas_vg_loader_json}]) ## Image Loaders diff --git a/m4/evas_check_loader.m4 b/m4/evas_check_loader.m4 index ff48c3c388..98a5872088 100644 --- a/m4/evas_check_loader.m4 +++ b/m4/evas_check_loader.m4 @@ -58,6 +58,37 @@ AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4]) ]) +dnl use: EVAS_CHECK_VG_LOADER_DEP_JSON(loader, want_static[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([EVAS_CHECK_VG_LOADER_DEP_JSON], +[ + +have_dep="no" +evas_vg_loader_[]$1[]_cflags="" +evas_vg_loader_[]$1[]_libs="" + +AC_CHECK_HEADER([lottieanimation_capi.h], [have_dep="yes"]) + +if test "x${have_dep}" = "xyes" ; then + AC_CHECK_LIB([lottie-player], + [lottie_animation_from_file], + [ + evas_vg_loader_[]$1[]_libs="-llottie-player" + ] + ) +fi + +if test "x$2" = "xstatic" && test "x${have_dep}" = "xyes" ; then + requirements_libs_evas="${evas_vg_loader_[]$1[]_libs} ${requirements_libs_evas}" +fi + +AC_SUBST([evas_vg_loader_$1_cflags]) +AC_SUBST([evas_vg_loader_$1_libs]) + +AS_IF([test "x${have_dep}" = "xyes"], [$3], [$4]) + +]) + dnl use: ARG_ENABLE_EVAS_IMAGE_LOADER(loader, default_value) AC_DEFUN([ARG_ENABLE_EVAS_IMAGE_LOADER], @@ -657,11 +688,12 @@ if test "x${have_loader}" = "xyes" ; then want_static_loader="yes" else have_evas_vg_loader_[]DOWN="yes" + want_static_loader="yes" fi fi if test "x${have_loader}" = "xyes" ; then - AC_DEFINE(BUILD_VG_LOADER_[]UP, [1], [UP Image Loader Support]) + AC_DEFINE(BUILD_VG_LOADER_[]UP, [1], [UP VG Loader Support]) fi AM_CONDITIONAL(BUILD_VG_LOADER_[]UP, [test "x${have_loader}" = "xyes"]) diff --git a/meson_options.txt b/meson_options.txt index e24f999ee4..713ac1304f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -201,7 +201,7 @@ option('evas-modules', option('evas-loaders-disabler', type : 'array', description : 'add names here to disable the loaders', - choices : ['gst', 'pdf', 'ps', 'raw', 'svg', 'xcf', 'bmp', 'dds', 'eet', 'generic', 'gif', 'ico', 'jp2k', 'jpeg', 'pmaps', 'png', 'psd', 'tga', 'tgv', 'tiff', 'wbmp', 'webp', 'xpm'], + choices : ['gst', 'pdf', 'ps', 'raw', 'svg', 'xcf', 'bmp', 'dds', 'eet', 'generic', 'gif', 'ico', 'jp2k', 'jpeg', 'pmaps', 'png', 'psd', 'tga', 'tgv', 'tiff', 'wbmp', 'webp', 'xpm', 'json'], value : ['webp'] ) diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 12f9b31fd5..4e19fbec79 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -1558,6 +1558,36 @@ modules_evas_vg_savers_eet_module_la_LIBTOOLFLAGS = --tag=disable-static endif endif +if BUILD_VG_LOADER_JSON +if EVAS_STATIC_BUILD_VG_JSON +lib_evas_libevas_la_SOURCES += modules/evas/vg_loaders/json/evas_vg_load_json.c \ +static_libs/vg_common/vg_common_json.c \ +static_libs/vg_common/vg_common.h +lib_evas_libevas_la_CPPFLAGS += -I$(top_srcdir)/src/static_libs/vg_common \ +@evas_vg_loader_json_cflags@ +lib_evas_libevas_la_LIBADD += @evas_vg_loader_json_libs@ +else +vgloaderjsonpkgdir = $(libdir)/evas/modules/vg_loaders/json/$(MODULE_ARCH) +vgloaderjsonpkg_LTLIBRARIES = modules/evas/vg_loaders/json/module.la + +# Workaround for broken parallel install support in automake (relink issue) +# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 +install_vgloaderjsonpkgLTLIBRARIES = install-vgloaderjsonpkgLTLIBRARIES +$(install_vgloaderjsonpkgLTLIBRARIES): install-libLTLIBRARIES + +modules_evas_vg_loaders_json_module_la_SOURCES = modules/evas/vg_loaders/json/evas_vg_load_json.c +modules_evas_vg_loaders_json_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/lib/evas/include \ +@EVAS_CFLAGS@ \ +@evas_vg_loader_json_cflags@ +modules_evas_vg_loaders_json_module_la_LIBADD = \ +@USE_EVAS_LIBS@ \ +@evas_vg_loader_json_libs@ +modules_evas_vg_loaders_json_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@ +modules_evas_vg_loaders_json_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ +modules_evas_vg_loaders_json_module_la_LIBTOOLFLAGS = --tag=disable-static +endif +endif if BUILD_LOADER_BMP if EVAS_STATIC_BUILD_BMP diff --git a/src/lib/evas/canvas/efl_canvas_vg_object.c b/src/lib/evas/canvas/efl_canvas_vg_object.c index 4ebf00d875..653e9dfd53 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_object.c +++ b/src/lib/evas/canvas/efl_canvas_vg_object.c @@ -117,7 +117,7 @@ _efl_canvas_vg_object_root_node_get(const Eo *obj, Efl_Canvas_Vg_Object_Data *pd evas_cache_vg_entry_del(pd->vg_entry); pd->vg_entry = vg_entry; } - root = evas_cache_vg_tree_get(pd->vg_entry); + root = evas_cache_vg_tree_get(pd->vg_entry, pd->frame_idx); } else if (pd->user_entry) root = pd->user_entry->root; else root = pd->root; @@ -244,22 +244,21 @@ _efl_canvas_vg_object_viewbox_align_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg if (align_y) *align_y = pd->align_y; } -EOLIAN static Eina_Bool -_efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const char *file, const char *key) +static Eina_Bool +_vg_file_mmap_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const Eina_File *file, const char *key, Eina_Bool mmap) { Vg_Cache_Entry *old_entry; + Evas_Object_Protected_Data *obj; - if (!file) return EINA_FALSE; + obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS); old_entry = pd->vg_entry; - Evas_Object_Protected_Data *obj; - obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS); - if (file) pd->vg_entry = evas_cache_vg_entry_create(file, key, obj->cur->geometry.w, - obj->cur->geometry.h); + obj->cur->geometry.h, + mmap); else pd->vg_entry = NULL; @@ -270,7 +269,9 @@ _efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *p } EOLIAN static void -_efl_canvas_vg_object_efl_file_file_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Object_Data *pd, const char **file, const char **key) +_efl_canvas_vg_object_efl_file_mmap_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd, + const Eina_File **file, const char **key) { if (file) *file = NULL; if (key) *key = NULL; @@ -282,6 +283,106 @@ _efl_canvas_vg_object_efl_file_file_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg } } +EOLIAN static Eina_Bool +_efl_canvas_vg_object_efl_file_mmap_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const Eina_File *file, const char *key) +{ + Eina_File *pf = pd->file; + Eina_Bool ret; + + if (!file) + { + Evas_Object_Protected_Data *obj; + obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS); + evas_cache_vg_entry_del(pd->vg_entry); + evas_object_change(eo_obj, obj); + eina_stringshare_del(pd->key); + pd->vg_entry = NULL; + pd->file = NULL; + pd->key = NULL; + return EINA_TRUE; + } + if (pd->file == file) + { + if (!pd->key && !key) return EINA_FALSE; + else if (pd->key && key) + { + if (!strcmp(pd->key, key)) return EINA_FALSE; + } + pf = NULL; + } + if (pd->file != file) + pd->file = eina_file_dup(file); + ret = _vg_file_mmap_set(eo_obj, pd, file, key, EINA_TRUE); + + //Close previous file after deleting ex-cache entry. + if (pf) eina_file_close(pf); + + return ret; +} + +EOLIAN static Eina_Bool +_efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const char *file, const char *key) +{ + Eina_File *pf = pd->file; + Eina_Bool ret; + + if (!file) + { + Evas_Object_Protected_Data *obj; + obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS); + evas_cache_vg_entry_del(pd->vg_entry); + evas_object_change(eo_obj, obj); + eina_stringshare_del(pd->key); + pd->vg_entry = NULL; + pd->file = NULL; + pd->key = NULL; + return EINA_TRUE; + } + if (!pd->file) + { + pd->file = eina_file_open(file, EINA_FALSE); + if (!pd->file) return EINA_FALSE; + } + else + { + const char *filename = eina_file_filename_get(pd->file); + if (filename) + { + if (strcmp(filename, file)) + pd->file = eina_file_open(file, EINA_FALSE); + else + { + if (!pd->key && !key) return EINA_FALSE; + else if (pd->key && key) + { + if (!strcmp(pd->key, key)) return EINA_FALSE; + } + pf = NULL; + } + } + } + + ret = _vg_file_mmap_set(eo_obj, pd, pd->file, key, EINA_FALSE); + + //Close previous file after deleting ex-cache entry. + if (pf) eina_file_close(pf); + + return ret; +} + +EOLIAN static void +_efl_canvas_vg_object_efl_file_file_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Object_Data *pd, const char **file, const char **key) +{ + if (file) *file = NULL; + if (key) *key = NULL; + + if (pd->vg_entry) + { + if (file) *file = eina_file_filename_get(pd->vg_entry->file); + if (key) *key = pd->vg_entry->key; + } +} + EOLIAN static Eina_Bool _efl_canvas_vg_object_efl_file_save(const Eo *obj, Efl_Canvas_Vg_Object_Data *pd, const char *file, const char *key, const char *flags) { @@ -319,6 +420,18 @@ _efl_canvas_vg_object_efl_object_destructor(Eo *eo_obj, Efl_Canvas_Vg_Object_Dat pd->user_entry = NULL; evas_cache_vg_entry_del(pd->vg_entry); + //Close files after deleting entry. + if (pd->file) + { + eina_file_close(pd->file); + pd->file = NULL; + } + if (pd->key) + { + eina_stringshare_del(pd->key); + pd->key = NULL; + } + efl_destructor(efl_super(eo_obj, MY_CLASS)); } @@ -425,7 +538,7 @@ _evas_vg_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, static void * _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, void *engine, Efl_VG *root, int w, int h, void *key, - void *buffer, Eina_Bool do_async) + void *buffer, Eina_Bool do_async, Eina_Bool cacheable) { Ector_Surface *ector; RGBA_Draw_Context *context; @@ -463,8 +576,11 @@ _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd ENFN->ector_end(engine, buffer, context, ector, do_async); evas_common_draw_context_free(context); - if (buffer_created) - ENFN->ector_surface_cache_set(engine, key, buffer); + if (buffer_created && cacheable) + { + ENFN->ector_surface_cache_set(engine, key, buffer); + pd->cached_frame_idx = pd->frame_idx; + } return buffer; } @@ -474,7 +590,7 @@ _render_buffer_to_screen(Evas_Object_Protected_Data *obj, void *engine, void *output, void *context, void *surface, void *buffer, int x, int y, int w, int h, - Eina_Bool do_async) + Eina_Bool do_async, Eina_Bool cacheable) { Eina_Bool async_unref; @@ -490,50 +606,90 @@ _render_buffer_to_screen(Evas_Object_Protected_Data *obj, evas_cache_image_ref((Image_Entry *)buffer); evas_unref_queue_image_put(obj->layer->evas, buffer); } + + //TODO: Reuse buffer if size is same? + if (!cacheable) ENFN->ector_surface_destroy(engine, buffer); } static void _cache_vg_entry_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, void *engine, void *output, void *context, void *surface, - int x, int y, int w, int h, Eina_Bool do_async) + int x, int y, int w, int h, Eina_Bool do_async, + Eina_Bool cacheable) { Vg_Cache_Entry *vg_entry = pd->vg_entry; Efl_VG *root; + Eina_Position2D offset = {0, 0}; //Offset after keeping aspect ratio. // if the size changed in between path set and the draw call; - if ((vg_entry->w != w) || (vg_entry->h != h)) { - vg_entry = evas_cache_vg_entry_resize(vg_entry, w, h); - evas_cache_vg_entry_del(pd->vg_entry); - pd->vg_entry = vg_entry; - } - root = evas_cache_vg_tree_get(vg_entry); - if (!root) return; + Eina_Size2D size = evas_cache_vg_entry_default_size_get(pd->vg_entry); - void *buffer = ENFN->ector_surface_cache_get(engine, root); + //adjust size for aspect ratio. + if (size.w > 0 && size.h > 0) + { + float rw = (float) w / (float) size.w; + float rh = (float) h / (float) size.h; + + if (rw < rh) + { + size.w = w; + size.h = (int) ((float) size.h * rw); + } + else + { + size.w = (int) ((float) size.w * rh); + size.h = h; + } + } + else + { + size.w = w; + size.h = h; + } + + vg_entry = evas_cache_vg_entry_resize(vg_entry, size.w, size.h); + evas_cache_vg_entry_del(pd->vg_entry); + pd->vg_entry = vg_entry; + + //update for adjusted pos and size. + offset.x = w - size.w; + if (offset.x > 0) offset.x /= 2; + offset.y = h - size.h; + if (offset.y > 0) offset.y /= 2; + w = size.w; + h = size.h; + } + root = evas_cache_vg_tree_get(vg_entry, pd->frame_idx); + if (!root) return; + void *buffer = NULL; + + if (pd->frame_idx == pd->cached_frame_idx) + buffer = ENFN->ector_surface_cache_get(engine, (void *) root); if (!buffer) buffer = _render_to_buffer(obj, pd, engine, root, w, h, root, NULL, - do_async); + do_async, cacheable); else //cache reference was increased when we get the cache. - ENFN->ector_surface_cache_drop(engine, root); + ENFN->ector_surface_cache_drop(engine, (void *) root); _render_buffer_to_screen(obj, engine, output, context, surface, buffer, - x, y, w, h, - do_async); + x + offset.x, y + offset.y, w, h, + do_async, cacheable); } static void _user_vg_entry_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, void *engine, void *output, void *context, void *surface, - int x, int y, int w, int h, Eina_Bool do_async) + int x, int y, int w, int h, Eina_Bool do_async, + Eina_Bool cacheable) { Vg_User_Entry *user_entry = pd->user_entry; @@ -554,7 +710,7 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj, // render to the buffer buffer = _render_to_buffer(obj, pd, engine, user_entry->root, w, h, user_entry, buffer, - do_async); + do_async, cacheable); } else { @@ -565,7 +721,7 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj, w, h, user_entry, buffer, - do_async); + do_async, EINA_FALSE); //cache reference was increased when we get the cache. ENFN->ector_surface_cache_drop(engine, user_entry->root); } @@ -574,7 +730,7 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj, engine, output, context, surface, buffer, x, y, w, h, - do_async); + do_async, cacheable); } static void @@ -596,19 +752,30 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED, ENFN->context_anti_alias_set(engine, context, obj->cur->anti_alias); ENFN->context_render_op_set(engine, context, obj->cur->render_op); + //Cache surface? + Eina_Bool cacheable = EINA_FALSE; + + /* Try caching buffer only for first and last frames + because it's an overhead task if it caches all frame images. + We assume the first and last frame images are the most resusable + in generic scenarios. */ + if (pd->frame_idx == 0 || + (pd->frame_idx == (int) evas_cache_vg_anim_frame_count_get(pd->vg_entry))) + cacheable = EINA_TRUE; + if (pd->vg_entry) { _cache_vg_entry_render(obj, pd, engine, output, context, surface, obj->cur->geometry.x + x, obj->cur->geometry.y + y, - obj->cur->geometry.w, obj->cur->geometry.h, do_async); + obj->cur->geometry.w, obj->cur->geometry.h, do_async, cacheable); } if (pd->user_entry) { _user_vg_entry_render(obj, pd, engine, output, context, surface, obj->cur->geometry.x + x, obj->cur->geometry.y + y, - obj->cur->geometry.w, obj->cur->geometry.h, do_async); + obj->cur->geometry.w, obj->cur->geometry.h, do_async, cacheable); } pd->changed = EINA_FALSE; } @@ -764,6 +931,79 @@ _efl_canvas_vg_object_was_opaque(Evas_Object *eo_obj EINA_UNUSED, return 0; } +/* animated feature */ +EOLIAN static Eina_Bool +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED EINA_UNUSED) +{ + //TODO: + return EINA_TRUE; +} + +EOLIAN static int +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_frame_count_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + if (!pd->vg_entry) return 0; + return evas_cache_vg_anim_frame_count_get(pd->vg_entry); +} + +EOLIAN static Efl_Gfx_Image_Animation_Controller_Loop_Hint +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_loop_type_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + //TODO: + return EFL_GFX_IMAGE_ANIMATION_CONTROLLER_LOOP_HINT_NONE; +} + +EOLIAN static int +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_loop_count_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + //TODO: + return 0; +} + +EOLIAN static double +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_frame_duration_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd, + int start_frame EINA_UNUSED, + int frame_num EINA_UNUSED) +{ + if (!pd->vg_entry) return 0; + return evas_cache_vg_anim_duration_get(pd->vg_entry); +} + +EOLIAN static Eina_Bool +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_frame_set(Eo *eo_obj, + Efl_Canvas_Vg_Object_Data *pd, + int frame_index) +{ + //TODO: Validate frame_index range + if (pd->frame_idx == frame_index) return EINA_TRUE; + + //Image is changed, drop previous cached image. + pd->frame_idx = frame_index; + pd->changed = EINA_TRUE; + evas_object_change(eo_obj, efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS)); + + return EINA_TRUE; +} + +EOLIAN static int +_efl_canvas_vg_object_efl_gfx_image_animation_controller_animated_frame_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + return pd->frame_idx; +} + +EOLIAN static Eina_Size2D +_efl_canvas_vg_object_default_size_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + return evas_cache_vg_entry_default_size_get(pd->vg_entry); +} + /* the actual api call to add a vector graphic object */ EAPI Evas_Object * evas_object_vg_add(Evas *e) @@ -774,4 +1014,40 @@ evas_object_vg_add(Evas *e) return efl_add(MY_CLASS, e, efl_canvas_object_legacy_ctor(efl_added)); } +EAPI int +evas_object_vg_animated_frame_get(const Evas_Object *obj) +{ + return efl_gfx_image_animated_frame_get(obj); +} + +EAPI double +evas_object_vg_animated_frame_duration_get(const Evas_Object *obj, int start_frame, int frame_num) +{ + return efl_gfx_image_animated_frame_duration_get(obj, start_frame, frame_num); +} + +EAPI int +evas_object_vg_animated_frame_count_get(const Evas_Object *obj) +{ + return efl_gfx_image_animated_frame_count_get(obj); +} + +EAPI Eina_Bool +evas_object_vg_animated_frame_set(Evas_Object *obj, int frame_index) +{ + return efl_gfx_image_animated_frame_set(obj, frame_index); +} + +EAPI Eina_Bool +evas_object_vg_mmap_set(Evas_Object *obj, const Eina_File *f, const char *key) +{ + return efl_file_mmap_set(obj, f, key); +} + +EAPI Eina_Bool +evas_object_vg_file_set(Evas_Object *obj, const char *file, const char *key) +{ + return efl_file_set(obj, file, key); +} + #include "efl_canvas_vg_object.eo.c" diff --git a/src/lib/evas/canvas/efl_canvas_vg_object.eo b/src/lib/evas/canvas/efl_canvas_vg_object.eo index 971c977daf..047544907e 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_object.eo +++ b/src/lib/evas/canvas/efl_canvas_vg_object.eo @@ -21,7 +21,7 @@ enum Efl.Canvas.Vg.Fill_Mode dimension of the viewport.]] } -class Efl.Canvas.Vg.Object (Efl.Canvas.Object, Efl.File) +class Efl.Canvas.Vg.Object (Efl.Canvas.Object, Efl.File, Efl.Gfx.Image_Animation_Controller) { [[Efl vector graphics class]] legacy_prefix: evas_object_vg; @@ -68,13 +68,28 @@ class Efl.Canvas.Vg.Object (Efl.Canvas.Object, Efl.File) root: Efl.Canvas.Vg.Node; [[Root node of the VG canvas.]] } } + @property default_size { + get { + [[Get the default vector size that specified from vector resource. + @since 1.22]] + } + values { + size: Eina.Size2D; + } + } } implements { Efl.Object.constructor; Efl.Object.finalize; Efl.Object.destructor; - // FIXME: Implement mmap only (also fix cache keys) + Efl.File.mmap { get; set; } Efl.File.file { get; set; } Efl.File.save; + Efl.Gfx.Image_Animation_Controller.animated { get; } + Efl.Gfx.Image_Animation_Controller.animated_frame { get; set; } + Efl.Gfx.Image_Animation_Controller.animated_frame_count { get; } + Efl.Gfx.Image_Animation_Controller.animated_loop_type { get; } + Efl.Gfx.Image_Animation_Controller.animated_loop_count { get; } + Efl.Gfx.Image_Animation_Controller.animated_frame_duration { get; } } } diff --git a/src/lib/evas/canvas/evas_vg_private.h b/src/lib/evas/canvas/evas_vg_private.h index fa122fa587..dc5cdf6a68 100644 --- a/src/lib/evas/canvas/evas_vg_private.h +++ b/src/lib/evas/canvas/evas_vg_private.h @@ -19,14 +19,15 @@ typedef struct _Vg_Cache typedef struct _Vg_Cache_Entry { char *hash_key; - Eina_Stringshare *file; + const Eina_File *file; Eina_Stringshare *key; int w; int h; - Efl_VG *root; + Efl_VG *root[3]; //0: default, 1: start frame, 2: end frame int ref; Vg_File_Data *vfd; + Eina_Bool mmap : 1; } Vg_Cache_Entry; // holds the vg tree info set by the user @@ -49,6 +50,10 @@ struct _Efl_Canvas_Vg_Object_Data Eina_Array cleanup; double align_x, align_y; Efl_Canvas_Vg_Fill_Mode fill_mode; + int frame_idx; + Eina_File *file; + Eina_Stringshare *key; + int cached_frame_idx; Eina_Bool changed : 1; }; @@ -119,12 +124,15 @@ struct _Efl_Canvas_Vg_Interpolation void evas_cache_vg_init(void); void evas_cache_vg_shutdown(void); Vg_Cache_Entry* evas_cache_vg_entry_resize(Vg_Cache_Entry *entry, int w, int h); -Vg_Cache_Entry* evas_cache_vg_entry_create(const char *file, const char *key, int w, int h); -Efl_VG* evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry); +Vg_Cache_Entry* evas_cache_vg_entry_create(const Eina_File *file, const char *key, int w, int h, Eina_Bool mmap); +Efl_VG* evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry, unsigned int frame_num); void evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry); -Vg_File_Data * evas_cache_vg_file_open(const char *file, const char *key); +Vg_File_Data * evas_cache_vg_file_open(const Eina_File *file, const char *key, Eina_Bool mmap); Eina_Bool evas_cache_vg_file_save(Efl_VG *root, int w, int h, const char *file, const char *key, const char *flags); Eina_Bool evas_cache_vg_entry_file_save(Vg_Cache_Entry *vg_entry, const char *file, const char *key, const char *flags); +double evas_cache_vg_anim_duration_get(const Vg_Cache_Entry *vg_entry); +unsigned int evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry *vg_entry); +Eina_Size2D evas_cache_vg_entry_default_size_get(const Vg_Cache_Entry *vg_entry); void efl_canvas_vg_node_vg_obj_set(Efl_VG *node, Efl_VG *vg_obj, Efl_Canvas_Vg_Object_Data *vd); void efl_canvas_vg_node_change(Efl_VG *node); void efl_canvas_vg_container_vg_obj_update(Efl_VG *obj, Efl_Canvas_Vg_Node_Data *nd); diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c index 63129bc286..b467ebcdbf 100644 --- a/src/lib/evas/file/evas_module.c +++ b/src/lib/evas/file/evas_module.c @@ -185,6 +185,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(engine, wayland_egl); #if !EVAS_MODULE_NO_VG_LOADERS EVAS_EINA_STATIC_MODULE_DEFINE(vg_loader, eet); EVAS_EINA_STATIC_MODULE_DEFINE(vg_loader, svg); +EVAS_EINA_STATIC_MODULE_DEFINE(vg_loader, json); #endif #if !EVAS_MODULE_NO_IMAGE_LOADERS @@ -274,6 +275,9 @@ static const struct { #ifdef EVAS_STATIC_BUILD_VG_EET EVAS_EINA_STATIC_MODULE_USE(vg_loader, eet), #endif +#ifdef EVAS_STATIC_BUILD_VG_JSON + EVAS_EINA_STATIC_MODULE_USE(vg_loader, json), +#endif #endif #if !EVAS_MODULE_NO_IMAGE_LOADERS #ifdef EVAS_STATIC_BUILD_BMP diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 76621161af..8b1c5ce538 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -138,6 +138,7 @@ typedef struct _Evas_Canvas3D_Header_Eet Evas_Canvas3D_Header_Eet; typedef struct _Evas_Canvas3D_File_Eet Evas_Canvas3D_File_Eet; typedef struct _Vg_File_Data Vg_File_Data; +typedef struct _Vg_File_Anim_Data Vg_File_Anim_Data; struct _Evas_Canvas3D_Vec2_Eet { @@ -1505,12 +1506,23 @@ struct _Evas_Image_Save_Func int (*image_save) (RGBA_Image *im, const char *file, const char *key, int quality, int compress, const char *encoding); }; +struct _Vg_File_Anim_Data +{ + unsigned int frame_num; //current frame number + unsigned int frame_cnt; //total frame count + float duration; //animation duration +}; + struct _Vg_File_Data { Efl_VG *root; Evas_Vg_Load_Func *loader; - Eina_Rectangle view_box; + Eina_Rectangle view_box; + Vg_File_Anim_Data *anim_data; //only when animation supported. int ref; + int w, h; //default size + + void *loader_data; //loader specific local data Eina_Bool static_viewbox: 1; Eina_Bool preserve_aspect : 1; //Used in SVG @@ -1518,7 +1530,7 @@ struct _Vg_File_Data struct _Evas_Vg_Load_Func { - Vg_File_Data *(*file_open) (const char *file, const char *key, int *error); + Vg_File_Data *(*file_open) (Eina_File *file, const char *key, Eina_Bool mmap, int *error); Eina_Bool (*file_close) (Vg_File_Data *vfd); Eina_Bool (*file_data) (Vg_File_Data *vfd); }; diff --git a/src/lib/evas/vg/evas_vg_cache.c b/src/lib/evas/vg/evas_vg_cache.c index ec5fc6b0be..9928d9a7f1 100644 --- a/src/lib/evas/vg/evas_vg_cache.c +++ b/src/lib/evas/vg/evas_vg_cache.c @@ -35,7 +35,7 @@ static const struct ext_loader_s loaders[] = static const char *loaders_name[] = { /* in order of most likely needed */ - "eet", "svg" + "eet", "json", "svg" }; static const struct ext_saver_s savers[] = @@ -70,7 +70,7 @@ _find_loader_module(const char *file) } static Vg_File_Data * -_vg_load_from_file(const char *file, const char *key) +_vg_load_from_file(const Eina_File *file, const char *key, Eina_Bool mmap) { Evas_Module *em; Evas_Vg_Load_Func *loader; @@ -78,11 +78,12 @@ _vg_load_from_file(const char *file, const char *key) Vg_File_Data *vfd; unsigned int i; - em = _find_loader_module(file); + const char *file_name = eina_file_filename_get(file); + em = _find_loader_module(file_name); if (em) { loader = em->functions; - vfd = loader->file_open(file, key, &error); + vfd = loader->file_open((Eina_File *) file, key, mmap, &error); if (vfd) { vfd->loader = loader; @@ -97,7 +98,8 @@ _vg_load_from_file(const char *file, const char *key) if (em) { loader = em->functions; - vfd = loader->file_open(file, key, &error); + vfd = loader->file_open((Eina_File *) file, key, mmap, &error); + if (vfd) { vfd->loader = loader; return vfd; @@ -105,7 +107,7 @@ _vg_load_from_file(const char *file, const char *key) } } } - WRN("Exhausted all means to load vector file = %s", file); + WRN("Exhausted all means to load vector file = %s", file_name); return NULL; } @@ -152,7 +154,7 @@ _evas_cache_vg_entry_free_cb(void *data) { Eina_Strbuf *hash_key = eina_strbuf_new(); eina_strbuf_append_printf(hash_key, "%s/%s", - vg_entry->file, + eina_file_filename_get(vg_entry->file), vg_entry->key); if (!eina_hash_del(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vg_entry->vfd)) ERR("Failed to delete vfd = (%p) from hash", vg_entry->vfd); @@ -162,7 +164,9 @@ _evas_cache_vg_entry_free_cb(void *data) eina_stringshare_del(vg_entry->key); free(vg_entry->hash_key); - efl_unref(vg_entry->root); + efl_unref(vg_entry->root[0]); + efl_unref(vg_entry->root[1]); + efl_unref(vg_entry->root[2]); free(vg_entry); } @@ -208,9 +212,37 @@ _vg_file_save(Vg_File_Data *vfd, const char *file, const char *key, const char * } static Efl_VG* -_cached_root_get(Vg_Cache_Entry *vg_entry) +_cached_root_get(Vg_Cache_Entry *vg_entry, unsigned int frame_num) { - return vg_entry->root; + Vg_File_Data *vfd = vg_entry->vfd; + + //Case 1: Animatable + if (vfd->anim_data) + { + //Start frame + if (vg_entry->root[1] && frame_num == 0) + { + return vg_entry->root[1]; + } + //End frame + else if (vg_entry->root[2] && (frame_num == (vfd->anim_data->frame_cnt - 1))) + { + return vg_entry->root[2]; + } + //Current frame + else if (vg_entry->root[0] && (frame_num == (vfd->anim_data->frame_num))) + { + return vg_entry->root[0]; + } + } + //Case 2: Static + else + { + if (vg_entry->root[0]) + return vg_entry->root[0]; + } + + return NULL; } static void @@ -227,12 +259,34 @@ _caching_root_update(Vg_Cache_Entry *vg_entry) /* TODO: Yet trivial but still we may have a better solution to avoid this unnecessary copy. If the ector surface key is not to this root pointer. */ - vg_entry->root = efl_duplicate(vfd->root); + vg_entry->root[0] = efl_duplicate(vfd->root); } - else if (vg_entry->root != vfd->root) + else if (vg_entry->root[0] != vfd->root) { - if (vg_entry->root) efl_unref(vg_entry->root); - vg_entry->root = efl_ref(vfd->root); + if (vg_entry->root[0]) efl_unref(vg_entry->root[0]); + vg_entry->root[0] = efl_ref(vfd->root); + } + + //Animatable? + if (!vfd->anim_data) return; + + //Start frame + if (vfd->anim_data->frame_num == 0) + { + if (vg_entry->root[1] != vfd->root) + { + if (vg_entry->root[1]) efl_unref(vg_entry->root[1]); + vg_entry->root[1] = efl_ref(vfd->root); + } + } + //End frame + else if (vfd->anim_data->frame_num == (vfd->anim_data->frame_cnt - 1)) + { + if (vg_entry->root[2] != vfd->root) + { + if (vg_entry->root[2]) efl_unref(vg_entry->root[2]); + vg_entry->root[2] = efl_ref(vfd->root); + } } } @@ -299,17 +353,17 @@ evas_cache_vg_shutdown(void) } Vg_File_Data * -evas_cache_vg_file_open(const char *file, const char *key) +evas_cache_vg_file_open(const Eina_File *file, const char *key, Eina_Bool mmap) { Vg_File_Data *vfd; Eina_Strbuf *hash_key; hash_key = eina_strbuf_new(); - eina_strbuf_append_printf(hash_key, "%s/%s", file, key); + eina_strbuf_append_printf(hash_key, "%s/%s", eina_file_filename_get(file), key); vfd = eina_hash_find(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key)); if (!vfd) { - vfd = _vg_load_from_file(file, key); + vfd = _vg_load_from_file(file, key, mmap); //File exists. if (vfd) eina_hash_add(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vfd); } @@ -320,13 +374,14 @@ evas_cache_vg_file_open(const char *file, const char *key) Vg_Cache_Entry* evas_cache_vg_entry_resize(Vg_Cache_Entry *vg_entry, int w, int h) { - return evas_cache_vg_entry_create(vg_entry->file, vg_entry->key, w, h); + return evas_cache_vg_entry_create(vg_entry->file, vg_entry->key, w, h, vg_entry->mmap); } Vg_Cache_Entry* -evas_cache_vg_entry_create(const char *file, +evas_cache_vg_entry_create(const Eina_File *file, const char *key, - int w, int h) + int w, int h, + Eina_Bool mmap) { Vg_Cache_Entry* vg_entry; Eina_Strbuf *hash_key; @@ -336,7 +391,7 @@ evas_cache_vg_entry_create(const char *file, //TODO: zero-sized entry is useless. how to skip it? hash_key = eina_strbuf_new(); - eina_strbuf_append_printf(hash_key, "%s/%s/%d/%d", file, key, w, h); + eina_strbuf_append_printf(hash_key, "%p/%s/%d/%d", file, key, w, h); vg_entry = eina_hash_find(vg_cache->vg_entry_hash, eina_strbuf_string_get(hash_key)); if (!vg_entry) { @@ -347,17 +402,18 @@ evas_cache_vg_entry_create(const char *file, eina_strbuf_free(hash_key); return NULL; } - vg_entry->file = eina_stringshare_add(file); + vg_entry->file = file; vg_entry->key = eina_stringshare_add(key); vg_entry->w = w; vg_entry->h = h; vg_entry->hash_key = eina_strbuf_string_steal(hash_key); + vg_entry->mmap = mmap; eina_hash_direct_add(vg_cache->vg_entry_hash, vg_entry->hash_key, vg_entry); } eina_strbuf_free(hash_key); vg_entry->ref++; - vg_entry->vfd = evas_cache_vg_file_open(file, key); + vg_entry->vfd = evas_cache_vg_file_open(file, key, mmap); //No File?? if (!vg_entry->vfd) { @@ -369,8 +425,24 @@ evas_cache_vg_entry_create(const char *file, return vg_entry; } +double +evas_cache_vg_anim_duration_get(const Vg_Cache_Entry* vg_entry) +{ + if (!vg_entry->vfd->anim_data) return 0; + return vg_entry->vfd->anim_data->duration; +} + +unsigned int +evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry* vg_entry) +{ + if (!vg_entry) return 0; + Vg_File_Data *vfd = vg_entry->vfd; + if (!vfd || !vfd->anim_data) return 0; + return vfd->anim_data->frame_cnt; +} + Efl_VG* -evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry) +evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry, unsigned int frame_num) { if (!vg_entry) return NULL; if ((vg_entry->w < 1) || (vg_entry->h < 1)) return NULL; @@ -378,7 +450,7 @@ evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry) Vg_File_Data *vfd = vg_entry->vfd; if (!vfd) return NULL; - Efl_VG *root = _cached_root_get(vg_entry); + Efl_VG *root = _cached_root_get(vg_entry, frame_num); if (root) return root; if (!vfd->static_viewbox) @@ -387,13 +459,15 @@ evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry) vfd->view_box.h = vg_entry->h; } + if (vfd->anim_data) vfd->anim_data->frame_num = frame_num; + if (!vfd->loader->file_data(vfd)) return NULL; _caching_root_update(vg_entry); - _local_transform(vg_entry->root, vg_entry->w, vg_entry->h, vfd); + _local_transform(vg_entry->root[0], vg_entry->w, vg_entry->h, vfd); - return vg_entry->root; + return vg_entry->root[0]; } void @@ -406,12 +480,19 @@ evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry) ERR("Failed to delete vg_entry = (%p) from hash", vg_entry); } +Eina_Size2D +evas_cache_vg_entry_default_size_get(const Vg_Cache_Entry *vg_entry) +{ + if (!vg_entry) return EINA_SIZE2D(0, 0); + return EINA_SIZE2D(vg_entry->vfd->w, vg_entry->vfd->h); +} + Eina_Bool evas_cache_vg_entry_file_save(Vg_Cache_Entry *vg_entry, const char *file, const char *key, const char *flags) { Vg_File_Data *vfd = - evas_cache_vg_file_open(vg_entry->file, vg_entry->key); + evas_cache_vg_file_open(vg_entry->file, vg_entry->key, EINA_FALSE); if (!vfd) return EINA_FALSE; diff --git a/src/modules/evas/meson.build b/src/modules/evas/meson.build index e97ff1b2a0..c1f009ebe8 100644 --- a/src/modules/evas/meson.build +++ b/src/modules/evas/meson.build @@ -29,7 +29,7 @@ evas_static_list = [] png = dependency('libpng') tiff = dependency('libtiff-4') giflib = cc.find_library('gif') - +json = dependency('lottie-player', required: get_option('evas-loaders-disabler').contains('json') == false) webp = dependency('libwebp', required: get_option('evas-loaders-disabler').contains('webp') == false) subdir('image_loaders') diff --git a/src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c b/src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c index 3b83b2f541..4f40819471 100644 --- a/src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c +++ b/src/modules/evas/vg_loaders/eet/evas_vg_load_eet.c @@ -13,13 +13,11 @@ static int _evas_vg_loader_eet_log_dom = -1; #define INF(...) EINA_LOG_DOM_INFO(_evas_vg_loader_eet_log_dom, __VA_ARGS__) static Vg_File_Data* -evas_vg_load_file_open_eet(const char *file, const char *key, int *error EINA_UNUSED) +evas_vg_load_file_open_eet(Eina_File *file, const char *key, Eina_Bool mmap EINA_UNUSED, int *error EINA_UNUSED) { Eet_Data_Descriptor *svg_node_eet; Svg_Node *node; - Eet_File *ef; - - ef = eet_open(file, EET_FILE_MODE_READ); + Eet_File *ef = eet_mmap(file); if (!ef) { *error = EVAS_LOAD_ERROR_CORRUPT_FILE; diff --git a/src/modules/evas/vg_loaders/json/evas_vg_load_json.c b/src/modules/evas/vg_loaders/json/evas_vg_load_json.c new file mode 100644 index 0000000000..0eba824d6c --- /dev/null +++ b/src/modules/evas/vg_loaders/json/evas_vg_load_json.c @@ -0,0 +1,145 @@ +#include +#include "vg_common.h" + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_vg_loader_json_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_vg_loader_json_log_dom, __VA_ARGS__) + +static int _evas_vg_loader_json_log_dom = -1; + +static Eina_Bool +evas_vg_load_file_close_json(Vg_File_Data *vfd) +{ + if (!vfd) return EINA_FALSE; + + Lottie_Animation *lot_anim = (Lottie_Animation *) vfd->loader_data; + lottie_animation_destroy(lot_anim); + if (vfd->anim_data) free(vfd->anim_data); + if (vfd->root) efl_unref(vfd->root); + free(vfd); + + return EINA_TRUE; +} + +static Eina_Bool +evas_vg_load_file_data_json(Vg_File_Data *vfd) +{ + return vg_common_json_create_vg_node(vfd); +} + +static Vg_File_Data* +evas_vg_load_file_open_json(Eina_File *file, + const char *key, + Eina_Bool mmap, + int *error EINA_UNUSED) +{ + Vg_File_Data *vfd = calloc(1, sizeof(Vg_File_Data)); + if (!vfd) return NULL; + + Lottie_Animation *lot_anim = NULL; + + if (mmap) + { + const char *data = (const char*) eina_file_map_all(file, EINA_FILE_SEQUENTIAL); + if (!data) goto err; + lot_anim = lottie_animation_from_data(data, key ? key:eina_file_filename_get(file)); + eina_file_map_free(file, (void *) data); + } + else + { + lot_anim = lottie_animation_from_file(eina_file_filename_get(file)); + } + + if (!lot_anim) + { + ERR("Failed lottie_animation_from_file"); + goto err; + } + + unsigned int frame_cnt = lottie_animation_get_totalframe(lot_anim); + + //Support animation + if (frame_cnt > 1) + { + vfd->anim_data = calloc(1, sizeof(Vg_File_Anim_Data)); + if (!vfd->anim_data) goto err; + vfd->anim_data->duration = lottie_animation_get_duration(lot_anim); + vfd->anim_data->frame_cnt = frame_cnt; + } + + //default size + size_t w, h; + lottie_animation_get_size(lot_anim, &w, &h); + vfd->w = (int) w; + vfd->h = (int) h; + + vfd->loader_data = (void *) lot_anim; + + return vfd; + +err: + if (vfd) + { + if (vfd->anim_data) free(vfd->anim_data); + free(vfd); + } + if (lot_anim) lottie_animation_destroy(lot_anim); + + return NULL; +} + +static Evas_Vg_Load_Func evas_vg_load_json_func = +{ + evas_vg_load_file_open_json, + evas_vg_load_file_close_json, + evas_vg_load_file_data_json +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_vg_load_json_func); + _evas_vg_loader_json_log_dom = eina_log_domain_register + ("vg-load-json", EVAS_DEFAULT_LOG_COLOR); + if (_evas_vg_loader_json_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + if (_evas_vg_loader_json_log_dom >= 0) + { + eina_log_domain_unregister(_evas_vg_loader_json_log_dom); + _evas_vg_loader_json_log_dom = -1; + } +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "json", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_VG_LOADER, vg_loader, json); + +#ifndef EVAS_STATIC_BUILD_VG_JSON +EVAS_EINA_MODULE_DEFINE(vg_loader, json); +#endif + diff --git a/src/modules/evas/vg_loaders/meson.build b/src/modules/evas/vg_loaders/meson.build index 584501a825..5ffe8f0ffe 100644 --- a/src/modules/evas/vg_loaders/meson.build +++ b/src/modules/evas/vg_loaders/meson.build @@ -1,13 +1,26 @@ -evas_vg_loaders_file = ['eet', 'svg'] +evas_vg_loaders_file = [ + ['eet', [eet]], + ['json', [json]], + ['svg', []], +] -foreach loader : evas_vg_loaders_file - file = join_paths(loader, 'evas_vg_load_'+loader+'.c') - static_library('vg_loader_'+loader, file, - include_directories : config_dir, - dependencies : evas_pre - ) - evas_static_list += [declare_dependency( - sources: file, - )] - config_h.set('EVAS_STATIC_BUILD_VG_'+loader.to_upper(), '1') +foreach loader_inst : evas_vg_loaders_file + loader = loader_inst[0] + loader_deps = loader_inst[1] + + if (get_option('evas-loaders-disabler').contains(loader) == false) + file = join_paths(loader, 'evas_vg_load_'+loader+'.c') + + static_library('vg_loader_'+loader, file, + include_directories : config_dir, + dependencies : [evas_pre] + loader_deps + ) + + evas_static_list += [declare_dependency( + sources: file, + dependencies: loader_deps, + )] + + config_h.set('EVAS_STATIC_BUILD_VG_'+loader.to_upper(), '1') + endif endforeach diff --git a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c index 25e72feb87..786fa4ab13 100644 --- a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c +++ b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c @@ -2339,7 +2339,10 @@ evas_vg_load_file_close_svg(Vg_File_Data *vfd) } static Vg_File_Data* -evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *error EINA_UNUSED) +evas_vg_load_file_open_svg(Eina_File *file, + const char *key EINA_UNUSED, + Eina_Bool mmap EINA_UNUSED, + int *error EINA_UNUSED) { Evas_SVG_Loader loader = { NULL, NULL, NULL, NULL, NULL, 0, EINA_FALSE @@ -2347,18 +2350,10 @@ evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *e const char *content; unsigned int length; Svg_Node *defs; - Eina_File *f; - - f = eina_file_open(file, EINA_FALSE); - if (!f) - { - *error = EVAS_LOAD_ERROR_CORRUPT_FILE; - return NULL; - } loader.svg_parse = calloc(1, sizeof(Evas_SVG_Parser)); - length = eina_file_size_get(f); - content = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + length = eina_file_size_get(file); + content = eina_file_map_all(file, EINA_FILE_SEQUENTIAL); if (content) { loader.stack = eina_array_new(8); @@ -2366,7 +2361,7 @@ evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *e _evas_svg_loader_parser, &loader); eina_array_free(loader.stack); - eina_file_map_free(f, (void*) content); + eina_file_map_free(file, (void*) content); } if (loader.doc) @@ -2383,7 +2378,7 @@ evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *e *error = EVAS_LOAD_ERROR_GENERIC; } free(loader.svg_parse); - eina_file_close(f); + return vg_common_svg_create_vg_node(loader.doc); } diff --git a/src/static_libs/vg_common/meson.build b/src/static_libs/vg_common/meson.build index 648fcd5570..7c90a42773 100644 --- a/src/static_libs/vg_common/meson.build +++ b/src/static_libs/vg_common/meson.build @@ -5,9 +5,18 @@ vg_common_src = files([ 'vg_common.h', ]) +json = dependency('lottie-player', required: get_option('evas-loaders-disabler').contains('json') == false) + +if (json.found()) + config_h.set('BUILD_VG_LOADER_JSON', '1') + vg_common_src += files('vg_common_json.c') +endif + vg_common_inc_dir = include_directories('.') vg_common = declare_dependency( include_directories: vg_common_inc_dir, sources: vg_common_src, ) + + diff --git a/src/static_libs/vg_common/vg_common.h b/src/static_libs/vg_common/vg_common.h index ab0cb0ded3..ba8d610bf6 100644 --- a/src/static_libs/vg_common/vg_common.h +++ b/src/static_libs/vg_common/vg_common.h @@ -317,4 +317,10 @@ Vg_File_Data * vg_common_svg_create_vg_node(Svg_Node *node); Svg_Node *vg_common_svg_create_svg_node(Vg_File_Data *node); void vg_common_svg_node_free(Svg_Node *node); + +/****************************************************************************************** + * Lottie Compatible feature implementation + ******************************************************************************************/ +Eina_Bool vg_common_json_create_vg_node(Vg_File_Data *vfd); + #endif //EVAS_VG_COMMON_H_ diff --git a/src/static_libs/vg_common/vg_common_json.c b/src/static_libs/vg_common/vg_common_json.c new file mode 100644 index 0000000000..ff3b35ca65 --- /dev/null +++ b/src/static_libs/vg_common/vg_common_json.c @@ -0,0 +1,290 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "vg_common.h" +#include + +#ifdef BUILD_VG_LOADER_JSON + +#include + +static char* +_get_key_val(void *key) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%ld", (size_t) key); + return buf; +} + +static void +_construct_drawable_nodes(Efl_Canvas_Vg_Container *parent, const LOTLayerNode *layer, int depth EINA_UNUSED) +{ + if (!parent) return; + + for (unsigned int i = 0; i < layer->mNodeList.size; i++) + { + LOTNode *node = layer->mNodeList.ptr[i]; + if (!node) continue; + + const float *data = node->mPath.ptPtr; + if (!data) continue; + + char *key = _get_key_val(node); + Efl_Canvas_Vg_Shape *shape = efl_key_data_get(parent, key); + if (!shape) + { + shape = efl_add(EFL_CANVAS_VG_SHAPE_CLASS, parent); + efl_key_data_set(parent, key, shape); + } + else + efl_gfx_path_reset(shape); + + efl_gfx_entity_visible_set(shape, EINA_TRUE); +#if DEBUG + for (int i = 0; i < depth; i++) printf(" "); + printf("%s (%p)\n", efl_class_name_get(efl_class_get(shape)), shape); +#endif + //0: Path + efl_gfx_path_reserve(shape, node->mPath.elmCount, node->mPath.ptCount); + + for (int i = 0; i < node->mPath.elmCount; i++) + { + switch (node->mPath.elmPtr[i]) + { + case 0: + efl_gfx_path_append_move_to(shape, data[0], data[1]); + data += 2; + break; + case 1: + efl_gfx_path_append_line_to(shape, data[0], data[1]); + data += 2; + break; + case 2: + efl_gfx_path_append_cubic_to(shape, data[0], data[1], data[2], data[3], data[4], data[5]); + data += 6; + break; + case 3: + efl_gfx_path_append_close(shape); + break; + default: + ERR("No reserved path type = %d", node->mPath.elmPtr[i]); + } + } + + //1: Stroke + if (node->mStroke.enable) + { + //Stroke Width + efl_gfx_shape_stroke_width_set(shape, node->mStroke.width); + + //Stroke Cap + Efl_Gfx_Cap cap; + switch (node->mStroke.cap) + { + case CapFlat: cap = EFL_GFX_CAP_BUTT; break; + case CapSquare: cap = EFL_GFX_CAP_SQUARE; break; + case CapRound: cap = EFL_GFX_CAP_ROUND; break; + default: cap = EFL_GFX_CAP_BUTT; break; + } + efl_gfx_shape_stroke_cap_set(shape, cap); + + //Stroke Join + Efl_Gfx_Join join; + switch (node->mStroke.join) + { + case JoinMiter: join = EFL_GFX_JOIN_MITER; break; + case JoinBevel: join = EFL_GFX_JOIN_BEVEL; break; + case JoinRound: join = EFL_GFX_JOIN_ROUND; break; + default: join = EFL_GFX_JOIN_MITER; break; + } + efl_gfx_shape_stroke_join_set(shape, join); + + //Stroke Dash + if (node->mStroke.dashArraySize > 0) + { + int size = (node->mStroke.dashArraySize / 2); + Efl_Gfx_Dash *dash = malloc(sizeof(Efl_Gfx_Dash) * size); + if (dash) + { + for (int i = 0; i <= size; i+=2) + { + dash[i].length = node->mStroke.dashArray[i]; + dash[i].gap = node->mStroke.dashArray[i + 1]; + } + efl_gfx_shape_stroke_dash_set(shape, dash, size); + free(dash); + } + } + } + + //2: Fill Method + switch (node->mBrushType) + { + case BrushSolid: + { + float pa = ((float)node->mColor.a) / 255; + int r = (int)(((float) node->mColor.r) * pa); + int g = (int)(((float) node->mColor.g) * pa); + int b = (int)(((float) node->mColor.b) * pa); + int a = node->mColor.a; + + if (node->mStroke.enable) + efl_gfx_shape_stroke_color_set(shape, r, g, b, a); + else + efl_gfx_color_set(shape, r, g, b, a); + } + break; + case BrushGradient: + { + Efl_Canvas_Vg_Gradient* grad = NULL; + + if (node->mGradient.type == GradientLinear) + { + grad = efl_add(EFL_CANVAS_VG_GRADIENT_LINEAR_CLASS, parent); + efl_gfx_gradient_linear_start_set(grad, node->mGradient.start.x, node->mGradient.start.y); + efl_gfx_gradient_linear_end_set(grad, node->mGradient.end.x, node->mGradient.end.y); + } + else if (node->mGradient.type == GradientRadial) + { + grad = efl_add(EFL_CANVAS_VG_GRADIENT_RADIAL_CLASS, parent); + efl_gfx_gradient_radial_center_set(grad, node->mGradient.center.x, node->mGradient.center.y); + efl_gfx_gradient_radial_focal_set(grad, node->mGradient.focal.x, node->mGradient.focal.y); + efl_gfx_gradient_radial_radius_set(grad, node->mGradient.cradius); + } + else + ERR("No reserved gradient type = %d", node->mGradient.type); + + if (grad) + { + //Gradient Stop + Efl_Gfx_Gradient_Stop* stops = malloc(sizeof(Efl_Gfx_Gradient_Stop) * node->mGradient.stopCount); + if (stops) + { + for (unsigned int i = 0; i < node->mGradient.stopCount; i++) + { + stops[i].offset = node->mGradient.stopPtr[i].pos; + float pa = ((float)node->mGradient.stopPtr[i].a) / 255; + stops[i].r = (int)(((float)node->mGradient.stopPtr[i].r) * pa); + stops[i].g = (int)(((float)node->mGradient.stopPtr[i].g) * pa); + stops[i].b = (int)(((float)node->mGradient.stopPtr[i].b) * pa); + stops[i].a = node->mGradient.stopPtr[i].a; + } + efl_gfx_gradient_stop_set(grad, stops, node->mGradient.stopCount); + free(stops); + } + if (node->mStroke.enable) + efl_canvas_vg_shape_stroke_fill_set(shape, grad); + else + efl_canvas_vg_shape_fill_set(shape, grad); + } + } + break; + default: + ERR("No reserved brush type = %d", node->mBrushType); + } + + //3: Fill Rule + if (node->mFillRule == FillEvenOdd) + efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_ODD_EVEN); + else if (node->mFillRule == FillWinding) + efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_WINDING); + } +} + +static void +_update_vg_tree(Efl_Canvas_Vg_Container *root, const LOTLayerNode *layer, int depth EINA_UNUSED) +{ + if (!layer->mVisible) + { + efl_gfx_entity_visible_set(root, EINA_FALSE); + return; + } + efl_gfx_entity_visible_set(root, EINA_TRUE); + + Efl_Canvas_Vg_Container *ptree = NULL; + + //Note: We assume that if matte is valid, next layer must be a matte source. + LOTMatteType matte = MatteNone; + + //Is this layer a container layer? + for (unsigned int i = 0; i < layer->mLayerList.size; i++) + { + LOTLayerNode *clayer = layer->mLayerList.ptr[i]; + + char *key = _get_key_val(clayer); + Efl_Canvas_Vg_Container *ctree = efl_key_data_get(root, key); + if (!ctree) + { + ctree = efl_add(EFL_CANVAS_VG_CONTAINER_CLASS, root); + efl_key_data_set(root, key, ctree); + } +#if DEBUG + for (int i = 0; i < depth; i++) printf(" "); + printf("%s (%p) matte:%d => %p\n", efl_class_name_get(efl_class_get(ctree)), ctree, matte, ptree); +#endif + _update_vg_tree(ctree, clayer, depth+1); + + //TODO: Only valid for MatteAlphaInverse? + //TODO: Set this blending option to efl_canvas_vg_node... + if (matte != MatteNone) + efl_canvas_vg_node_mask_set(ptree, ctree, matte); + + matte = clayer->mMatte; + ptree = ctree; + + //Debug Matte Info + switch (matte) + { + case MatteNone: + case MatteAlphaInv: + case MatteAlpha: + break; + case MatteLuma: + ERR("TODO: MatteLuma"); + break; + case MatteLumaInv: + ERR("TODO: MatteLumaInv"); + break; + default: + ERR("No reserved Matte type = %d", matte); + } + } + + //Construct drawable nodes. + if (layer->mNodeList.size > 0) + _construct_drawable_nodes(root, layer, depth); +} +#endif + +Eina_Bool +vg_common_json_create_vg_node(Vg_File_Data *vfd) +{ +#ifdef BUILD_VG_LOADER_JSON + Lottie_Animation *lot_anim = (Lottie_Animation *) vfd->loader_data; + if (!lot_anim) return EINA_FALSE; + + unsigned int frame_num = (vfd->anim_data) ? vfd->anim_data->frame_num : 0; + const LOTLayerNode *tree = + lottie_animation_render_tree(lot_anim, frame_num, + vfd->view_box.w, vfd->view_box.h); +#if DEBUG + printf("%s (%p)\n", efl_class_name_get(efl_class_get(vfd->root)), vfd->root); +#endif + + //Root node + Efl_Canvas_Vg_Container *root = vfd->root; + if (!root) + { + root = efl_add_ref(EFL_CANVAS_VG_CONTAINER_CLASS, NULL); + if (!root) return EINA_FALSE; + efl_key_data_set(root, _get_key_val((void *) tree), tree); + vfd->root = root; + } + + _update_vg_tree(root, tree, 1); +#else + return EINA_FALSE; +#endif + return EINA_TRUE; +}