1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include "evas_common_private.h"
6 #include "evas_private.h"
7 #include "evas_vg_private.h"
8 
9 static Vg_Cache* vg_cache = NULL;
10 
11 struct ext_loader_s
12 {
13    unsigned int length;
14    const char *extension;
15    const char *loader;
16 };
17 
18 struct ext_saver_s
19 {
20    unsigned int length;
21    const char *extension;
22    const char *saver;
23 };
24 
25 #define MATCHING(Ext, Module) { sizeof(Ext)-1, Ext, Module }
26 
27 static const struct ext_loader_s loaders[] =
28 { /* map extensions to loaders to use for good first-guess tries */
29    MATCHING(".eet", "eet"),
30    MATCHING(".edj", "eet"),
31    MATCHING(".svg", "svg"),
32    MATCHING(".svgz", "svg"),
33    MATCHING(".svg.gz", "svg")
34 };
35 
36 static const char *loaders_name[] =
37 { /* in order of most likely needed */
38   "eet", "json", "svg"
39 };
40 
41 static const struct ext_saver_s savers[] =
42 { /* map extensions to savers to use for good first-guess tries */
43    MATCHING(".eet", "eet"),
44    MATCHING(".edj", "eet"),
45    MATCHING(".svg", "svg")
46 };
47 
48 static Evas_Module *
_find_loader_module(const char * file)49 _find_loader_module(const char *file)
50 {
51    const char           *loader = NULL, *end;
52    Evas_Module          *em = NULL;
53    unsigned int          i;
54    int                   len, len2;
55    len = strlen(file);
56    end = file + len;
57    for (i = 0; i < (sizeof (loaders) / sizeof(struct ext_loader_s)); i++)
58      {
59         len2 = loaders[i].length;
60         if (len2 > len) continue;
61         if (!strcasecmp(end - len2, loaders[i].extension))
62           {
63              loader = loaders[i].loader;
64              break;
65           }
66      }
67    if (loader)
68      em = evas_module_find_type(EVAS_MODULE_TYPE_VG_LOADER, loader);
69    return em;
70 }
71 
72 static Vg_File_Data *
_vg_load_from_file(const Eina_File * file,const char * key)73 _vg_load_from_file(const Eina_File *file, const char *key)
74 {
75    Evas_Module       *em;
76    Evas_Vg_Load_Func *loader;
77    int                error = EVAS_LOAD_ERROR_GENERIC;
78    Vg_File_Data      *vfd;
79    unsigned int i;
80 
81    const char *file_name = eina_file_filename_get(file);
82    em = _find_loader_module(file_name);
83    if (em)
84      {
85         loader = em->functions;
86         vfd = loader->file_open((Eina_File *) file, key, &error);
87         if (vfd)
88           {
89              vfd->loader = loader;
90              return vfd;
91           }
92      }
93    else
94      {
95         for (i = 0; i < sizeof (loaders_name) / sizeof (char *); i++)
96           {
97              em = evas_module_find_type(EVAS_MODULE_TYPE_VG_LOADER, loaders_name[i]);
98              if (em)
99                {
100                   loader = em->functions;
101                   vfd = loader->file_open((Eina_File *) file, key, &error);
102                   if (vfd)
103                     {
104                        vfd->loader = loader;
105                        return vfd;
106                     }
107                }
108           }
109      }
110    WRN("Exhausted all means to load vector file = %s", file_name);
111    return NULL;
112 }
113 
114 static Evas_Module *
_find_saver_module(const char * file)115 _find_saver_module(const char *file)
116 {
117    const char           *saver = NULL, *end;
118    Evas_Module          *em = NULL;
119    unsigned int          i;
120    int                   len, len2;
121    len = strlen(file);
122    end = file + len;
123    for (i = 0; i < (sizeof (savers) / sizeof(struct ext_saver_s)); i++)
124      {
125         len2 = savers[i].length;
126         if (len2 > len) continue;
127         if (!strcasecmp(end - len2, savers[i].extension))
128           {
129              saver = savers[i].saver;
130              break;
131           }
132      }
133    if (saver)
134      em = evas_module_find_type(EVAS_MODULE_TYPE_VG_SAVER, saver);
135    return em;
136 }
137 
138 static void
_evas_cache_vg_data_free_cb(void * data)139 _evas_cache_vg_data_free_cb(void *data)
140 {
141    Vg_File_Data *vfd = data;
142    vfd->loader->file_close(vfd);
143 }
144 
145 static void
_evas_cache_vg_entry_free_cb(void * data)146 _evas_cache_vg_entry_free_cb(void *data)
147 {
148    Vg_Cache_Entry *vg_entry = data;
149 
150    if (vg_entry->vfd)
151      {
152         vg_entry->vfd->ref--;
153 
154         if (vg_entry->vfd->ref <= 0)
155           {
156              if (vg_entry->vfd->shareable)
157                {
158                   Eina_Strbuf *hash_key = eina_strbuf_new();
159                   eina_strbuf_append_printf(hash_key, "%s/%s/%p",
160                                             eina_file_filename_get(vg_entry->file),
161                                             vg_entry->key,
162                                             vg_entry->evas);
163                   if (!eina_hash_del(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vg_entry->vfd))
164                     ERR("Failed to delete vfd = (%p) from hash", vg_entry->vfd);
165                   eina_strbuf_free(hash_key);
166                }
167              else
168                {
169                   vg_entry->vfd->loader->file_close(vg_entry->vfd);
170                }
171           }
172      }
173 
174    eina_stringshare_del(vg_entry->key);
175    free(vg_entry->hash_key);
176    efl_unref(vg_entry->root);
177    free(vg_entry);
178 }
179 
180 static Eina_Bool
_vg_file_save(Vg_File_Data * vfd,const char * file,const char * key,const Efl_File_Save_Info * info)181 _vg_file_save(Vg_File_Data *vfd, const char *file, const char *key, const Efl_File_Save_Info *info)
182 {
183    Evas_Module       *em;
184    Evas_Vg_Save_Func *saver;
185    Evas_Load_Error    error = EVAS_LOAD_ERROR_GENERIC;
186    int                compress = 9;
187 
188    if (!file) return EINA_FALSE;
189 
190    if (info) compress = info->compression;
191 
192    em = _find_saver_module(file);
193    if (em)
194      {
195         saver = em->functions;
196         error = saver->file_save(vfd, file, key, compress);
197      }
198 
199    if (error)
200      return EINA_FALSE;
201 
202    return EINA_TRUE;
203 }
204 
205 static void
_root_update(Vg_Cache_Entry * vg_entry)206 _root_update(Vg_Cache_Entry *vg_entry)
207 {
208    Vg_File_Data *vfd = vg_entry->vfd;
209 
210    /* Optimization: static viewbox may have same root data regardless of size.
211       So we can't use the root data directly, but copy it for each vg_entries.
212       In the meantime, non-static viewbox root data may have difference instance for each
213       size. So it's affordable to share the root data for each vg_entries. */
214    if (vfd->static_viewbox)
215      {
216         /* TODO: Yet trivial but still we may have a better solution to
217            avoid this unnecessary copy. If the ector surface key is not
218            to this root pointer. */
219         vg_entry->root = efl_duplicate(vfd->root);
220      }
221    //Shareable??
222    else if (vg_entry->root != vfd->root)
223      {
224         if (vg_entry->root) efl_unref(vg_entry->root);
225         vg_entry->root = efl_ref(vfd->root);
226      }
227 }
228 
229 static void
_local_transform(Efl_VG * root,double w,double h,Vg_File_Data * vfd)230 _local_transform(Efl_VG *root, double w, double h, Vg_File_Data *vfd)
231 {
232    double sx = 0, sy= 0, scale;
233    Eina_Matrix3 m;
234 
235    if (!vfd->static_viewbox) return;
236    if (EINA_DBL_EQ(vfd->view_box.w, w) && EINA_DBL_EQ(vfd->view_box.h, h)) return;
237 
238    sx = w / vfd->view_box.w;
239    sy = h / vfd->view_box.h;
240 
241    scale = sx < sy ? sx : sy;
242    eina_matrix3_identity(&m);
243 
244    // align hcenter and vcenter
245    if (vfd->preserve_aspect)
246      {
247         eina_matrix3_translate(&m, (w - vfd->view_box.w * scale)/2.0, (h - vfd->view_box.h * scale)/2.0);
248         eina_matrix3_scale(&m, scale, scale);
249         eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
250      }
251    else
252      {
253         eina_matrix3_scale(&m, sx, sy);
254         eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
255      }
256    efl_canvas_vg_node_transformation_set(root, &m);
257 }
258 
259 void
evas_cache_vg_init(void)260 evas_cache_vg_init(void)
261 {
262    if (vg_cache)
263      {
264         vg_cache->ref++;
265         return;
266      }
267    vg_cache = calloc(1, sizeof(Vg_Cache));
268    if (!vg_cache)
269      {
270         CRI("Failed to alloc Vg_Cache");
271         return;
272      }
273 
274    vg_cache->vfd_hash = eina_hash_string_superfast_new(_evas_cache_vg_data_free_cb);
275    vg_cache->vg_entry_hash = eina_hash_string_superfast_new(_evas_cache_vg_entry_free_cb);
276    vg_cache->ref++;
277 }
278 
279 void *
evas_cache_vg_surface_key_get(Efl_Canvas_Vg_Node * root,int w,int h,int frame_idx)280 evas_cache_vg_surface_key_get(Efl_Canvas_Vg_Node *root, int w, int h, int frame_idx)
281 {
282    //This make a unique key pointer by arguments.
283    Eina_Strbuf *hash_key = eina_strbuf_new();
284    eina_strbuf_append_printf(hash_key, "%p/%d/%d/%d", root, w, h, frame_idx);
285    const char *new_key = eina_strbuf_string_get(hash_key);
286    if (!new_key)
287      {
288         eina_strbuf_free(hash_key);
289         return NULL;
290      }
291 
292    Eina_List *l;
293    char *key;
294    EINA_LIST_FOREACH(vg_cache->vg_surface_keys, l, key)
295      {
296         //Exisiting key!
297         if (!strcmp(key, new_key))
298           {
299              eina_strbuf_free(hash_key);
300              return key;
301           }
302      }
303 
304    //New key comes.
305    key = eina_strbuf_string_steal(hash_key);
306    vg_cache->vg_surface_keys = eina_list_append(vg_cache->vg_surface_keys, key);
307 
308    eina_strbuf_free(hash_key);
309 
310    return (void *) key;
311 }
312 
313 void
evas_cache_vg_shutdown(void)314 evas_cache_vg_shutdown(void)
315 {
316    if (!vg_cache) return;
317    vg_cache->ref--;
318    if (vg_cache->ref > 0) return;
319 
320    char *key;
321    EINA_LIST_FREE(vg_cache->vg_surface_keys, key)
322      free(key);
323    eina_list_free(vg_cache->vg_surface_keys);
324 
325    eina_hash_free(vg_cache->vg_entry_hash);
326    eina_hash_free(vg_cache->vfd_hash);
327    free(vg_cache);
328    vg_cache = NULL;
329 }
330 
331 Vg_File_Data *
evas_cache_vg_file_open(const Eina_File * file,const char * key,Evas * e,Eina_Bool shareable)332 evas_cache_vg_file_open(const Eina_File *file, const char *key, Evas *e, Eina_Bool shareable)
333 {
334    Vg_File_Data *vfd;
335    Eina_Strbuf *hash_key;
336 
337    if (shareable)
338      {
339         hash_key = eina_strbuf_new();
340         eina_strbuf_append_printf(hash_key, "%s/%s/%p", eina_file_filename_get(file), key, e);
341         vfd = eina_hash_find(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key));
342         if (!vfd)
343           {
344              vfd = _vg_load_from_file(file, key);
345              //File exists.
346              if (vfd) eina_hash_add(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vfd);
347           }
348         eina_strbuf_free(hash_key);
349      }
350    else
351      {
352         vfd = _vg_load_from_file(file, key);
353      }
354    if (vfd) vfd->shareable = shareable;
355    return vfd;
356 }
357 
358 Vg_Cache_Entry*
evas_cache_vg_entry_resize(Vg_Cache_Entry * vg_entry,int w,int h)359 evas_cache_vg_entry_resize(Vg_Cache_Entry *vg_entry, int w, int h)
360 {
361    return evas_cache_vg_entry_create(vg_entry->evas, vg_entry->file, vg_entry->key, w, h, vg_entry->vfd->vp_list);
362 }
363 
364 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)365 evas_cache_vg_entry_create(Evas *evas,
366                            const Eina_File *file,
367                            const char *key,
368                            int w, int h, Eina_List *vp_list)
369 {
370    Vg_Cache_Entry* vg_entry;
371    Eina_Strbuf *hash_key;
372 
373    if (!vg_cache) return NULL;
374 
375    //TODO: zero-sized entry is useless. how to skip it?
376    hash_key = eina_strbuf_new();
377    eina_strbuf_append_printf(hash_key, "%p/%p/%s/%d/%d/%p", evas, file, key, w, h, vp_list);
378    vg_entry = eina_hash_find(vg_cache->vg_entry_hash, eina_strbuf_string_get(hash_key));
379    if (!vg_entry)
380      {
381         vg_entry = calloc(1, sizeof(Vg_Cache_Entry));
382         if (!vg_entry)
383           {
384              CRI("Failed to alloc Vg_Cache_Entry");
385              eina_strbuf_free(hash_key);
386              return NULL;
387           }
388         vg_entry->file = file;
389         vg_entry->key = eina_stringshare_add(key);
390         vg_entry->w = w;
391         vg_entry->h = h;
392         vg_entry->evas = evas;
393         vg_entry->hash_key = eina_strbuf_string_steal(hash_key);
394         eina_hash_direct_add(vg_cache->vg_entry_hash, vg_entry->hash_key, vg_entry);
395      }
396    eina_strbuf_free(hash_key);
397    vg_entry->ref++;
398    vg_entry->vfd = evas_cache_vg_file_open(file, key, vg_entry->evas, vp_list ? EINA_FALSE : EINA_TRUE);
399    //No File??
400    if (!vg_entry->vfd)
401      {
402         evas_cache_vg_entry_del(vg_entry);
403         return NULL;
404      }
405    vg_entry->vfd->ref++;
406    vg_entry->vfd->vp_list = vp_list;
407 
408    return vg_entry;
409 }
410 
411 double
evas_cache_vg_anim_duration_get(const Vg_Cache_Entry * vg_entry)412 evas_cache_vg_anim_duration_get(const Vg_Cache_Entry* vg_entry)
413 {
414    if (!vg_entry->vfd->anim_data) return 0;
415    return vg_entry->vfd->anim_data->duration;
416 }
417 
418 unsigned int
evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry * vg_entry)419 evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry* vg_entry)
420 {
421    if (!vg_entry) return 0;
422    Vg_File_Data *vfd = vg_entry->vfd;
423    if (!vfd || !vfd->anim_data) return 0;
424    return vfd->anim_data->frame_cnt;
425 }
426 
427 Eina_Bool
evas_cache_vg_anim_sector_set(const Vg_Cache_Entry * vg_entry,const char * name,int startframe,int endframe)428 evas_cache_vg_anim_sector_set(const Vg_Cache_Entry* vg_entry, const char *name, int startframe, int endframe)
429 {
430    if (!vg_entry) return EINA_FALSE;
431    if (!vg_entry->vfd->anim_data) return EINA_FALSE;
432    if (!vg_entry->vfd->anim_data->markers) return EINA_FALSE;
433    if (!name) return EINA_FALSE;
434 
435    Vg_File_Anim_Data_Marker *marker;
436    Vg_File_Anim_Data_Marker new_marker;
437    int i = 0;
438 
439    EINA_INARRAY_FOREACH(vg_entry->vfd->anim_data->markers, marker)
440      {
441         if (!strcmp(marker->name, name))
442           {
443              marker->startframe = startframe;
444              marker->endframe = endframe;
445              return EINA_TRUE;
446           }
447         i++;
448      }
449 
450    new_marker.name = eina_stringshare_add(name);
451    new_marker.startframe = startframe;
452    new_marker.endframe = endframe;
453    eina_inarray_push(vg_entry->vfd->anim_data->markers, &new_marker);
454 
455    return EINA_TRUE;
456 }
457 
458 Eina_Bool
evas_cache_vg_anim_sector_get(const Vg_Cache_Entry * vg_entry,const char * name,int * startframe,int * endframe)459 evas_cache_vg_anim_sector_get(const Vg_Cache_Entry* vg_entry, const char *name, int* startframe, int* endframe)
460 {
461    if (!vg_entry) return EINA_FALSE;
462    if (!vg_entry->vfd->anim_data) return EINA_FALSE;
463    if (!vg_entry->vfd->anim_data->markers) return EINA_FALSE;
464    if (!name) return EINA_FALSE;
465 
466    Vg_File_Anim_Data_Marker *marker;
467    EINA_INARRAY_FOREACH(vg_entry->vfd->anim_data->markers, marker)
468      {
469         if (!strcmp(marker->name, name))
470           {
471              if (startframe) *startframe = marker->startframe;
472              if (endframe) *endframe = marker->endframe;
473              return EINA_TRUE;
474           }
475      }
476    return EINA_FALSE;
477 }
478 
479 Efl_VG*
evas_cache_vg_tree_get(Vg_Cache_Entry * vg_entry,unsigned int frame_num)480 evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry, unsigned int frame_num)
481 {
482    if (!vg_entry) return NULL;
483    if ((vg_entry->w < 1) || (vg_entry->h < 1)) return NULL;
484 
485    Vg_File_Data *vfd = vg_entry->vfd;
486    if (!vfd) return NULL;
487 
488    //No need to update.
489    if (vfd->anim_data)
490      {
491         if ((vg_entry->w == vfd->view_box.w) &&
492             (vg_entry->h == vfd->view_box.h))
493           {
494              if (vg_entry->root &&
495                  vfd->anim_data->frame_num == frame_num)
496                return vg_entry->root;
497           }
498      }
499    else
500      {
501         if (vg_entry->root)
502           return vg_entry->root;
503      }
504 
505    if (!vfd->static_viewbox)
506      {
507         vfd->view_box.w = vg_entry->w;
508         vfd->view_box.h = vg_entry->h;
509      }
510 
511    if (vfd->anim_data) vfd->anim_data->frame_num = frame_num;
512 
513    if (!vfd->loader->file_data(vfd)) return NULL;
514 
515    _root_update(vg_entry);
516 
517    _local_transform(vg_entry->root, vg_entry->w, vg_entry->h, vfd);
518 
519    return vg_entry->root;
520 }
521 
522 void
evas_cache_vg_entry_value_provider_update(Vg_Cache_Entry * vg_entry,Eina_List * vp_list)523 evas_cache_vg_entry_value_provider_update(Vg_Cache_Entry *vg_entry, Eina_List *vp_list)
524 {
525    if (!vg_entry) return;
526 
527    Vg_File_Data *vfd = vg_entry->vfd;
528    if (!vfd) return;
529 
530    vfd->vp_list = vp_list;
531 }
532 
533 void
evas_cache_vg_entry_del(Vg_Cache_Entry * vg_entry)534 evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry)
535 {
536    if (!vg_cache || !vg_entry) return;
537    vg_entry->ref--;
538    if (vg_entry->ref > 0) return;
539    if (!eina_hash_del(vg_cache->vg_entry_hash, vg_entry->hash_key, vg_entry))
540      ERR("Failed to delete vg_entry = (%p) from hash", vg_entry);
541 }
542 
543 Eina_Size2D
evas_cache_vg_entry_default_size_get(const Vg_Cache_Entry * vg_entry)544 evas_cache_vg_entry_default_size_get(const Vg_Cache_Entry *vg_entry)
545 {
546    if (!vg_entry) return EINA_SIZE2D(0, 0);
547    return EINA_SIZE2D(vg_entry->vfd->w, vg_entry->vfd->h);
548 }
549 
550 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)551 evas_cache_vg_entry_file_save(Vg_Cache_Entry *vg_entry, const char *file, const char *key, const Efl_File_Save_Info *info)
552 {
553    Vg_File_Data *vfd =
554       evas_cache_vg_file_open(vg_entry->file, vg_entry->key, vg_entry->evas
555                               ,vg_entry->vfd ? (vg_entry->vfd->vp_list ? EINA_FALSE : EINA_TRUE): EINA_TRUE);
556 
557    if (!vfd) return EINA_FALSE;
558 
559    return _vg_file_save(vfd, file, key, info);
560 }
561 
562 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)563 evas_cache_vg_file_save(Efl_VG *root, int w, int h, const char *file, const char *key, const Efl_File_Save_Info *info)
564 {
565    Vg_File_Data vfd = {};
566 
567    if (!root) return EINA_FALSE;
568 
569    vfd.view_box.x = w;
570    vfd.view_box.y = h;
571    vfd.root = root;
572    vfd.preserve_aspect = EINA_FALSE;
573 
574    return _vg_file_save(&vfd, file, key, info);
575 }
576