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