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