};
static void
+_drop_vg_image(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd)
+{
+ if (!pd->image) return;
+ ENFN->image_free(ENC, pd->image);
+ pd->image = NULL;
+}
+
+static void
+_drop_vg_caches(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd)
+{
+ if (!pd->vg_entry) return;
+ if (pd->ckeys[0]) ENFN->tvg_image_cache_drop(ENC, pd->ckeys[0]);
+ if (pd->ckeys[1]) ENFN->tvg_image_cache_drop(ENC, pd->ckeys[1]);
+ pd->ckeys[0] = NULL;
+ pd->ckeys[1] = NULL;
+
+ _drop_vg_image(obj, pd);
+}
+
+static void
_update_vgtree_viewport(Eo *obj, Efl_Canvas_Vg_Object_Data *pd)
{
double vb_w, vb_h, vp_w, vp_h, scale_w, scale_h, scale;
}
EOLIAN static Efl_VG *
-_efl_canvas_vg_object_root_node_get(const Eo *obj, Efl_Canvas_Vg_Object_Data *pd)
+_efl_canvas_vg_object_root_node_get(const Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd)
{
Efl_VG *root;
if (pd->vg_entry)
{
Evas_Coord w, h;
- evas_object_geometry_get(obj, NULL, NULL, &w, &h);
+ evas_object_geometry_get(eo_obj, NULL, NULL, &w, &h);
//Update vg data with current size.
if ((pd->vg_entry->w != w) || (pd->vg_entry->h != h))
{
+ Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
Vg_Cache_Entry *vg_entry = evas_cache_vg_entry_resize(pd->vg_entry, w, h);
+ _drop_vg_caches(obj, pd);
evas_cache_vg_entry_del(pd->vg_entry);
pd->vg_entry = vg_entry;
}
// check if a file has been already set
if (pd->vg_entry)
{
+ _drop_vg_caches(obj, pd);
evas_cache_vg_entry_del(pd->vg_entry);
pd->vg_entry = NULL;
}
// detach/free the old root_node
if (pd->user_entry && pd->user_entry->root)
{
+ ENFN->tvg_image_cache_drop(ENC, pd->user_entry->root);
efl_canvas_vg_node_vg_obj_set(pd->user_entry->root, NULL, NULL);
efl_replace(&pd->user_entry->root, NULL);
}
}
EOLIAN static Eina_Error
-_efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd EINA_UNUSED, const char *file)
+_efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const char *file)
{
/* Careful: delete previous vg entry.
When a new efl file is set, ex-file will be invalid.
if ((pl != cl) || (pname && file && strcmp(pname, file)))
{
- Evas_Object_Protected_Data *obj;
- obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
+ Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
+ _drop_vg_caches(obj, pd);
evas_cache_vg_entry_del(pd->vg_entry);
evas_object_change(eo_obj, obj);
pd->vg_entry = NULL;
- if (pd->image)
- {
- ENFN->image_free(ENC, pd->image);
- pd->image = NULL;
- }
evas_object_change(eo_obj, obj);
pd->changed = EINA_TRUE;
}
{
if (!efl_file_loaded_get(eo_obj)) return;
- Evas_Object_Protected_Data *obj;
- obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
+ Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
+ _drop_vg_caches(obj, pd);
evas_cache_vg_entry_del(pd->vg_entry);
evas_object_change(eo_obj, obj);
pd->vg_entry = NULL;
- if (pd->image)
- {
- ENFN->image_free(ENC, pd->image);
- pd->image = NULL;
- }
}
EOLIAN static Eina_Bool
efl_gfx_entity_visible_set(efl_super(eo_obj, MY_CLASS), vis);
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
-
- if (!vis && pd->image)
- {
- ENFN->image_free(ENC, pd->image);
- pd->image = NULL;
- }
+ _drop_vg_caches(obj, pd);
}
EOLIAN static void
if (pd->user_entry)
{
+ ENFN->tvg_image_cache_drop(ENC, pd->user_entry->root);
if (pd->user_entry->root) efl_unref(pd->user_entry->root);
free(pd->user_entry);
}
pd->user_entry = NULL;
+ _drop_vg_caches(obj, pd);
evas_cache_vg_entry_del(pd->vg_entry);
if (pd->tvg_canvas)
tvg_canvas_destroy(pd->tvg_canvas);
}
- if (pd->image)
- {
- ENFN->image_free(ENC, pd->image);
- pd->image = NULL;
- }
-
efl_invalidate(efl_super(eo_obj, MY_CLASS));
}
pd->obj = obj;
pd->root = efl_add_ref(EFL_CANVAS_VG_CONTAINER_CLASS, NULL);
pd->tvg_canvas = tvg_swcanvas_create();
- pd->size.w = 0;
- pd->size.h = 0;
-
return eo_obj;
}
}
static void
-_update_scene(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, Efl_VG *root)
+_render_image_to_screen(Evas_Object_Protected_Data *obj,
+ void *engine, void *output, void *context, void *surface, void *image,
+ int x, int y, int w, int h, Eina_Bool async)
{
+ ENFN->image_dirty_region(engine, image, 0, 0, w, h);
+
+ Eina_Bool async_unref = ENFN->image_draw(engine, output, context, surface, image,
+ 0, 0, w, h, x, y, w, h,
+ EINA_TRUE, async);
+ if (async && async_unref)
+ {
+ evas_cache_image_ref(image);
+ evas_unref_queue_image_put(obj->layer->evas, image);
+ }
+}
+
+static void*
+_render_tvg_to_image(Evas_Object_Protected_Data* obj, Efl_Canvas_Vg_Object_Data *pd, void *engine, void *image, Efl_VG* root, int w, int h, void *ckey)
+{
+ Eina_Bool new_image = EINA_FALSE;
+
+ //create a image
+ if (!image)
+ {
+ image = ENFN->tvg_image_new(engine, w, h);
+ if (!image) return NULL;
+ new_image = EINA_TRUE;
+ }
+
+ //prepare for redering
+ int stride = 0;
+ DATA32* pixels = ENFN->tvg_image_acquire(engine, image, &stride);
+ tvg_swcanvas_set_target(pd->tvg_canvas, (uint32_t*) pixels, stride, w, h, TVG_COLORSPACE_ARGB8888);
+
+ //update scene
if (!efl_isa(root, EFL_CANVAS_VG_GRADIENT_CLASS))
{
Efl_Canvas_Vg_Node_Data *nd = efl_data_scope_get(root, EFL_CANVAS_VG_NODE_CLASS);
if (nd) nd->render_pre(obj, root, nd, NULL, pd->tvg_canvas, NULL);
}
-}
-static void
-_render_image_to_screen(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
- void *engine, void *output, void *context, void *surface,
- int x, int y, Eina_Bool async)
-{
- if (!pd || !pd->image) return;
+ //raterize
+ if (tvg_canvas_draw(pd->tvg_canvas) == TVG_RESULT_SUCCESS)
+ tvg_canvas_sync(pd->tvg_canvas);
- ENFN->image_dirty_region(engine, pd->image, 0, 0, pd->size.w, pd->size.h);
+ ENFN->tvg_image_release(engine, image, pixels);
- Eina_Bool async_unref = ENFN->image_draw(engine, output, context, surface, pd->image, 0, 0,
- pd->size.w, pd->size.h, x, y, pd->size.w, pd->size.h,
- EINA_TRUE, async);
- if (async && async_unref)
+ tvg_canvas_clear(pd->tvg_canvas, EINA_FALSE);
+
+ //caching
+ if (new_image && ckey)
{
- evas_cache_image_ref(pd->image);
- evas_unref_queue_image_put(obj->layer->evas, pd->image);
+ //Drop ex invalid cache images.
+ if (pd->frame_idx == 0 && ckey != pd->ckeys[0])
+ {
+ if (pd->ckeys[0]) ENFN->tvg_image_cache_drop(engine, pd->ckeys[0]);
+ pd->ckeys[0] = ckey;
+ }
+ else if (pd->frame_idx == (int) (evas_cache_vg_anim_frame_count_get(pd->vg_entry) - 1)
+ && ckey != pd->ckeys[1])
+ {
+ if (pd->ckeys[1]) ENFN->tvg_image_cache_drop(engine, pd->ckeys[1]);
+ pd->ckeys[1] = ckey;
+ }
+ ENFN->tvg_image_cache_set(engine, ckey, image);
}
+
+ return image;
}
static void
-_prepare_render_image(Evas_Object_Protected_Data* obj, Efl_Canvas_Vg_Object_Data *pd, void *engine)
+_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)
{
- if (pd->image) ENFN->image_free(engine, pd->image);
+ Vg_User_Entry *user_entry = pd->user_entry;
- pd->image = ENFN->tvg_image_new(engine, pd->size.w, pd->size.h);
- if (!pd->image) return;
+ //if the size doesn't match, drop previous cache surface.
+ if ((user_entry->w != w) || (user_entry->h != h))
+ {
+ ENFN->tvg_image_cache_drop(engine, user_entry->root);
+ user_entry->w = w;
+ user_entry->h = h;
+ }
- int stride = 0;
- DATA32* pixels = ENFN->tvg_image_acquire(engine, pd->image, &stride);
+ void *image = ENFN->tvg_image_cache_get(engine, user_entry->root);
- tvg_swcanvas_set_target(pd->tvg_canvas, (uint32_t*) pixels, stride, pd->size.w, pd->size.h, TVG_COLORSPACE_ARGB8888);
+ if (!image)
+ {
+ image = _render_tvg_to_image(obj, pd, engine, NULL, user_entry->root, w, h, user_entry->root);
+ }
+ else
+ {
+ if (pd->changed)
+ {
+ image = _render_tvg_to_image(obj, pd, engine, image, user_entry->root, w, h, NULL);
+ }
+ //cache reference was increased when we get the cache.
+ ENFN->tvg_image_cache_drop(engine, user_entry->root);
+ }
- ENFN->tvg_image_release(engine, pd->image, pixels);
+ _render_image_to_screen(obj, engine, output, context, surface, image, x, y, w, h, do_async);
}
+
static void
-_efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
- Evas_Object_Protected_Data *obj,
- void *type_private_data,
- void *engine, void *output EINA_UNUSED, void *context, void *surface EINA_UNUSED,
- int x, int y, Eina_Bool do_async)
+_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 cacheable, Eina_Bool do_async)
{
- Efl_Canvas_Vg_Object_Data *pd = type_private_data;
- Eina_Position2D offset = {0, 0}; //Offset after keeping aspect ratio.
- ENFN->context_color_set(engine, context, 255, 255, 255, 255);
- ENFN->context_multiplier_set(engine, context,
- obj->cur->cache.clip.r,
- obj->cur->cache.clip.g,
- obj->cur->cache.clip.b,
- obj->cur->cache.clip.a);
- ENFN->context_anti_alias_set(engine, context, obj->cur->anti_alias);
- ENFN->context_render_op_set(engine, context, obj->cur->render_op);
-
- int w = obj->cur->geometry.w;
- int h = obj->cur->geometry.h;
-
Vg_Cache_Entry *vg_entry = pd->vg_entry;
+ Eina_Position2D offset = {0, 0}; //Offset after keeping aspect ratio.
//Update the size only when user entry is not valid.
- if (!pd->user_entry && vg_entry)
+ evas_cache_vg_entry_value_provider_update(vg_entry, efl_key_data_get(obj->object, "_vg_value_providers"));
+
+ // if the size changed in between path set and the draw call;
+ if ((vg_entry->w != w) || (vg_entry->h != h))
{
- evas_cache_vg_entry_value_provider_update(vg_entry, efl_key_data_get(obj->object, "_vg_value_providers"));
+ Eina_Size2D size = evas_cache_vg_entry_default_size_get(vg_entry);
- // if the size changed in between path set and the draw call;
- if ((vg_entry->w != w) || (vg_entry->h != h))
+ //adjust size for aspect ratio.
+ if (size.w > 0 && size.h > 0)
{
- Eina_Size2D size = evas_cache_vg_entry_default_size_get(vg_entry);
+ float rw = (float) w / (float) size.w;
+ float rh = (float) h / (float) size.h;
- //adjust size for aspect ratio.
- if (size.w > 0 && size.h > 0)
+ if (rw < rh)
{
- 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;
- }
+ size.w = w;
+ size.h = (int) ((float) size.h * rw);
}
else
{
- size.w = w;
+ 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))
- {
- 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;
+ //Size is changed, cached data is invalid.
+ if ((size.w != vg_entry->w) || (size.h != vg_entry->h))
+ {
+ vg_entry = evas_cache_vg_entry_resize(vg_entry, size.w, size.h);
+ _drop_vg_caches(obj, pd);
+ 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;
}
- Eina_Bool resized = EINA_FALSE;
- Eina_Bool prepared = EINA_FALSE;
+ Efl_VG *root = evas_cache_vg_tree_get(pd->vg_entry, pd->frame_idx);
+ if (!root) return;
- //Reset canvas size
- if (pd->size.w != w || pd->size.h != h)
- {
- pd->size.w = w;
- pd->size.h = h;
- resized = EINA_TRUE;
- }
+ void *key = NULL;
+ void *image = NULL;
- if (pd->vg_entry && (resized || pd->changed))
+ if (cacheable)
{
- Efl_VG *root = evas_cache_vg_tree_get(pd->vg_entry, pd->frame_idx);
- if (root)
+ key = evas_cache_vg_surface_key_get(root, w, h, pd->frame_idx);
+ if (key) image = ENFN->tvg_image_cache_get(engine, key);
+
+ if (!image)
{
- if (resized && !prepared)
- {
- _prepare_render_image(obj, pd, engine);
- prepared = EINA_TRUE;
- }
- _update_scene(obj, pd, root);
+ image = _render_tvg_to_image(obj, pd, engine, NULL, root, w, h, key);
}
- }
-
- if (pd->user_entry && (resized || pd->changed))
- {
- if (resized && !prepared)
+ else
{
- _prepare_render_image(obj, pd, engine);
- prepared = EINA_TRUE;
+ //cache reference was increased when we get the cache.
+ if (key) ENFN->tvg_image_cache_drop(engine, key);
}
- _update_scene(obj, pd, pd->user_entry->root);
}
-
- //Render the vector canvas
- if ((resized || pd->changed) && pd->image)
+ //Non-cacheable... such as intermediate animation frames.
+ else
{
- DATA32 *pixels = ENFN->tvg_image_acquire(engine, pd->image, NULL);
+ if (w != pd->size.w || h != pd->size.h) _drop_vg_image(obj, pd);
+ image = _render_tvg_to_image(obj, pd, engine, pd->image, root, w, h, NULL);
+ pd->image = image;
+ pd->size.w = w;
+ pd->size.h = h;
+ }
- if (tvg_canvas_draw(pd->tvg_canvas) == TVG_RESULT_SUCCESS)
- tvg_canvas_sync(pd->tvg_canvas);
+ _render_image_to_screen(obj, engine, output, context, surface, image,
+ x + offset.x, y + offset.y, w, h, do_async);
+}
- ENFN->tvg_image_release(engine, pd->image, pixels);
+static void
+_efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
+ Evas_Object_Protected_Data *obj,
+ void *type_private_data,
+ void *engine, void *output EINA_UNUSED, void *context, void *surface EINA_UNUSED,
+ int x, int y, Eina_Bool do_async)
+{
+ Efl_Canvas_Vg_Object_Data *pd = type_private_data;
- tvg_canvas_clear(pd->tvg_canvas, EINA_FALSE);
+ ENFN->context_color_set(engine, context, 255, 255, 255, 255);
+ ENFN->context_multiplier_set(engine, context,
+ obj->cur->cache.clip.r,
+ obj->cur->cache.clip.g,
+ obj->cur->cache.clip.b,
+ obj->cur->cache.clip.a);
+ ENFN->context_anti_alias_set(engine, context, obj->cur->anti_alias);
+ ENFN->context_render_op_set(engine, context, obj->cur->render_op);
+
+ //Cache tvg image?
+ 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;
+ //Drop owned image since we gonna use cached image.
+ _drop_vg_image(obj, pd);
+ }
+
+ 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, cacheable, do_async);
}
- _render_image_to_screen(obj, pd, engine, output, context, surface,
- obj->cur->geometry.x + x + offset.x,
- obj->cur->geometry.y + y + offset.y, do_async);
+ 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);
+ }
pd->changed = EINA_FALSE;
}