forked from enlightenment/efl
560 lines
15 KiB
C
560 lines
15 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_surface_key_get(Efl_Canvas_Vg_Node *root, int w, int h, int frame_idx)
|
|
{
|
|
//This make a unique key pointer by arguments.
|
|
Eina_Strbuf *hash_key = eina_strbuf_new();
|
|
eina_strbuf_append_printf(hash_key, "%p/%d/%d/%d", root, w, h, frame_idx);
|
|
const char *new_key = eina_strbuf_string_get(hash_key);
|
|
if (!new_key)
|
|
{
|
|
eina_strbuf_free(hash_key);
|
|
return NULL;
|
|
}
|
|
|
|
Eina_List *l;
|
|
char *key;
|
|
EINA_LIST_FOREACH(vg_cache->vg_surface_keys, l, key)
|
|
{
|
|
//Exisiting key!
|
|
if (!strcmp(key, new_key))
|
|
{
|
|
eina_strbuf_free(hash_key);
|
|
return key;
|
|
}
|
|
}
|
|
|
|
//New key comes.
|
|
key = eina_strbuf_string_steal(hash_key);
|
|
vg_cache->vg_surface_keys = eina_list_append(vg_cache->vg_surface_keys, key);
|
|
|
|
eina_strbuf_free(hash_key);
|
|
|
|
return (void *) key;
|
|
}
|
|
|
|
void
|
|
evas_cache_vg_shutdown(void)
|
|
{
|
|
if (!vg_cache) return;
|
|
vg_cache->ref--;
|
|
if (vg_cache->ref > 0) return;
|
|
|
|
char *key;
|
|
EINA_LIST_FREE(vg_cache->vg_surface_keys, key)
|
|
free(key);
|
|
eina_list_free(vg_cache->vg_surface_keys);
|
|
|
|
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->w == vfd->view_box.w) &&
|
|
(vg_entry->h == vfd->view_box.h))
|
|
{
|
|
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);
|
|
}
|