ac1637aeb3cb60b8b4ea1307e58f943ee4b7d8f7
[platform/upstream/ethumb.git] / src / lib / ethumb.c
1 /**
2  * @file
3  *
4  * Copyright (C) 2009 by ProFUSION embedded systems
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library;
18  * if not, see <http://www.gnu.org/licenses/>.
19  *
20  * @author Rafael Antognolli <antognolli@profusion.mobi>
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #ifdef HAVE_ALLOCA_H
27 # include <alloca.h>
28 #elif defined __GNUC__
29 # define alloca __builtin_alloca
30 #elif defined _AIX
31 # define alloca __alloca
32 #elif defined _MSC_VER
33 # include <malloc.h>
34 # define alloca _alloca
35 #else
36 # include <stddef.h>
37 # ifdef  __cplusplus
38 extern "C"
39 # endif
40 void *alloca (size_t);
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <dirent.h>
52 #include <dlfcn.h>
53 #include <ctype.h>
54
55 #ifdef HAVE_XATTR
56 # include <sys/xattr.h>
57 #endif
58
59 #ifndef PATH_MAX
60 # define PATH_MAX 4096
61 #endif
62
63 #include <Eina.h>
64 #include <eina_safety_checks.h>
65 #include <Evas.h>
66 #include <Ecore.h>
67 #include <Ecore_Evas.h>
68 #include <Ecore_File.h>
69 #include <Edje.h>
70
71 #include "Ethumb.h"
72 #include "ethumb_private.h"
73 #include "Ethumb_Plugin.h"
74 #include "md5.h"
75
76 #ifdef HAVE_LIBEXIF
77   #include <libexif/exif-data.h>
78 #endif
79
80 static Ethumb_Version _version = { VMAJ, VMIN, VMIC, VREV };
81 EAPI Ethumb_Version *ethumb_version = &_version;
82
83 static int _log_dom = -1;
84 #define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
85 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
86 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
87 #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
88
89 static int initcount = 0;
90 static const char *_home_thumb_dir = NULL;
91 static const char *_thumb_category_normal = NULL;
92 static const char *_thumb_category_large = NULL;
93
94 static const int THUMB_SIZE_NORMAL = 128;
95 static const int THUMB_SIZE_LARGE = 256;
96
97 static Eina_Hash *_plugins_ext = NULL;
98 static Eina_Array *_plugins = NULL;
99
100 static Eina_Bool
101 _ethumb_plugin_list_cb(Eina_Module *m, void *data __UNUSED__)
102 {
103    const char *file;
104    const char **ext;
105    Ethumb_Plugin *plugin;
106    Ethumb_Plugin *(*plugin_get)(void);
107
108    file = eina_module_file_get(m);
109    if (!eina_module_load(m))
110      {
111         ERR("could not load module \"%s\": %s",
112             file, eina_error_msg_get(eina_error_get()));
113         return EINA_FALSE;
114      }
115
116    plugin_get = eina_module_symbol_get(m, "ethumb_plugin_get");
117    if (!plugin_get)
118      {
119         ERR("could not find ethumb_plugin_get() in module \"%s\": %s",
120             file, eina_error_msg_get(eina_error_get()));
121         eina_module_unload(m);
122         return EINA_FALSE;
123      }
124
125    plugin = plugin_get();
126    if (!plugin)
127      {
128         ERR("plugin \"%s\" failed to init.", file);
129         eina_module_unload(m);
130         return EINA_FALSE;
131      }
132
133    DBG("loaded plugin \"%s\" (%p) with extensions:", file, plugin);
134    for (ext = plugin->extensions; *ext; ext++)
135      {
136         DBG("   extension \"%s\"", *ext);
137         eina_hash_add(_plugins_ext, *ext, plugin);
138      }
139
140    return EINA_TRUE;
141 }
142
143 static void
144 _ethumb_plugins_load(void)
145 {
146    _plugins_ext = eina_hash_string_small_new(NULL);
147    EINA_SAFETY_ON_NULL_RETURN(_plugins_ext);
148
149    _plugins = eina_module_list_get(_plugins, PLUGINSDIR, 1,
150                                    &_ethumb_plugin_list_cb, NULL);
151 }
152
153 static void
154 _ethumb_plugins_unload(void)
155 {
156    eina_hash_free(_plugins_ext);
157    _plugins_ext = NULL;
158    eina_module_list_unload(_plugins);
159    eina_module_list_free(_plugins);
160    eina_array_free(_plugins);
161    _plugins = NULL;
162 }
163
164 EAPI int
165 ethumb_init(void)
166 {
167    const char *home;
168    char buf[PATH_MAX];
169
170    if (initcount)
171      return ++initcount;
172
173    if (!eina_init())
174      {
175         fprintf(stderr, "ERROR: Could not initialize eina.\n");
176         return 0;
177      }
178    _log_dom = eina_log_domain_register("ethumb", EINA_COLOR_GREEN);
179    if (_log_dom < 0)
180      {
181         EINA_LOG_ERR("Could not register log domain: ethumb");
182         eina_shutdown();
183         return 0;
184      }
185
186    evas_init();
187    ecore_init();
188    ecore_evas_init();
189    edje_init();
190
191    home = getenv("HOME");
192    snprintf(buf, sizeof(buf), "%s/.thumbnails", home);
193
194    _home_thumb_dir = eina_stringshare_add(buf);
195    _thumb_category_normal = eina_stringshare_add("normal");
196    _thumb_category_large = eina_stringshare_add("large");
197
198    _ethumb_plugins_load();
199    return ++initcount;
200 }
201
202 EAPI int
203 ethumb_shutdown(void)
204 {
205    if (initcount <= 0)
206      {
207         EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
208         return 0;
209      }
210    initcount--;
211    if (initcount == 0)
212      {
213         _ethumb_plugins_unload();
214         eina_stringshare_del(_home_thumb_dir);
215         eina_stringshare_del(_thumb_category_normal);
216         eina_stringshare_del(_thumb_category_large);
217         evas_shutdown();
218         ecore_shutdown();
219         ecore_evas_shutdown();
220         edje_shutdown();
221         eina_log_domain_unregister(_log_dom);
222         _log_dom = -1;
223         eina_shutdown();
224      }
225
226    return initcount;
227 }
228
229 EAPI Ethumb *
230 ethumb_new(void)
231 {
232    Ethumb *ethumb;
233    Ecore_Evas *ee, *sub_ee;
234    Evas *e, *sub_e;
235    Evas_Object *o, *img;
236
237    ethumb = calloc(1, sizeof(Ethumb));
238    EINA_SAFETY_ON_NULL_RETURN_VAL(ethumb, NULL);
239
240    /* IF CHANGED, UPDATE DOCS in (Ethumb.c, Ethumb_Client.c, python...)!!! */
241    ethumb->tw = THUMB_SIZE_NORMAL;
242    ethumb->th = THUMB_SIZE_NORMAL;
243    ethumb->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
244    ethumb->crop_x = 0.5;
245    ethumb->crop_y = 0.5;
246    ethumb->quality = 80;
247    ethumb->compress = 9;
248    ethumb->video.start = 0.1;
249    ethumb->video.time = 3;
250    ethumb->video.interval = 0.05;
251    ethumb->video.ntimes = 3;
252    ethumb->video.fps = 10;
253
254    ee = ecore_evas_buffer_new(1, 1);
255    e = ecore_evas_get(ee);
256    if (!e)
257      {
258         ERR("could not create ecore evas buffer");
259         free(ethumb);
260         return NULL;
261      }
262
263    evas_image_cache_set(e, 0);
264    evas_font_cache_set(e, 0);
265
266    o = ecore_evas_object_image_new(ee);
267    if (!o)
268      {
269         ERR("could not create sub ecore evas buffer");
270         ecore_evas_free(ee);
271         free(ethumb);
272         return NULL;
273      }
274
275    sub_ee = ecore_evas_object_ecore_evas_get(o);
276    sub_e = ecore_evas_object_evas_get(o);
277    ecore_evas_alpha_set(sub_ee, EINA_TRUE);
278
279    evas_image_cache_set(sub_e, 0);
280    evas_font_cache_set(sub_e, 0);
281
282    img = evas_object_image_add(sub_e);
283    if (!img)
284      {
285         ERR("could not create source objects.");
286         ecore_evas_free(ee);
287         free(ethumb);
288         return NULL;
289      }
290
291    ethumb->ee = ee;
292    ethumb->e = e;
293    ethumb->sub_ee = sub_ee;
294    ethumb->sub_e = sub_e;
295    ethumb->o = o;
296    ethumb->img = img;
297
298    DBG("ethumb=%p", ethumb);
299
300    return ethumb;
301 }
302
303 static void
304 _ethumb_frame_free(Ethumb_Frame *frame)
305 {
306    Evas_Object *o;
307
308    if (!frame)
309      return;
310
311    if (frame->swallow && frame->edje)
312      {
313      o = edje_object_part_swallow_get(frame->edje, frame->swallow);
314      if (o)
315        edje_object_part_unswallow(frame->edje, o);
316      }
317    eina_stringshare_del(frame->file);
318    eina_stringshare_del(frame->group);
319    eina_stringshare_del(frame->swallow);
320
321    if (frame->edje)
322      evas_object_del(frame->edje);
323
324    free(frame);
325 }
326
327 EAPI void
328 ethumb_free(Ethumb *ethumb)
329 {
330    EINA_SAFETY_ON_NULL_RETURN(ethumb);
331
332    DBG("ethumb=%p", ethumb);
333
334    if (ethumb->frame)
335      _ethumb_frame_free(ethumb->frame);
336    ethumb_file_free(ethumb);
337    evas_object_del(ethumb->o);
338    ecore_evas_free(ethumb->ee);
339    eina_stringshare_del(ethumb->thumb_dir);
340    eina_stringshare_del(ethumb->category);
341    if (ethumb->finished_idler)
342      ecore_idler_del(ethumb->finished_idler);
343    free(ethumb);
344 }
345
346 EAPI void
347 ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s)
348 {
349    EINA_SAFETY_ON_NULL_RETURN(e);
350    EINA_SAFETY_ON_FALSE_RETURN(s == ETHUMB_THUMB_NORMAL ||
351                                s == ETHUMB_THUMB_LARGE);
352    DBG("ethumb=%p, size=%d", e, s);
353
354    if (s == ETHUMB_THUMB_NORMAL)
355      {
356         e->tw = THUMB_SIZE_NORMAL;
357         e->th = THUMB_SIZE_NORMAL;
358      }
359    else
360      {
361         e->tw = THUMB_SIZE_LARGE;
362         e->th = THUMB_SIZE_LARGE;
363      }
364
365    e->format = ETHUMB_THUMB_FDO;
366    e->aspect = ETHUMB_THUMB_KEEP_ASPECT;
367    e->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
368    _ethumb_frame_free(e->frame);
369    e->frame = NULL;
370    eina_stringshare_del(e->thumb_dir);
371    eina_stringshare_del(e->category);
372    e->thumb_dir = NULL;
373    e->category = NULL;
374 }
375
376 EAPI void
377 ethumb_thumb_size_set(Ethumb *e, int tw, int th)
378 {
379    EINA_SAFETY_ON_NULL_RETURN(e);
380    EINA_SAFETY_ON_FALSE_RETURN(tw > 0);
381    EINA_SAFETY_ON_FALSE_RETURN(th > 0);
382
383    DBG("ethumb=%p, w=%d, h=%d", e, tw, th);
384    e->tw = tw;
385    e->th = th;
386 }
387
388 EAPI void
389 ethumb_thumb_size_get(const Ethumb *e, int *tw, int *th)
390 {
391    EINA_SAFETY_ON_NULL_RETURN(e);
392
393    if (tw) *tw = e->tw;
394    if (th) *th = e->th;
395 }
396
397 EAPI void
398 ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f)
399 {
400    EINA_SAFETY_ON_NULL_RETURN(e);
401    EINA_SAFETY_ON_FALSE_RETURN(f == ETHUMB_THUMB_FDO ||
402                                f == ETHUMB_THUMB_JPEG ||
403                                f == ETHUMB_THUMB_EET);
404
405    DBG("ethumb=%p, format=%d", e, f);
406    e->format = f;
407 }
408
409 EAPI Ethumb_Thumb_Format
410 ethumb_thumb_format_get(const Ethumb *e)
411 {
412    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
413    return e->format;
414 }
415
416 EAPI void
417 ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a)
418 {
419    EINA_SAFETY_ON_NULL_RETURN(e);
420    EINA_SAFETY_ON_FALSE_RETURN(a == ETHUMB_THUMB_KEEP_ASPECT ||
421                                a == ETHUMB_THUMB_IGNORE_ASPECT ||
422                                a == ETHUMB_THUMB_CROP);
423
424    DBG("ethumb=%p, aspect=%d", e, a);
425    e->aspect = a;
426 }
427
428 EAPI Ethumb_Thumb_Aspect
429 ethumb_thumb_aspect_get(const Ethumb *e)
430 {
431    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
432    return e->aspect;
433 }
434
435 EAPI void
436 ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o)
437 {
438    EINA_SAFETY_ON_NULL_RETURN(e);
439    EINA_SAFETY_ON_FALSE_RETURN(o == ETHUMB_THUMB_ORIENT_NONE ||
440                                o == ETHUMB_THUMB_ROTATE_90_CW ||
441                                o == ETHUMB_THUMB_ROTATE_180 ||
442                                o == ETHUMB_THUMB_ROTATE_90_CCW ||
443                                o == ETHUMB_THUMB_FLIP_HORIZONTAL ||
444                                o == ETHUMB_THUMB_FLIP_VERTICAL ||
445                                o == ETHUMB_THUMB_FLIP_TRANSPOSE ||
446                                o == ETHUMB_THUMB_FLIP_TRANSVERSE ||
447                                o == ETHUMB_THUMB_ORIENT_ORIGINAL);
448
449    DBG("ethumb=%p, orientation=%d", e, o);
450    e->orientation = o;
451 }
452
453 EAPI Ethumb_Thumb_Orientation
454 ethumb_thumb_orientation_get(const Ethumb *e)
455 {
456    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
457    return e->orientation;
458 }
459
460 EAPI void
461 ethumb_thumb_crop_align_set(Ethumb *e, float x, float y)
462 {
463    EINA_SAFETY_ON_NULL_RETURN(e);
464
465    DBG("ethumb=%p, x=%f, y=%f", e, x, y);
466    e->crop_x = x;
467    e->crop_y = y;
468 }
469
470 EAPI void
471 ethumb_thumb_crop_align_get(const Ethumb *e, float *x, float *y)
472 {
473    EINA_SAFETY_ON_NULL_RETURN(e);
474
475    if (x) *x = e->crop_x;
476    if (y) *y = e->crop_y;
477 }
478
479 EAPI void
480 ethumb_thumb_quality_set(Ethumb *e, int quality)
481 {
482    EINA_SAFETY_ON_NULL_RETURN(e);
483
484    DBG("ethumb=%p, quality=%d", e, quality);
485    e->quality = quality;
486 }
487
488 EAPI int
489 ethumb_thumb_quality_get(const Ethumb *e)
490 {
491    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
492    return e->quality;
493 }
494
495 EAPI void
496 ethumb_thumb_compress_set(Ethumb *e, int compress)
497 {
498    EINA_SAFETY_ON_NULL_RETURN(e);
499
500    DBG("ethumb=%p, compress=%d", e, compress);
501    e->compress = compress;
502 }
503
504 EAPI int
505 ethumb_thumb_compress_get(const Ethumb *e)
506 {
507    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
508    return e->compress;
509 }
510
511 EAPI Eina_Bool
512 ethumb_frame_set(Ethumb *e, const char *theme_file, const char *group, const char *swallow)
513 {
514    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
515
516    Ethumb_Frame *frame;
517    frame = e->frame;
518
519    DBG("ethumb=%p, theme_file=%s, group=%s, swallow=%s",
520        e, theme_file ? theme_file : "", group ? group : "",
521        swallow ? swallow : "");
522
523    if (frame)
524      {
525         edje_object_part_unswallow(frame->edje, e->img);
526         if (!theme_file)
527           _ethumb_frame_free(frame);
528      }
529
530    if (!theme_file)
531      {
532         e->frame = NULL;
533         return EINA_TRUE;
534      }
535
536    if (!frame)
537      {
538         frame = calloc(1, sizeof(Ethumb_Frame));
539         if (!frame)
540           {
541              ERR("could not allocate Ethumb_Frame structure.");
542              return EINA_FALSE;
543           }
544
545         frame->edje = edje_object_add(e->sub_e);
546         if (!frame->edje)
547           {
548              ERR("could not create edje frame object.");
549              _ethumb_frame_free(frame);
550              e->frame = NULL;
551              return EINA_FALSE;
552           }
553      }
554
555    if (!edje_object_file_set(frame->edje, theme_file, group))
556      {
557         ERR("could not load frame theme.");
558         _ethumb_frame_free(frame);
559         e->frame = NULL;
560         return EINA_FALSE;
561      }
562
563    edje_object_part_swallow(frame->edje, swallow, e->img);
564    if (!edje_object_part_swallow_get(frame->edje, swallow))
565      {
566         ERR("could not swallow image to edje frame.");
567         _ethumb_frame_free(frame);
568         e->frame = NULL;
569         return EINA_FALSE;
570      }
571
572    eina_stringshare_replace(&frame->file, theme_file);
573    eina_stringshare_replace(&frame->group, group);
574    eina_stringshare_replace(&frame->swallow, swallow);
575
576    e->frame = frame;
577
578    return EINA_TRUE;
579 }
580
581 EAPI void
582 ethumb_frame_get(const Ethumb *e, const char **theme_file, const char **group, const char **swallow)
583 {
584    EINA_SAFETY_ON_NULL_RETURN(e);
585
586    if (e->frame)
587      {
588         if (theme_file) *theme_file = e->frame->file;
589         if (group) *group = e->frame->group;
590         if (swallow) *swallow = e->frame->swallow;
591      }
592    else
593      {
594         if (theme_file) *theme_file = NULL;
595         if (group) *group = NULL;
596         if (swallow) *swallow = NULL;
597      }
598 }
599
600 static const char *
601 _ethumb_build_absolute_path(const char *path, char buf[PATH_MAX])
602 {
603    char *p;
604    int len;
605
606    if (!path)
607      return NULL;
608
609    p = buf;
610
611    if (path[0] == '/')
612      strcpy(p, path);
613    else if (path[0] == '~')
614      {
615         const char *home = getenv("HOME");
616         if (!home)
617           return NULL;
618         strcpy(p, home);
619         len = strlen(p);
620         p += len;
621         p[0] = '/';
622         p++;
623         strcpy(p, path + 2);
624      }
625    else
626      {
627         if (!getcwd(p, PATH_MAX))
628           return NULL;
629         len = strlen(p);
630         p += len;
631         p[0] = '/';
632         p++;
633         strcpy(p, path);
634      }
635
636    return buf;
637 }
638
639 EAPI void
640 ethumb_thumb_dir_path_set(Ethumb *e, const char *path)
641 {
642    char buf[PATH_MAX];
643    EINA_SAFETY_ON_NULL_RETURN(e);
644
645    DBG("ethumb=%p, path=%s", e, path ? path : "");
646    path = _ethumb_build_absolute_path(path, buf);
647    eina_stringshare_replace(&e->thumb_dir, path);
648 }
649
650 EAPI const char *
651 ethumb_thumb_dir_path_get(const Ethumb *e)
652 {
653    EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
654
655    return e->thumb_dir;
656 }
657
658 EAPI void
659 ethumb_thumb_category_set(Ethumb *e, const char *category)
660 {
661    EINA_SAFETY_ON_NULL_RETURN(e);
662
663    DBG("ethumb=%p, category=%s", e, category ? category : "");
664    eina_stringshare_replace(&e->category, category);
665 }
666
667 EAPI const char *
668 ethumb_thumb_category_get(const Ethumb *e)
669 {
670    EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
671
672    return e->category;
673 }
674
675 EAPI void
676 ethumb_video_start_set(Ethumb *e, float start)
677 {
678    EINA_SAFETY_ON_NULL_RETURN(e);
679    EINA_SAFETY_ON_FALSE_RETURN(start >= 0.0);
680    EINA_SAFETY_ON_FALSE_RETURN(start <= 1.0);
681
682    DBG("ethumb=%p, video_start=%f", e, start);
683    e->video.start = start;
684 }
685
686 EAPI float
687 ethumb_video_start_get(const Ethumb *e)
688 {
689    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
690
691    return e->video.start;
692 }
693
694 EAPI void
695 ethumb_video_time_set(Ethumb *e, float t)
696 {
697    EINA_SAFETY_ON_NULL_RETURN(e);
698
699    DBG("ethumb=%p, video_start=%f", e, t);
700    e->video.time = t;
701 }
702
703 EAPI float
704 ethumb_video_time_get(const Ethumb *e)
705 {
706    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
707
708    return e->video.time;
709 }
710
711 EAPI void
712 ethumb_video_interval_set(Ethumb *e, float interval)
713 {
714    EINA_SAFETY_ON_NULL_RETURN(e);
715
716    DBG("ethumb=%p, video_interval=%f", e, interval);
717    e->video.interval = interval;
718 }
719
720 EAPI float
721 ethumb_video_interval_get(const Ethumb *e)
722 {
723    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
724
725    return e->video.interval;
726 }
727
728 EAPI void
729 ethumb_video_ntimes_set(Ethumb *e, unsigned int ntimes)
730 {
731    EINA_SAFETY_ON_NULL_RETURN(e);
732    EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
733
734    DBG("ethumb=%p, video_ntimes=%d", e, ntimes);
735    e->video.ntimes = ntimes;
736 }
737
738 EAPI unsigned int
739 ethumb_video_ntimes_get(const Ethumb *e)
740 {
741    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
742
743    return e->video.ntimes;
744 }
745
746 EAPI void
747 ethumb_video_fps_set(Ethumb *e, unsigned int fps)
748 {
749    EINA_SAFETY_ON_NULL_RETURN(e);
750    EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
751
752    DBG("ethumb=%p, video_fps=%d", e, fps);
753    e->video.fps = fps;
754 }
755
756 EAPI unsigned int
757 ethumb_video_fps_get(const Ethumb *e)
758 {
759    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
760
761    return e->video.fps;
762 }
763
764 EAPI void
765 ethumb_document_page_set(Ethumb *e, unsigned int page)
766 {
767    EINA_SAFETY_ON_NULL_RETURN(e);
768
769    DBG("ethumb=%p, document_page=%d", e, page);
770    e->document.page = page;
771 }
772
773 EAPI unsigned int
774 ethumb_document_page_get(const Ethumb *e)
775 {
776    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
777
778    return e->document.page;
779 }
780
781 EAPI Eina_Bool
782 ethumb_file_set(Ethumb *e, const char *path, const char *key)
783 {
784    char buf[PATH_MAX];
785    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
786
787    eina_stringshare_replace(&e->thumb_path, NULL);
788    eina_stringshare_replace(&e->thumb_key, NULL);
789
790    DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
791    if (path && access(path, R_OK))
792      {
793         ERR("couldn't access file \"%s\"", path);
794         return EINA_FALSE;
795      }
796
797    path = _ethumb_build_absolute_path(path, buf);
798    eina_stringshare_replace(&e->src_hash, NULL);
799    eina_stringshare_replace(&e->src_path, path);
800    eina_stringshare_replace(&e->src_key, key);
801
802    return EINA_TRUE;
803 }
804
805 EAPI void
806 ethumb_file_get(const Ethumb *e, const char **path, const char **key)
807 {
808    EINA_SAFETY_ON_NULL_RETURN(e);
809
810    if (path) *path = e->src_path;
811    if (key) *key = e->src_key;
812 }
813
814 static const char ACCEPTABLE_URI_CHARS[96] = {
815      /*      !    "    #    $    %    &    '    (    )    *    +    ,    -    .    / */ 
816      0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
817      /* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ? */
818      0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
819      /* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O */
820      0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
821      /* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _ */
822      0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
823      /* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o */
824      0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
825      /* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  DEL */
826      0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
827 };
828
829 static const char *
830 _ethumb_generate_hash(const char *file)
831 {
832   int n;
833   MD5_CTX ctx;
834   char md5out[(2 * MD5_HASHBYTES) + 1];
835   unsigned char hash[MD5_HASHBYTES];
836   static const char hex[] = "0123456789abcdef";
837
838   char *uri;
839   char *t;
840   const unsigned char *c;
841
842 #ifdef HAVE_XATTR
843   ssize_t length;
844
845   length = getxattr(file, "user.e.md5", NULL, 0);
846
847   if (length > 0)
848     {
849        char *tmp;
850
851        tmp = alloca(length);
852        length = getxattr(file, "user.e.md5", tmp, length);
853
854        /* check if we have at least something that look like a md5 hash */
855        if (length > 0 && (length == MD5_HASHBYTES * 2 + 1))
856          {
857             tmp[length] = '\0';
858             return eina_stringshare_add(tmp);
859          }
860     }
861 #endif
862
863 #define _check_uri_char(c) \
864   ((c) >= 32 && (c) < 128 && (ACCEPTABLE_URI_CHARS[(c) - 32] & 0x08))
865
866   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
867
868   uri = alloca(3 * strlen(file) + 9);
869   memcpy(uri, "file://", sizeof("file://") - 1);
870   t = uri + sizeof("file://") - 1;
871
872   for (c = (const unsigned char *)file; *c != '\0'; c++)
873     {
874        if (!_check_uri_char(*c))
875          {
876             *t++ = '%';
877             *t++ = hex[*c >> 4];
878             *t++ = hex[*c & 15];
879          }
880        else
881          *t++ = *c;
882     }
883   *t = '\0';
884
885 #undef _check_uri_char
886
887   MD5Init (&ctx);
888   MD5Update (&ctx, (unsigned char const*)uri, (unsigned)strlen (uri));
889   MD5Final (hash, &ctx);
890
891   for (n = 0; n < MD5_HASHBYTES; n++)
892     {
893       md5out[2 * n] = hex[hash[n] >> 4];
894       md5out[2 * n + 1] = hex[hash[n] & 0x0f];
895     }
896   md5out[2 * n] = '\0';
897
898 #ifdef HAVE_XATTR
899   setxattr(file, "user.e.md5", md5out, 2 * n + 1, 0);
900 #endif
901
902   DBG("md5=%s, file=%s", md5out, file);
903   return eina_stringshare_add(md5out);
904 }
905
906 static int
907 _ethumb_file_check_fdo(Ethumb *e)
908 {
909    if (!((e->tw == THUMB_SIZE_NORMAL && e->th == THUMB_SIZE_NORMAL) ||
910        (e->tw == THUMB_SIZE_LARGE && e->th == THUMB_SIZE_LARGE)))
911      return 0;
912
913    if (e->format != ETHUMB_THUMB_FDO)
914      return 0;
915
916    if (e->aspect != ETHUMB_THUMB_KEEP_ASPECT)
917      return 0;
918
919    if (e->frame)
920      return 0;
921
922    return 1;
923 }
924
925 static const char *
926 _ethumb_file_generate_custom_category(Ethumb *e)
927 {
928    char buf[PATH_MAX];
929    const char *aspect, *format;
930    const char *frame;
931
932    if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
933      aspect = "keep_aspect";
934    else if (e->aspect == ETHUMB_THUMB_IGNORE_ASPECT)
935      aspect = "ignore_aspect";
936    else
937      aspect = "crop";
938
939    if (e->format == ETHUMB_THUMB_FDO)
940      format = "png";
941    else if (e->format == ETHUMB_THUMB_JPEG)
942      format = "jpg";
943    else
944      format = "eet";
945
946    if (e->frame)
947      frame = "-framed";
948    else
949      frame = "";
950
951    snprintf(buf, sizeof(buf), "%dx%d-%s%s-%s",
952             e->tw, e->th, aspect, frame, format);
953
954    return eina_stringshare_add(buf);
955 }
956
957 static void
958 _ethumb_file_generate_path(Ethumb *e)
959 {
960    char buf[PATH_MAX];
961    const char *thumb_dir, *category;
962    const char *ext;
963    int fdo_format;
964
965    fdo_format = _ethumb_file_check_fdo(e);
966
967    if (e->thumb_dir)
968      thumb_dir = eina_stringshare_ref(e->thumb_dir);
969    else
970      thumb_dir = eina_stringshare_ref(_home_thumb_dir);
971
972    if (e->category)
973      category = eina_stringshare_ref(e->category);
974    else if (!fdo_format)
975      category = _ethumb_file_generate_custom_category(e);
976    else
977      {
978         if (e->tw == THUMB_SIZE_NORMAL)
979           category = eina_stringshare_ref(_thumb_category_normal);
980         else if (e->tw == THUMB_SIZE_LARGE)
981           category = eina_stringshare_ref(_thumb_category_large);
982         else
983           {
984              ERR("fdo_format but size %d is not NORMAL (%d) or LARGE (%d)?",
985                  e->tw, THUMB_SIZE_NORMAL, THUMB_SIZE_LARGE);
986              category = "unknown";
987           }
988      }
989
990    if (e->format == ETHUMB_THUMB_FDO)
991      ext = "png";
992    else if (e->format == ETHUMB_THUMB_JPEG)
993      ext = "jpg";
994    else
995      ext = "eet";
996
997    if (!e->src_hash)
998      {
999        char *fullname;
1000
1001        fullname = ecore_file_realpath(e->src_path);
1002        e->src_hash = _ethumb_generate_hash(fullname);
1003        free(fullname);
1004      }
1005    snprintf(buf, sizeof(buf), "%s/%s/%s.%s", thumb_dir, category, e->src_hash, ext);
1006    DBG("ethumb=%p, path=%s", e, buf);
1007    eina_stringshare_replace(&e->thumb_path, buf);
1008    if (e->format == ETHUMB_THUMB_EET)
1009      eina_stringshare_replace(&e->thumb_key, "thumbnail");
1010    else
1011      {
1012         eina_stringshare_del(e->thumb_key);
1013         e->thumb_key = NULL;
1014      }
1015
1016    eina_stringshare_del(thumb_dir);
1017    eina_stringshare_del(category);
1018 }
1019
1020 EAPI void
1021 ethumb_file_free(Ethumb *e)
1022 {
1023    EINA_SAFETY_ON_NULL_RETURN(e);
1024    DBG("ethumb=%p", e);
1025
1026    eina_stringshare_replace(&e->src_hash, NULL);
1027    eina_stringshare_replace(&e->src_path, NULL);
1028    eina_stringshare_replace(&e->src_key, NULL);
1029    eina_stringshare_replace(&e->thumb_path, NULL);
1030    eina_stringshare_replace(&e->thumb_key, NULL);
1031 }
1032
1033 EAPI void
1034 ethumb_thumb_path_set(Ethumb *e, const char *path, const char *key)
1035 {
1036    char buf[PATH_MAX];
1037
1038    EINA_SAFETY_ON_NULL_RETURN(e);
1039    DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
1040
1041    if (!path)
1042      {
1043         eina_stringshare_replace(&e->thumb_path, NULL);
1044         eina_stringshare_replace(&e->thumb_key, NULL);
1045      }
1046    else
1047      {
1048         path = _ethumb_build_absolute_path(path, buf);
1049         eina_stringshare_replace(&e->thumb_path, path);
1050         eina_stringshare_replace(&e->thumb_key, key);
1051      }
1052 }
1053
1054 EAPI void
1055 ethumb_thumb_path_get(Ethumb *e, const char **path, const char **key)
1056 {
1057    EINA_SAFETY_ON_NULL_RETURN(e);
1058    if (!e->thumb_path)
1059      _ethumb_file_generate_path(e);
1060
1061    if (path) *path = e->thumb_path;
1062    if (key) *key = e->thumb_key;
1063 }
1064
1065 EAPI void
1066 ethumb_thumb_hash(Ethumb *e)
1067 {
1068    EINA_SAFETY_ON_NULL_RETURN(e);
1069    if (!e->src_hash)
1070      {
1071         char *fullname;
1072
1073         fullname = ecore_file_realpath(e->src_path);
1074         e->src_hash = _ethumb_generate_hash(fullname);
1075         free(fullname);
1076      }
1077 }
1078
1079 EAPI void
1080 ethumb_thumb_hash_copy(Ethumb *dst, const Ethumb *src)
1081 {
1082    EINA_SAFETY_ON_NULL_RETURN(dst);
1083    EINA_SAFETY_ON_NULL_RETURN(src);
1084
1085    if (src == dst) return ;
1086
1087    eina_stringshare_del(dst->src_hash);
1088    dst->src_hash = eina_stringshare_ref(src->src_hash);
1089 }
1090
1091 EAPI void
1092 ethumb_calculate_aspect_from_ratio(Ethumb *e, float ia, int *w, int *h)
1093 {
1094    float a;
1095
1096    *w = e->tw;
1097    *h = e->th;
1098
1099    if (ia == 0)
1100      return;
1101
1102    a = e->tw / (float)e->th;
1103
1104    if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
1105      {
1106         if ((ia > a && e->tw > 0) || e->th <= 0)
1107           *h = e->tw / ia;
1108         else
1109           *w = e->th * ia;
1110      }
1111 }
1112
1113 EAPI void
1114 ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h)
1115 {
1116    float ia;
1117
1118    ia = iw / (float)ih;
1119
1120    ethumb_calculate_aspect_from_ratio(e, ia, w, h);
1121 }
1122
1123 EAPI void
1124 ethumb_calculate_fill_from_ratio(Ethumb *e, float ia, int *fx, int *fy, int *fw, int *fh)
1125 {
1126    float a;
1127
1128    *fw = e->tw;
1129    *fh = e->th;
1130    *fx = 0;
1131    *fy = 0;
1132
1133    if (ia == 0)
1134      return;
1135
1136    a = e->tw / (float)e->th;
1137
1138    if (e->aspect == ETHUMB_THUMB_CROP)
1139      {
1140         if ((ia > a && e->tw > 0) || e->th <= 0)
1141           *fw = e->th * ia;
1142         else
1143           *fh = e->tw / ia;
1144
1145         *fx = - e->crop_x * (*fw - e->tw);
1146         *fy = - e->crop_y * (*fh - e->th);
1147      }
1148    else if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
1149      {
1150         if ((ia > a && e->tw > 0) || e->th <= 0)
1151           *fh = e->tw / ia;
1152         else
1153           *fw = e->th * ia;
1154      }
1155 }
1156
1157 EAPI void
1158 ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, int *fh)
1159 {
1160    float ia;
1161    ia = iw / (float)ih;
1162
1163    ethumb_calculate_fill_from_ratio(e, ia, fx, fy, fw, fh);
1164 }
1165
1166 static Eina_Bool
1167 _ethumb_plugin_generate(Ethumb *e)
1168 {
1169    const char *extp;
1170    char ext[PATH_MAX];
1171    Ethumb_Plugin *plugin;
1172    int i;
1173
1174    extp = strrchr(e->src_path, '.');
1175    if (!extp)
1176      {
1177         ERR("could not get extension for file \"%s\"", e->src_path);
1178         return EINA_FALSE;
1179      }
1180
1181    for (i = 0; extp[i] != '\0'; i++)
1182         ext[i] = tolower(extp[i + 1]);
1183
1184    plugin = eina_hash_find(_plugins_ext, ext);
1185    if (!plugin)
1186      {
1187         DBG("no plugin for extension: \"%s\"", ext);
1188         return EINA_FALSE;
1189      }
1190
1191    if (e->frame)
1192      evas_object_hide(e->frame->edje);
1193    else
1194      evas_object_hide(e->img);
1195
1196    e->plugin = plugin;
1197    e->pdata = plugin->thumb_generate(e);
1198
1199    return EINA_TRUE;
1200 }
1201
1202 EAPI Eina_Bool
1203 ethumb_plugin_image_resize(Ethumb *e, int w, int h)
1204 {
1205    Evas_Object *img;
1206
1207    img = e->img;
1208
1209    if (e->frame)
1210      {
1211         edje_extern_object_min_size_set(img, w, h);
1212         edje_extern_object_max_size_set(img, w, h);
1213         edje_object_calc_force(e->frame->edje);
1214         evas_object_move(e->frame->edje, 0, 0);
1215         evas_object_resize(e->frame->edje, w, h);
1216      }
1217    else
1218      {
1219         evas_object_move(img, 0, 0);
1220         evas_object_resize(img, w, h);
1221      }
1222
1223    evas_object_image_size_set(e->o, w, h);
1224    ecore_evas_resize(e->sub_ee, w, h);
1225
1226    e->rw = w;
1227    e->rh = h;
1228
1229    return EINA_TRUE;
1230 }
1231
1232 EAPI Eina_Bool
1233 ethumb_image_save(Ethumb *e)
1234 {
1235    Eina_Bool r;
1236    char *dname;
1237    char flags[256];
1238
1239    evas_damage_rectangle_add(e->sub_e, 0, 0, e->rw, e->rh);
1240    evas_render(e->sub_e);
1241
1242    if (!e->thumb_path)
1243      _ethumb_file_generate_path(e);
1244
1245    if (!e->thumb_path)
1246      {
1247         ERR("could not create file path...");
1248         return EINA_FALSE;
1249      }
1250
1251    dname = ecore_file_dir_get(e->thumb_path);
1252    r = ecore_file_mkpath(dname);
1253    free(dname);
1254    if (!r)
1255      {
1256         ERR("could not create directory '%s'", dname);
1257         return EINA_FALSE;
1258      }
1259
1260    snprintf(flags, sizeof(flags), "quality=%d compress=%d",
1261             e->quality, e->compress);
1262    r = evas_object_image_save(e->o, e->thumb_path, e->thumb_key, flags);
1263
1264    if (!r)
1265      {
1266         ERR("could not save image: path=%s, key=%s", e->thumb_path,
1267             e->thumb_key);
1268         return EINA_FALSE;
1269      }
1270
1271    return EINA_TRUE;
1272 }
1273
1274 static void
1275 _ethumb_image_orient(Ethumb *e, int orientation)
1276 {
1277    Evas_Object *img = e->img, *tmp;
1278    unsigned int *data, *data2, *to, *from, *p1, *p2, pt;
1279    int x, y, w, hw, iw, ih, tw, th;
1280    const char *file, *key;
1281
1282    evas_object_image_size_get(img, &iw, &ih);
1283    evas_object_image_load_size_get(img, &tw, &th);
1284    evas_object_image_file_get(img, &file, &key);
1285    data = evas_object_image_data_get(img, 1);
1286
1287    switch (orientation)
1288      {
1289       case ETHUMB_THUMB_FLIP_HORIZONTAL:
1290          for (y = 0; y < ih; y++)
1291            {
1292               p1 = data + (y * iw);
1293               p2 = data + ((y + 1) * iw) - 1;
1294               for (x = 0; x < (iw >> 1); x++)
1295                 {
1296                    pt = *p1;
1297                    *p1 = *p2;
1298                    *p2 = pt;
1299                    p1++;
1300                    p2--;
1301                 }
1302            }
1303          evas_object_image_data_set(img, data);
1304          evas_object_image_data_update_add(img, 0, 0, iw, ih);
1305          return;
1306       case ETHUMB_THUMB_FLIP_VERTICAL:
1307          for (y = 0; y < (ih >> 1); y++)
1308            {
1309               p1 = data + (y * iw);
1310               p2 = data + ((ih - 1 - y) * iw);
1311               for (x = 0; x < iw; x++)
1312                 {
1313                    pt = *p1;
1314                    *p1 = *p2;
1315                    *p2 = pt;
1316                    p1++;
1317                    p2++;
1318                 }
1319            }
1320          evas_object_image_data_set(img, data);
1321          evas_object_image_data_update_add(img, 0, 0, iw, ih);
1322          return;
1323       case ETHUMB_THUMB_ROTATE_180:
1324          hw = iw * ih;
1325          x = (hw / 2);
1326          p1 = data;
1327          p2 = data + hw - 1;
1328          for (; --x > 0;)
1329            {
1330               pt = *p1;
1331               *p1 = *p2;
1332               *p2 = pt;
1333               p1++;
1334               p2--;
1335            }
1336          evas_object_image_data_set(img, data);
1337          evas_object_image_data_update_add(img, 0, 0, iw, ih);
1338          return;
1339      }
1340
1341    tmp = evas_object_image_add(evas_object_evas_get(img));
1342    evas_object_image_load_size_set(tmp, tw, th);
1343    evas_object_image_file_set(tmp, file, key);
1344    data2 = evas_object_image_data_get(tmp, 0);
1345
1346    w = ih;
1347    ih = iw;
1348    iw = w;
1349    hw = w * ih;
1350
1351    evas_object_image_size_set(img, iw, ih);
1352    data = evas_object_image_data_get(img, 1);
1353
1354    switch (orientation)
1355      {
1356       case ETHUMB_THUMB_FLIP_TRANSPOSE:
1357          to = data;
1358          hw = -hw + 1;
1359          break;
1360       case ETHUMB_THUMB_FLIP_TRANSVERSE:
1361          to = data + hw - 1;
1362          w = -w;
1363          hw = hw - 1;
1364          break;
1365       case ETHUMB_THUMB_ROTATE_90_CW:
1366          to = data + w - 1;
1367          hw = -hw - 1;
1368          break;
1369       case ETHUMB_THUMB_ROTATE_90_CCW:
1370          to = data + hw - w;
1371          w = -w;
1372          hw = hw + 1;
1373          break;
1374       default:
1375          ERR("unknown orient %d", orientation);
1376          evas_object_del(tmp);
1377          evas_object_image_data_set(img, data); // give it back
1378          return;
1379      }
1380    from = data2;
1381    for (x = iw; --x >= 0;)
1382      {
1383         for (y = ih; --y >= 0;)
1384           {
1385              *to = *from;
1386              from++;
1387              to += w;
1388           }
1389         to += hw;
1390      }
1391    evas_object_del(tmp);
1392    evas_object_image_data_set(img, data);
1393    evas_object_image_data_update_add(img, 0, 0, iw, ih);
1394 }
1395
1396 static int
1397 _ethumb_image_load(Ethumb *e)
1398 {
1399    int error;
1400    Evas_Coord w, h, ww, hh, fx, fy, fw, fh;
1401    Evas_Object *img;
1402    int orientation = ETHUMB_THUMB_ORIENT_NONE;
1403
1404    img = e->img;
1405
1406    if (e->frame)
1407      evas_object_hide(e->frame->edje);
1408    else
1409      evas_object_hide(img);
1410    evas_object_image_file_set(img, NULL, NULL);
1411    evas_object_image_load_size_set(img, e->tw, e->th);
1412    evas_object_image_file_set(img, e->src_path, e->src_key);
1413
1414    if (e->frame)
1415      evas_object_show(e->frame->edje);
1416    else
1417      evas_object_show(img);
1418
1419    error = evas_object_image_load_error_get(img);
1420    if (error != EVAS_LOAD_ERROR_NONE)
1421      {
1422         ERR("could not load image '%s': %d", e->src_path, error);
1423         return 0;
1424      }
1425
1426    if (e->orientation == ETHUMB_THUMB_ORIENT_ORIGINAL)
1427       {
1428 #ifdef HAVE_LIBEXIF
1429          ExifData  *exif = exif_data_new_from_file(e->src_path);
1430          ExifEntry *entry = NULL;
1431          ExifByteOrder bo;
1432          int o = 0;
1433
1434          if (exif)
1435            {
1436               entry = exif_data_get_entry(exif, EXIF_TAG_ORIENTATION);
1437               if (entry)
1438                 {
1439                    bo = exif_data_get_byte_order(exif);
1440                    o = exif_get_short(entry->data, bo);
1441                 }
1442               exif_data_free(exif);
1443               switch (o)
1444                 {
1445                  case 2:
1446                     orientation = ETHUMB_THUMB_FLIP_HORIZONTAL;
1447                     break;
1448                  case 3:
1449                     orientation = ETHUMB_THUMB_ROTATE_180;
1450                     break;
1451                  case 4:
1452                     orientation = ETHUMB_THUMB_FLIP_VERTICAL;
1453                     break;
1454                  case 5:
1455                     orientation = ETHUMB_THUMB_FLIP_TRANSPOSE;
1456                     break;
1457                  case 6:
1458                     orientation = ETHUMB_THUMB_ROTATE_90_CW;
1459                     break;
1460                  case 7:
1461                     orientation = ETHUMB_THUMB_FLIP_TRANSVERSE;
1462                     break;
1463                  case 8:
1464                     orientation = ETHUMB_THUMB_ROTATE_90_CCW;
1465                     break;
1466                 }
1467            }
1468 #endif
1469    }
1470
1471    if (orientation != ETHUMB_THUMB_ORIENT_NONE)
1472      _ethumb_image_orient(e, orientation);
1473
1474    evas_object_image_size_get(img, &w, &h);
1475    if ((w <= 0) || (h <= 0))
1476      return 0;
1477
1478    ethumb_calculate_aspect(e, w, h, &ww, &hh);
1479
1480    if (e->frame)
1481      {
1482         edje_extern_object_min_size_set(img, ww, hh);
1483         edje_extern_object_max_size_set(img, ww, hh);
1484         edje_object_calc_force(e->frame->edje);
1485         evas_object_move(e->frame->edje, 0, 0);
1486         evas_object_resize(e->frame->edje, ww, hh);
1487      }
1488    else
1489      {
1490         evas_object_move(img, 0, 0);
1491         evas_object_resize(img, ww, hh);
1492      }
1493
1494    ethumb_calculate_fill(e, w, h, &fx, &fy, &fw, &fh);
1495    evas_object_image_fill_set(img, fx, fy, fw, fh);
1496
1497    evas_object_image_size_set(e->o, ww, hh);
1498    ecore_evas_resize(e->sub_ee, ww, hh);
1499
1500    e->rw = ww;
1501    e->rh = hh;
1502
1503    return 1;
1504 }
1505
1506 static Eina_Bool
1507 _ethumb_finished_idler_cb(void *data)
1508 {
1509    Ethumb *e = data;
1510
1511    e->finished_cb(e->cb_data, e, e->cb_result);
1512    if (e->cb_data_free)
1513      e->cb_data_free(e->cb_data);
1514    e->finished_idler = NULL;
1515    e->finished_cb = NULL;
1516    e->cb_data = NULL;
1517    e->cb_data_free = NULL;
1518
1519    return EINA_FALSE;
1520 }
1521
1522 EAPI void
1523 ethumb_finished_callback_call(Ethumb *e, int result)
1524 {
1525    EINA_SAFETY_ON_NULL_RETURN(e);
1526
1527    e->cb_result = result;
1528    if (e->finished_idler)
1529      ecore_idler_del(e->finished_idler);
1530    e->finished_idler = ecore_idler_add(_ethumb_finished_idler_cb, e);
1531    e->plugin = NULL;
1532    e->pdata = NULL;
1533 }
1534
1535 EAPI Eina_Bool
1536 ethumb_generate(Ethumb *e, Ethumb_Generate_Cb finished_cb, const void *data, Eina_Free_Cb free_data)
1537 {
1538    int r;
1539
1540    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
1541    EINA_SAFETY_ON_NULL_RETURN_VAL(finished_cb, 0);
1542    DBG("ethumb=%p, finished_cb=%p, data=%p, free_data=%p, path=%s, key=%s",
1543        e, finished_cb, data, free_data,
1544        e->src_path ? e->src_path : "", e->src_key ? e->src_key : "");
1545
1546    if (e->finished_idler)
1547      {
1548         ERR("thumbnail generation already in progress.");
1549         return EINA_FALSE;
1550      }
1551    if (e->pdata)
1552      {
1553         e->plugin->thumb_cancel(e, e->pdata);
1554         e->pdata = NULL;
1555         e->plugin = NULL;
1556      }
1557
1558    e->finished_cb = finished_cb;
1559    e->cb_data = (void *)data;
1560    e->cb_data_free = free_data;
1561
1562    if (!e->src_path)
1563      {
1564         ERR("no file set.");
1565         ethumb_finished_callback_call(e, 0);
1566         return EINA_FALSE;
1567      }
1568
1569    r = _ethumb_plugin_generate(e);
1570    fprintf(stderr, "ethumb generate: %i: %p\n", r, e->pdata);
1571    if (r)
1572      {
1573         return EINA_TRUE;
1574      }
1575
1576    if (!_ethumb_image_load(e))
1577      {
1578         ERR("could not load input image.");
1579         ethumb_finished_callback_call(e, 0);
1580         return EINA_FALSE;
1581      }
1582
1583    r = ethumb_image_save(e);
1584
1585    ethumb_finished_callback_call(e, r);
1586
1587    return EINA_TRUE;
1588 }
1589
1590 EAPI Eina_Bool
1591 ethumb_exists(Ethumb *e)
1592 {
1593    struct stat thumb, src;
1594    int r_thumb, r_src;
1595    Eina_Bool r = EINA_FALSE;
1596
1597    EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
1598    EINA_SAFETY_ON_NULL_RETURN_VAL(e->src_path, 0);
1599    DBG("ethumb=%p, path=%s", e, e->src_path ? e->src_path : "");
1600
1601    if (!e->thumb_path)
1602      _ethumb_file_generate_path(e);
1603
1604    EINA_SAFETY_ON_NULL_RETURN_VAL(e->thumb_path, 0);
1605
1606    r_thumb = stat(e->thumb_path, &thumb);
1607    r_src = stat(e->src_path, &src);
1608
1609    EINA_SAFETY_ON_TRUE_RETURN_VAL(r_src, 0);
1610
1611    if (r_thumb && errno != ENOENT)
1612      ERR("could not access file \"%s\": %s", e->thumb_path, strerror(errno));
1613    else if (!r_thumb && thumb.st_mtime > src.st_mtime)
1614      r = EINA_TRUE;
1615
1616    return r;
1617 }
1618
1619 EAPI Evas *
1620 ethumb_evas_get(const Ethumb *e)
1621 {
1622    EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
1623
1624    return e->sub_e;
1625 }
1626
1627 EAPI Ecore_Evas *
1628 ethumb_ecore_evas_get(const Ethumb *e)
1629 {
1630    EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
1631
1632    return e->sub_ee;
1633 }
1634
1635 EAPI Ethumb *
1636 ethumb_dup(const Ethumb *e)
1637 {
1638    Ecore_Evas *ee;
1639    Ecore_Evas *sub_ee;
1640    Evas *ev;
1641    Evas *sub_ev;
1642    Evas_Object *o;
1643    Evas_Object *img;
1644    Ethumb *r;
1645
1646    r = malloc(sizeof (Ethumb));
1647    if (!r) return NULL;
1648
1649    memcpy(r, e, sizeof (Ethumb));
1650
1651    r->thumb_dir = eina_stringshare_ref(e->thumb_dir);
1652    r->category = eina_stringshare_ref(e->category);
1653    r->src_hash = eina_stringshare_ref(e->src_hash);
1654    r->src_path = eina_stringshare_ref(e->src_path);
1655    r->src_key = eina_stringshare_ref(e->src_key);
1656    r->thumb_path = eina_stringshare_ref(e->thumb_path);
1657    r->thumb_key = eina_stringshare_ref(e->thumb_key);
1658
1659    ee = ecore_evas_buffer_new(1, 1);
1660    ev = ecore_evas_get(ee);
1661    if (!ev)
1662      {
1663         ERR("could not create ecore evas buffer");
1664         free(r);
1665         return NULL;
1666      }
1667
1668    evas_image_cache_set(ev, 0);
1669    evas_font_cache_set(ev, 0);
1670
1671    o = ecore_evas_object_image_new(ee);
1672    if (!o)
1673      {
1674         ERR("could not create sub ecore evas buffer");
1675         ecore_evas_free(ee);
1676         free(r);
1677         return NULL;
1678      }
1679
1680    sub_ee = ecore_evas_object_ecore_evas_get(o);
1681    sub_ev = ecore_evas_object_evas_get(o);
1682    ecore_evas_alpha_set(sub_ee, EINA_TRUE);
1683
1684    evas_image_cache_set(sub_ev, 0);
1685    evas_font_cache_set(sub_ev, 0);
1686
1687    img = evas_object_image_add(sub_ev);
1688    if (!img)
1689      {
1690         ERR("could not create source objects.");
1691         ecore_evas_free(ee);
1692         free(r);
1693         return NULL;
1694      }
1695
1696    r->ee = ee;
1697    r->sub_ee = sub_ee;
1698    r->e = ev;
1699    r->sub_e = sub_ev;
1700    r->o = o;
1701    r->img = img;
1702
1703    r->frame = NULL;
1704    r->finished_idler = NULL;
1705    r->finished_cb = NULL;
1706    r->cb_data = NULL;
1707    r->cb_data_free = NULL;
1708    r->cb_result = 0;
1709    r->plugin = NULL;
1710    r->pdata = NULL;
1711
1712    return r;
1713 }
1714
1715 #define CHECK_DELTA(Param)                      \
1716   if (e1->Param != e2->Param)                   \
1717     return EINA_TRUE;
1718
1719 EAPI Eina_Bool
1720 ethumb_cmp(const Ethumb *e1, const Ethumb *e2)
1721 {
1722    CHECK_DELTA(thumb_dir);
1723    CHECK_DELTA(category);
1724    CHECK_DELTA(tw);
1725    CHECK_DELTA(th);
1726    CHECK_DELTA(format);
1727    CHECK_DELTA(aspect);
1728    CHECK_DELTA(orientation);
1729    CHECK_DELTA(crop_x);
1730    CHECK_DELTA(crop_y);
1731    CHECK_DELTA(quality);
1732    CHECK_DELTA(compress);
1733    CHECK_DELTA(rw);
1734    CHECK_DELTA(rh);
1735    CHECK_DELTA(video.start);
1736    CHECK_DELTA(video.time);
1737    CHECK_DELTA(video.interval);
1738    CHECK_DELTA(video.ntimes);
1739    CHECK_DELTA(video.fps);
1740    CHECK_DELTA(document.page);
1741
1742    return EINA_FALSE;
1743 }
1744
1745 EAPI unsigned int
1746 ethumb_length(__UNUSED__ const void *key)
1747 {
1748    return sizeof (Ethumb);
1749 }
1750
1751 #define CMP_PARAM(Param)                        \
1752   if (e1->Param != e2->Param)                   \
1753     return e1->Param - e2->Param;
1754
1755 EAPI int
1756 ethumb_key_cmp(const void *key1, __UNUSED__ int key1_length,
1757                const void *key2, __UNUSED__ int key2_length)
1758 {
1759    const Ethumb *e1 = key1;
1760    const Ethumb *e2 = key2;
1761
1762    CMP_PARAM(thumb_dir);
1763    CMP_PARAM(category);
1764    CMP_PARAM(tw);
1765    CMP_PARAM(th);
1766    CMP_PARAM(format);
1767    CMP_PARAM(aspect);
1768    CMP_PARAM(orientation);
1769    CMP_PARAM(crop_x);
1770    CMP_PARAM(crop_y);
1771    CMP_PARAM(quality);
1772    CMP_PARAM(compress);
1773    CMP_PARAM(rw);
1774    CMP_PARAM(rh);
1775    CMP_PARAM(video.start);
1776    CMP_PARAM(video.time);
1777    CMP_PARAM(video.interval);
1778    CMP_PARAM(video.ntimes);
1779    CMP_PARAM(video.fps);
1780    CMP_PARAM(document.page);
1781    CMP_PARAM(src_path);
1782    CMP_PARAM(src_key);
1783
1784    return 0;
1785 }
1786
1787 #undef CMP_PARAM
1788
1789 #define HASH_PARAM_I(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
1790 #ifdef __LP64__
1791 # define HASH_PARAM_P(Param) r ^= eina_hash_int64((unsigned long int*) &e->Param, 0);
1792 #else
1793 # define HASH_PARAM_P(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
1794 #endif
1795 #define HASH_PARAM_D(Param) r ^= eina_hash_int64((unsigned long int*)&e->Param, 0);
1796 #define HASH_PARAM_F(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
1797
1798 EAPI int
1799 ethumb_hash(const void *key, int key_length __UNUSED__)
1800 {
1801    const Ethumb *e = key;
1802    int r = 0;
1803
1804    HASH_PARAM_P(thumb_dir);
1805    HASH_PARAM_P(category);
1806    HASH_PARAM_I(tw);
1807    HASH_PARAM_I(th);
1808    HASH_PARAM_I(format);
1809    HASH_PARAM_I(aspect);
1810    HASH_PARAM_I(orientation);
1811    HASH_PARAM_F(crop_x);
1812    HASH_PARAM_F(crop_y);
1813    HASH_PARAM_I(quality);
1814    HASH_PARAM_I(compress);
1815    HASH_PARAM_P(src_path);
1816    HASH_PARAM_P(src_key);
1817    HASH_PARAM_I(rw);
1818    HASH_PARAM_I(rh);
1819    HASH_PARAM_D(video.start);
1820    HASH_PARAM_D(video.time);
1821    HASH_PARAM_D(video.interval);
1822    HASH_PARAM_I(video.ntimes);
1823    HASH_PARAM_I(video.fps);
1824    HASH_PARAM_I(document.page);
1825
1826    return r;
1827 }