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
This commit is contained in:
Hermet Park 2019-01-09 16:22:52 +09:00
parent 865c60373a
commit 5b0c43cbbc
18 changed files with 1016 additions and 101 deletions

View File

@ -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

View File

@ -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"])

View File

@ -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']
)

View File

@ -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

View File

@ -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"

View File

@ -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; }
}
}

View File

@ -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);

View File

@ -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

View File

@ -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);
};

View File

@ -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;

View File

@ -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')

View File

@ -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;

View File

@ -0,0 +1,145 @@
#include <lottieanimation_capi.h>
#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

View File

@ -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

View File

@ -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);
}

View File

@ -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,
)

View File

@ -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_

View File

@ -0,0 +1,290 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "vg_common.h"
#include <Evas.h>
#ifdef BUILD_VG_LOADER_JSON
#include <lottieanimation_capi.h>
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;
}