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