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 <jsuya.choi@samsung.com>

Reviewers: #committers, jsuya

Subscribers: cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D8941
This commit is contained in:
Hermet Park 2019-06-21 17:32:37 +09:00
parent 23af6ec640
commit 625e11f584
7 changed files with 423 additions and 51 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

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[] =
@ -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)
{