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) {