efl/src/lib/evas/vg/evas_vg_cache.c

517 lines
14 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_vg_private.h"
static Vg_Cache* vg_cache = NULL;
struct ext_loader_s
{
unsigned int length;
const char *extension;
const char *loader;
};
struct ext_saver_s
{
unsigned int length;
const char *extension;
const char *saver;
};
#define MATCHING(Ext, Module) { sizeof(Ext)-1, Ext, Module }
static const struct ext_loader_s loaders[] =
{ /* map extensions to loaders to use for good first-guess tries */
MATCHING(".eet", "eet"),
MATCHING(".edj", "eet"),
MATCHING(".svg", "svg"),
MATCHING(".svgz", "svg"),
MATCHING(".svg.gz", "svg")
};
static const char *loaders_name[] =
{ /* in order of most likely needed */
"eet", "json", "svg"
};
static const struct ext_saver_s savers[] =
{ /* map extensions to savers to use for good first-guess tries */
MATCHING(".eet", "eet"),
MATCHING(".edj", "eet"),
MATCHING(".svg", "svg")
};
static Evas_Module *
_find_loader_module(const char *file)
{
const char *loader = NULL, *end;
Evas_Module *em = NULL;
unsigned int i;
int len, len2;
len = strlen(file);
end = file + len;
for (i = 0; i < (sizeof (loaders) / sizeof(struct ext_loader_s)); i++)
{
len2 = loaders[i].length;
if (len2 > len) continue;
if (!strcasecmp(end - len2, loaders[i].extension))
{
loader = loaders[i].loader;
break;
}
}
if (loader)
em = evas_module_find_type(EVAS_MODULE_TYPE_VG_LOADER, loader);
return em;
}
static Vg_File_Data *
_vg_load_from_file(const Eina_File *file, const char *key)
{
Evas_Module *em;
Evas_Vg_Load_Func *loader;
int error = EVAS_LOAD_ERROR_GENERIC;
Vg_File_Data *vfd;
unsigned int i;
const char *file_name = eina_file_filename_get(file);
em = _find_loader_module(file_name);
if (em)
{
loader = em->functions;
vfd = loader->file_open((Eina_File *) file, key, &error);
if (vfd)
{
vfd->loader = loader;
return vfd;
}
}
else
{
for (i = 0; i < sizeof (loaders_name) / sizeof (char *); i++)
{
em = evas_module_find_type(EVAS_MODULE_TYPE_VG_LOADER, loaders_name[i]);
if (em)
{
loader = em->functions;
vfd = loader->file_open((Eina_File *) file, key, &error);
if (vfd)
{
vfd->loader = loader;
return vfd;
}
}
}
}
WRN("Exhausted all means to load vector file = %s", file_name);
return NULL;
}
static Evas_Module *
_find_saver_module(const char *file)
{
const char *saver = NULL, *end;
Evas_Module *em = NULL;
unsigned int i;
int len, len2;
len = strlen(file);
end = file + len;
for (i = 0; i < (sizeof (savers) / sizeof(struct ext_saver_s)); i++)
{
len2 = savers[i].length;
if (len2 > len) continue;
if (!strcasecmp(end - len2, savers[i].extension))
{
saver = savers[i].saver;
break;
}
}
if (saver)
em = evas_module_find_type(EVAS_MODULE_TYPE_VG_SAVER, saver);
return em;
}
static void
_evas_cache_vg_data_free_cb(void *data)
{
Vg_File_Data *vfd = data;
vfd->loader->file_close(vfd);
}
static void
_evas_cache_vg_entry_free_cb(void *data)
{
Vg_Cache_Entry *vg_entry = data;
if (vg_entry->vfd)
{
vg_entry->vfd->ref--;
if (vg_entry->vfd->ref <= 0)
{
Eina_Strbuf *hash_key = eina_strbuf_new();
eina_strbuf_append_printf(hash_key, "%s/%s/%p",
eina_file_filename_get(vg_entry->file),
vg_entry->key,
vg_entry->evas);
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);
free(vg_entry);
}
static Eina_Bool
_vg_file_save(Vg_File_Data *vfd, const char *file, const char *key, const Efl_File_Save_Info *info)
{
Evas_Module *em;
Evas_Vg_Save_Func *saver;
Evas_Load_Error error = EVAS_LOAD_ERROR_GENERIC;
int compress = 9;
if (!file) return EINA_FALSE;
if (info) compress = info->compression;
em = _find_saver_module(file);
if (em)
{
saver = em->functions;
error = saver->file_save(vfd, file, key, compress);
}
if (error)
return EINA_FALSE;
return EINA_TRUE;
}
static void
_root_update(Vg_Cache_Entry *vg_entry)
{
Vg_File_Data *vfd = vg_entry->vfd;
/* Optimization: static viewbox may have same root data regardless of size.
So we can't use the root data directly, but copy it for each vg_entries.
In the meantime, non-static viewbox root data may have difference instance for each
size. So it's affordable to share the root data for each vg_entries. */
if (vfd->static_viewbox)
{
/* 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);
}
//Shareable??
else if (vg_entry->root != vfd->root)
{
if (vg_entry->root) efl_unref(vg_entry->root);
vg_entry->root = efl_ref(vfd->root);
}
}
static void
_local_transform(Efl_VG *root, double w, double h, Vg_File_Data *vfd)
{
double sx = 0, sy= 0, scale;
Eina_Matrix3 m;
if (!vfd->static_viewbox) return;
if (vfd->view_box.w == w && vfd->view_box.h == h) return;
sx = w / vfd->view_box.w;
sy = h / vfd->view_box.h;
scale = sx < sy ? sx : sy;
eina_matrix3_identity(&m);
// align hcenter and vcenter
if (vfd->preserve_aspect)
{
eina_matrix3_translate(&m, (w - vfd->view_box.w * scale)/2.0, (h - vfd->view_box.h * scale)/2.0);
eina_matrix3_scale(&m, scale, scale);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
else
{
eina_matrix3_scale(&m, sx, sy);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
efl_canvas_vg_node_transformation_set(root, &m);
}
void
evas_cache_vg_init(void)
{
if (vg_cache)
{
vg_cache->ref++;
return;
}
vg_cache = calloc(1, sizeof(Vg_Cache));
if (!vg_cache)
{
CRI("Failed to alloc Vg_Cache");
return;
}
vg_cache->vfd_hash = eina_hash_string_superfast_new(_evas_cache_vg_data_free_cb);
vg_cache->vg_entry_hash = eina_hash_string_superfast_new(_evas_cache_vg_entry_free_cb);
vg_cache->ref++;
}
void
evas_cache_vg_shutdown(void)
{
if (!vg_cache) return;
vg_cache->ref--;
if (vg_cache->ref > 0) return;
eina_hash_free(vg_cache->vfd_hash);
eina_hash_free(vg_cache->vg_entry_hash);
free(vg_cache);
vg_cache = NULL;
}
Vg_File_Data *
evas_cache_vg_file_open(const Eina_File *file, const char *key, Evas *e)
{
Vg_File_Data *vfd;
Eina_Strbuf *hash_key;
hash_key = eina_strbuf_new();
eina_strbuf_append_printf(hash_key, "%s/%s/%p", eina_file_filename_get(file), key, e);
vfd = eina_hash_find(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key));
if (!vfd)
{
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);
}
eina_strbuf_free(hash_key);
return vfd;
}
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->evas, vg_entry->file, vg_entry->key, w, h, vg_entry->vfd->vp_list);
}
Vg_Cache_Entry*
evas_cache_vg_entry_create(Evas *evas,
const Eina_File *file,
const char *key,
int w, int h, Eina_List *vp_list)
{
Vg_Cache_Entry* vg_entry;
Eina_Strbuf *hash_key;
if (!vg_cache) return NULL;
//TODO: zero-sized entry is useless. how to skip it?
hash_key = eina_strbuf_new();
eina_strbuf_append_printf(hash_key, "%p/%p/%s/%d/%d/%p", evas, file, key, w, h, vp_list);
vg_entry = eina_hash_find(vg_cache->vg_entry_hash, eina_strbuf_string_get(hash_key));
if (!vg_entry)
{
vg_entry = calloc(1, sizeof(Vg_Cache_Entry));
if (!vg_entry)
{
CRI("Failed to alloc Vg_Cache_Entry");
eina_strbuf_free(hash_key);
return NULL;
}
vg_entry->file = file;
vg_entry->key = eina_stringshare_add(key);
vg_entry->w = w;
vg_entry->h = h;
vg_entry->evas = evas;
vg_entry->hash_key = eina_strbuf_string_steal(hash_key);
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->evas);
//No File??
if (!vg_entry->vfd)
{
evas_cache_vg_entry_del(vg_entry);
return NULL;
}
vg_entry->vfd->ref++;
vg_entry->vfd->vp_list = vp_list;
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;
}
Eina_Bool
evas_cache_vg_anim_sector_set(const Vg_Cache_Entry* vg_entry, const char *name, int startframe, int endframe)
{
if (!vg_entry) return EINA_FALSE;
if (!vg_entry->vfd->anim_data) return EINA_FALSE;
if (!vg_entry->vfd->anim_data->markers) return EINA_FALSE;
if (!name) return EINA_FALSE;
Vg_File_Anim_Data_Marker *marker;
Vg_File_Anim_Data_Marker new_marker;
int i = 0;
EINA_INARRAY_FOREACH(vg_entry->vfd->anim_data->markers, marker)
{
if (!strcmp(marker->name, name))
{
marker->startframe = startframe;
marker->endframe = endframe;
return EINA_TRUE;
}
i++;
}
new_marker.name = eina_stringshare_add(name);
new_marker.startframe = startframe;
new_marker.endframe = endframe;
eina_inarray_push(vg_entry->vfd->anim_data->markers, &new_marker);
return EINA_TRUE;
}
Eina_Bool
evas_cache_vg_anim_sector_get(const Vg_Cache_Entry* vg_entry, const char *name, int* startframe, int* endframe)
{
if (!vg_entry) return EINA_FALSE;
if (!vg_entry->vfd->anim_data) return EINA_FALSE;
if (!vg_entry->vfd->anim_data->markers) return EINA_FALSE;
if (!name) return EINA_FALSE;
Vg_File_Anim_Data_Marker *marker;
EINA_INARRAY_FOREACH(vg_entry->vfd->anim_data->markers, marker)
{
if (!strcmp(marker->name, name))
{
if (startframe) *startframe = marker->startframe;
if (endframe) *endframe = marker->endframe;
return EINA_TRUE;
}
}
return EINA_FALSE;
}
Efl_VG*
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;
Vg_File_Data *vfd = vg_entry->vfd;
if (!vfd) return NULL;
//No need to update.
if (vfd->anim_data)
{
if (vg_entry->root &&
vfd->anim_data->frame_num == frame_num)
return vg_entry->root;
}
else
{
if (vg_entry->root)
return vg_entry->root;
}
if (!vfd->static_viewbox)
{
vfd->view_box.w = vg_entry->w;
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;
_root_update(vg_entry);
_local_transform(vg_entry->root, vg_entry->w, vg_entry->h, vfd);
return vg_entry->root;
}
void
evas_cache_vg_entry_value_provider_update(Vg_Cache_Entry *vg_entry, Eina_List *vp_list)
{
if (!vg_entry) return;
Vg_File_Data *vfd = vg_entry->vfd;
if (!vfd) return;
vfd->vp_list = vp_list;
}
void
evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry)
{
if (!vg_cache || !vg_entry) return;
vg_entry->ref--;
if (vg_entry->ref > 0) return;
if (!eina_hash_del(vg_cache->vg_entry_hash, vg_entry->hash_key, 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)
{
Vg_File_Data *vfd =
evas_cache_vg_file_open(vg_entry->file, vg_entry->key, vg_entry->evas);
if (!vfd) return EINA_FALSE;
return _vg_file_save(vfd, file, key, info);
}
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)
{
Vg_File_Data vfd = {};
if (!root) return EINA_FALSE;
vfd.view_box.x = w;
vfd.view_box.y = h;
vfd.root = root;
vfd.preserve_aspect = EINA_FALSE;
return _vg_file_save(&vfd, file, key, info);
}