#ifdef HAVE_CONFIG_H # include #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 && 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); }