From 625e11f584fa00c6a2adc160ed20d96f3763d130 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 21 Jun 2019 17:32:37 +0900 Subject: [PATCH] evas vector: support lottie animation as using json loader. Summary: This patch extends efl_canvas_vg_object class to implement efl_gfx_frame_controller to suppor any playable animation on it. Plus, vector object takes care of lottie animation by using json loader. it's caching mechanism is changed to cache only static frame, not all frames. vg_cache supports json loader and make it animation request properly. This feature has been stabilized enough, it's using in Samsung Galaxy Watch active product, proved its stability enough. Depends on {D8940} Co-authored-by: JunsuChoi Reviewers: #committers, jsuya Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D8941 --- src/lib/evas/Evas_Legacy.h | 76 ++++++ src/lib/evas/canvas/efl_canvas_vg_container.c | 8 +- src/lib/evas/canvas/efl_canvas_vg_object.c | 223 ++++++++++++++++-- src/lib/evas/canvas/efl_canvas_vg_object.eo | 18 +- src/lib/evas/canvas/evas_vg_private.h | 9 +- src/lib/evas/include/evas_private.h | 15 +- src/lib/evas/vg/evas_vg_cache.c | 125 ++++++++-- 7 files changed, 423 insertions(+), 51 deletions(-) diff --git a/src/lib/evas/Evas_Legacy.h b/src/lib/evas/Evas_Legacy.h index 3e4ae59c25..ff92ad8bbe 100644 --- a/src/lib/evas/Evas_Legacy.h +++ b/src/lib/evas/Evas_Legacy.h @@ -3613,6 +3613,82 @@ EAPI Evas_Object *evas_object_rectangle_add(Evas *e) EINA_WARN_UNUSED_RESULT EIN */ EAPI Evas_Object *evas_object_vg_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; +/** + * Get the total number of frames of the vector, if it's animated. + * + * @return The number of frames. 0, if it's not animated. + * + * @since 1.23 + */ +EAPI int evas_object_vg_animated_frame_count_get(const Evas_Object *obj) EINA_ARG_NONNULL(1); + +/** + * Get the duration of a sequence of frames. + * + * This returns total duration in seconds that the specified + * sequence of frames should take. + * + * If @p start_frame is 1 and @p frame_num is 0, this returns the + * duration of frame 1. If @p start_frame is 1 and @p frame_num is 1, + * this returns the total duration of frame 1 + frame 2. + * + * @param[in] start_frame The first frame, ranges from 1 to maximum frame count. + * @param[in] frame_num Number of frames in the sequence, starts from 0. + * + * @return Duration in seconds. + * + * @see evas_object_vg_animated_frame_count_get() + * @since 1.23 + */ +EAPI double evas_object_vg_animated_frame_duration_get(const Evas_Object *obj, int start_frame EINA_UNUSED, int frame_num EINA_UNUSED) EINA_ARG_NONNULL(1); + +/** + * + * Set the source file from where an vector object must fetch the real + * vector data (it may be one of json, svg, eet files). + * + * If the file supports multiple data stored in it (as Eet files do), + * you can specify the key to be used as the index of the vector in + * this file. + * + * @param[in] file The vector file path. + * @param[in] key The vector key in @p file (if its an Eet one), or @c +NULL, otherwise. + * + * @return @c EINA_TRUE if it's succeed to read file, @c EINA_FALSE otherwise. + * + * @since 1.23 + */ +EAPI Eina_Bool evas_object_vg_file_set(Evas_Object *obj, const char *file, const char *key); + +/** + * Set current frame of animated vector object. + * + * @param[in] frame_index The index of current frame. + * + * @note the @p frame_index must be in range of animation frames. (0 ~ max frame count) + * + * @return @c EINA_TRUE, if the frame index is valid. @c EINA_FALSE, otherwise. + * + * @see evas_object_vg_animated_frame_count_get() + * + * @since 1.23 + */ +EAPI Eina_Bool evas_object_vg_animated_frame_set(Evas_Object *obj, int frame_index) EINA_ARG_NONNULL(1, 2); + +/** + * Get the current frame number of animated vector object. + * + * @return The frame index. + * + * @see evas_object_vg_animated_frame_set() + * @see evas_object_vg_animated_frame_count_get() + * + * @since 1.23 + */ +EAPI int evas_object_vg_animated_frame_get(const Evas_Object *obj) EINA_ARG_NONNULL(1); + + #include "canvas/efl_canvas_vg_node_eo.legacy.h" #include "canvas/efl_canvas_vg_object_eo.legacy.h" #include "canvas/efl_canvas_vg_container_eo.legacy.h" diff --git a/src/lib/evas/canvas/efl_canvas_vg_container.c b/src/lib/evas/canvas/efl_canvas_vg_container.c index 2c55071a4d..4d11e0b5cb 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_container.c +++ b/src/lib/evas/canvas/efl_canvas_vg_container.c @@ -124,7 +124,7 @@ _prepare_mask(Evas_Object_Protected_Data *obj, //vector object if (!pd->mask.buffer) ERR("Mask Buffer is invalid"); //FIXME: This code means that there is another masking container. - if (pd->mask.option != EFL_CANVAS_VG_NODE_BLEND_TYPE_NONE) + if (pd->mask.option >= EFL_CANVAS_VG_NODE_BLEND_TYPE_MASK_ADD) { Efl_Canvas_Vg_Container_Data *src_pd = pd; mask = pd->mask.buffer; @@ -178,7 +178,11 @@ _efl_canvas_vg_container_render_pre(Evas_Object_Protected_Data *vg_pd, EFL_CANVAS_VG_COMPUTE_MATRIX(ctransform, ptransform, nd); //Container may have mask source. - if (pd->mask_src && !pd->mask.target) + //FIXME : _prepare_mask() should only work in cases with matte or main mask. + // This condition is valid because the main mask use same type as matte alpha. + if (pd->mask_src && + (pd->mask.option == EFL_CANVAS_VG_NODE_BLEND_TYPE_ALPHA || + pd->mask.option == EFL_CANVAS_VG_NODE_BLEND_TYPE_ALPHA_INV)) { mask_op = pd->mask.option; mask = _prepare_mask(vg_pd, pd->mask_src, diff --git a/src/lib/evas/canvas/efl_canvas_vg_object.c b/src/lib/evas/canvas/efl_canvas_vg_object.c index 026640a80a..e8c1524432 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; @@ -459,7 +459,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 *buffer, - Eina_Bool do_async) + Eina_Bool do_async, Eina_Bool cacheable) { Ector_Surface *ector; RGBA_Draw_Context *context; @@ -502,10 +502,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) + if (buffer_created && cacheable) { //Use root as a cache key. ENFN->ector_surface_cache_set(engine, root, buffer); + pd->cached_frame_idx = pd->frame_idx; } return buffer; @@ -516,7 +517,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; @@ -532,49 +533,107 @@ _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. + Eina_Bool drop_cache = EINA_FALSE; + void *buffer = NULL; // 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; + Eina_Size2D size = evas_cache_vg_entry_default_size_get(pd->vg_entry); + + //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; + } + + //Size is changed, cached data is invalid. + if ((size.w != vg_entry->w) || (size.h != vg_entry->h)) + { + drop_cache = EINA_TRUE; + 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); + root = evas_cache_vg_tree_get(vg_entry, pd->frame_idx); if (!root) return; - void *buffer = ENFN->ector_surface_cache_get(engine, root); + if (cacheable) + { + //if the size doesn't match, drop previous cache surface. + if (drop_cache) + ENFN->ector_surface_cache_drop(engine, (void *) root); + //Cache Hit! + else if (pd->frame_idx == pd->cached_frame_idx) + buffer = ENFN->ector_surface_cache_get(engine, (void *) root); + //Drop invalid one. + else + ENFN->ector_surface_cache_drop(engine, (void *) root); + } if (!buffer) - buffer = _render_to_buffer(obj, pd, engine, root, w, h, NULL, do_async); + buffer = _render_to_buffer(obj, pd, engine, root, w, h, NULL, + 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; @@ -588,13 +647,16 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj, } //if the buffer is not created yet - void *buffer = ENFN->ector_surface_cache_get(engine, user_entry->root); + void *buffer = NULL; + + if (cacheable) + buffer = ENFN->ector_surface_cache_get(engine, user_entry->root); if (!buffer) { // render to the buffer buffer = _render_to_buffer(obj, pd, engine, user_entry->root, - w, h, buffer, do_async); + w, h, buffer, do_async, cacheable); } else { @@ -602,9 +664,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj, if (pd->changed) buffer = _render_to_buffer(obj, pd, engine, user_entry->root, - w, h, - buffer, - do_async); + w, h, buffer, + do_async, EINA_FALSE); //cache reference was increased when we get the cache. ENFN->ector_surface_cache_drop(engine, user_entry->root); } @@ -613,7 +674,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 @@ -635,19 +696,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) - 1))) + 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; } @@ -804,6 +876,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_frame_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_frame_controller_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_Frame_Controller_Loop_Hint +_efl_canvas_vg_object_efl_gfx_frame_controller_loop_type_get(const Eo *eo_obj EINA_UNUSED, + Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED) +{ + //TODO: + return EFL_GFX_FRAME_CONTROLLER_LOOP_HINT_NONE; +} + +EOLIAN static int +_efl_canvas_vg_object_efl_gfx_frame_controller_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_frame_controller_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_frame_controller_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_frame_controller_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) @@ -814,5 +959,35 @@ 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_frame_controller_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_frame_controller_frame_duration_get(obj, start_frame, frame_num); +} + +EAPI int +evas_object_vg_animated_frame_count_get(const Evas_Object *obj) +{ + return efl_gfx_frame_controller_frame_count_get(obj); +} + +EAPI Eina_Bool +evas_object_vg_animated_frame_set(Evas_Object *obj, int frame_index) +{ + return efl_gfx_frame_controller_frame_set(obj, frame_index); +} + +EAPI Eina_Bool +evas_object_vg_file_set(Evas_Object *obj, const char *file, const char *key) +{ + return efl_file_simple_load(obj, file, key); +} + #include "efl_canvas_vg_object.eo.c" #include "efl_canvas_vg_object_eo.legacy.c" diff --git a/src/lib/evas/canvas/efl_canvas_vg_object.eo b/src/lib/evas/canvas/efl_canvas_vg_object.eo index 53bc6de0ea..12411eab37 100644 --- a/src/lib/evas/canvas/efl_canvas_vg_object.eo +++ b/src/lib/evas/canvas/efl_canvas_vg_object.eo @@ -21,7 +21,8 @@ enum @beta Efl.Canvas.Vg.Fill_Mode dimension of the viewport.]] } -class @beta Efl.Canvas.Vg.Object extends Efl.Canvas.Object implements Efl.File, Efl.File_Save +class @beta Efl.Canvas.Vg.Object extends Efl.Canvas.Object implements Efl.File, Efl.File_Save, + Efl.Gfx.Frame_Controller { [[Efl vector graphics class]] methods { @@ -67,6 +68,15 @@ class @beta Efl.Canvas.Vg.Object extends Efl.Canvas.Object implements 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; @@ -76,5 +86,11 @@ class @beta Efl.Canvas.Vg.Object extends Efl.Canvas.Object implements Efl.File, Efl.File.unload; Efl.File.file { set; } Efl.File_Save.save; + Efl.Gfx.Frame_Controller.animated { get; } + Efl.Gfx.Frame_Controller.frame { get; set; } + Efl.Gfx.Frame_Controller.frame_count { get; } + Efl.Gfx.Frame_Controller.loop_type { get; } + Efl.Gfx.Frame_Controller.loop_count { get; } + Efl.Gfx.Frame_Controller.frame_duration { get; } } } diff --git a/src/lib/evas/canvas/evas_vg_private.h b/src/lib/evas/canvas/evas_vg_private.h index 73ff63e7d8..0fad40d15c 100644 --- a/src/lib/evas/canvas/evas_vg_private.h +++ b/src/lib/evas/canvas/evas_vg_private.h @@ -23,7 +23,7 @@ typedef struct _Vg_Cache_Entry 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; } Vg_Cache_Entry; @@ -48,6 +48,8 @@ struct _Efl_Canvas_Vg_Object_Data Eina_Array cleanup; double align_x, align_y; Efl_Canvas_Vg_Fill_Mode fill_mode; + int frame_idx; + int cached_frame_idx; Eina_Bool changed : 1; }; @@ -123,11 +125,14 @@ 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 Eina_File *file, const char *key, int w, int h); -Efl_VG* evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry); +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 Eina_File *file, const char *key); Eina_Bool evas_cache_vg_file_save(Efl_VG *root, int w, int h, const char *file, const char *key, const Efl_File_Save_Info *info); Eina_Bool evas_cache_vg_entry_file_save(Vg_Cache_Entry *vg_entry, const char *file, const char *key, const Efl_File_Save_Info *info); +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/include/evas_private.h b/src/lib/evas/include/evas_private.h index 7d6d1c452f..d6b53bc210 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -139,6 +139,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 { @@ -1510,13 +1511,25 @@ 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 no_share : 1; //Shareable VFD through multiple file open requests. Eina_Bool static_viewbox: 1; Eina_Bool preserve_aspect : 1; //Used in SVG }; diff --git a/src/lib/evas/vg/evas_vg_cache.c b/src/lib/evas/vg/evas_vg_cache.c index 534dca2748..dd85349ee9 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[] = @@ -153,19 +153,26 @@ _evas_cache_vg_entry_free_cb(void *data) if (vg_entry->vfd->ref <= 0) { - Eina_Strbuf *hash_key = eina_strbuf_new(); - eina_strbuf_append_printf(hash_key, "%s/%s", - 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); - eina_strbuf_free(hash_key); + if (vg_entry->vfd->no_share) + vg_entry->vfd->loader->file_close(vg_entry->vfd); + else + { + Eina_Strbuf *hash_key = eina_strbuf_new(); + eina_strbuf_append_printf(hash_key, "%s/%s", + 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); + eina_strbuf_free(hash_key); + } } } 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); } @@ -195,9 +202,37 @@ _vg_file_save(Vg_File_Data *vfd, const char *file, const char *key, const Efl_Fi } 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 @@ -214,12 +249,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); + } } } @@ -294,11 +351,12 @@ evas_cache_vg_file_open(const Eina_File *file, const char *key) hash_key = eina_strbuf_new(); 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) + if (!vfd || vfd->no_share) { vfd = _vg_load_from_file(file, key); //File exists. - if (vfd) eina_hash_add(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vfd); + if (vfd && !vfd->no_share) + eina_hash_add(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vfd); } eina_strbuf_free(hash_key); return vfd; @@ -356,8 +414,24 @@ evas_cache_vg_entry_create(const Eina_File *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; @@ -365,7 +439,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) @@ -374,13 +448,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 @@ -393,6 +469,13 @@ 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 Efl_File_Save_Info *info) {