4 * Copyright (C) 2009 by ProFUSION embedded systems
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.
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.
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/>.
20 * @author Rafael Antognolli <antognolli@profusion.mobi>
28 #elif defined __GNUC__
29 # define alloca __builtin_alloca
31 # define alloca __alloca
32 #elif defined _MSC_VER
34 # define alloca _alloca
40 void *alloca (size_t);
49 #include <sys/types.h>
56 # include <sys/xattr.h>
60 # define PATH_MAX 4096
64 #include <eina_safety_checks.h>
67 #include <Ecore_Evas.h>
68 #include <Ecore_File.h>
72 #include "ethumb_private.h"
73 #include "Ethumb_Plugin.h"
77 #include <libexif/exif-data.h>
80 static Ethumb_Version _version = { VMAJ, VMIN, VMIC, VREV };
81 EAPI Ethumb_Version *ethumb_version = &_version;
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__)
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;
94 static const int THUMB_SIZE_NORMAL = 128;
95 static const int THUMB_SIZE_LARGE = 256;
97 static Eina_Hash *_plugins_ext = NULL;
98 static Eina_Array *_plugins = NULL;
101 _ethumb_plugin_list_cb(Eina_Module *m, void *data __UNUSED__)
105 Ethumb_Plugin *plugin;
106 Ethumb_Plugin *(*plugin_get)(void);
108 file = eina_module_file_get(m);
109 if (!eina_module_load(m))
111 ERR("could not load module \"%s\": %s",
112 file, eina_error_msg_get(eina_error_get()));
116 plugin_get = eina_module_symbol_get(m, "ethumb_plugin_get");
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);
125 plugin = plugin_get();
128 ERR("plugin \"%s\" failed to init.", file);
129 eina_module_unload(m);
133 DBG("loaded plugin \"%s\" (%p) with extensions:", file, plugin);
134 for (ext = plugin->extensions; *ext; ext++)
136 DBG(" extension \"%s\"", *ext);
137 eina_hash_add(_plugins_ext, *ext, plugin);
144 _ethumb_plugins_load(void)
146 _plugins_ext = eina_hash_string_small_new(NULL);
147 EINA_SAFETY_ON_NULL_RETURN(_plugins_ext);
149 _plugins = eina_module_list_get(_plugins, PLUGINSDIR, 1,
150 &_ethumb_plugin_list_cb, NULL);
154 _ethumb_plugins_unload(void)
156 eina_hash_free(_plugins_ext);
158 eina_module_list_unload(_plugins);
159 eina_module_list_free(_plugins);
160 eina_array_free(_plugins);
175 fprintf(stderr, "ERROR: Could not initialize eina.\n");
178 _log_dom = eina_log_domain_register("ethumb", EINA_COLOR_GREEN);
181 EINA_LOG_ERR("Could not register log domain: ethumb");
191 home = getenv("HOME");
192 snprintf(buf, sizeof(buf), "%s/.thumbnails", home);
194 _home_thumb_dir = eina_stringshare_add(buf);
195 _thumb_category_normal = eina_stringshare_add("normal");
196 _thumb_category_large = eina_stringshare_add("large");
198 _ethumb_plugins_load();
203 ethumb_shutdown(void)
207 EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
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);
219 ecore_evas_shutdown();
221 eina_log_domain_unregister(_log_dom);
233 Ecore_Evas *ee, *sub_ee;
235 Evas_Object *o, *img;
237 ethumb = calloc(1, sizeof(Ethumb));
238 EINA_SAFETY_ON_NULL_RETURN_VAL(ethumb, NULL);
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;
254 ee = ecore_evas_buffer_new(1, 1);
255 e = ecore_evas_get(ee);
258 ERR("could not create ecore evas buffer");
263 evas_image_cache_set(e, 0);
264 evas_font_cache_set(e, 0);
266 o = ecore_evas_object_image_new(ee);
269 ERR("could not create sub ecore evas buffer");
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);
279 evas_image_cache_set(sub_e, 0);
280 evas_font_cache_set(sub_e, 0);
282 img = evas_object_image_add(sub_e);
285 ERR("could not create source objects.");
293 ethumb->sub_ee = sub_ee;
294 ethumb->sub_e = sub_e;
298 DBG("ethumb=%p", ethumb);
304 _ethumb_frame_free(Ethumb_Frame *frame)
311 if (frame->swallow && frame->edje)
313 o = edje_object_part_swallow_get(frame->edje, frame->swallow);
315 edje_object_part_unswallow(frame->edje, o);
317 eina_stringshare_del(frame->file);
318 eina_stringshare_del(frame->group);
319 eina_stringshare_del(frame->swallow);
322 evas_object_del(frame->edje);
328 ethumb_free(Ethumb *ethumb)
330 EINA_SAFETY_ON_NULL_RETURN(ethumb);
332 DBG("ethumb=%p", ethumb);
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);
347 ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s)
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);
354 if (s == ETHUMB_THUMB_NORMAL)
356 e->tw = THUMB_SIZE_NORMAL;
357 e->th = THUMB_SIZE_NORMAL;
361 e->tw = THUMB_SIZE_LARGE;
362 e->th = THUMB_SIZE_LARGE;
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);
370 eina_stringshare_del(e->thumb_dir);
371 eina_stringshare_del(e->category);
377 ethumb_thumb_size_set(Ethumb *e, int tw, int th)
379 EINA_SAFETY_ON_NULL_RETURN(e);
380 EINA_SAFETY_ON_FALSE_RETURN(tw > 0);
381 EINA_SAFETY_ON_FALSE_RETURN(th > 0);
383 DBG("ethumb=%p, w=%d, h=%d", e, tw, th);
389 ethumb_thumb_size_get(const Ethumb *e, int *tw, int *th)
391 EINA_SAFETY_ON_NULL_RETURN(e);
398 ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f)
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);
405 DBG("ethumb=%p, format=%d", e, f);
409 EAPI Ethumb_Thumb_Format
410 ethumb_thumb_format_get(const Ethumb *e)
412 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
417 ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a)
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);
424 DBG("ethumb=%p, aspect=%d", e, a);
428 EAPI Ethumb_Thumb_Aspect
429 ethumb_thumb_aspect_get(const Ethumb *e)
431 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
436 ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o)
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);
449 DBG("ethumb=%p, orientation=%d", e, o);
453 EAPI Ethumb_Thumb_Orientation
454 ethumb_thumb_orientation_get(const Ethumb *e)
456 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
457 return e->orientation;
461 ethumb_thumb_crop_align_set(Ethumb *e, float x, float y)
463 EINA_SAFETY_ON_NULL_RETURN(e);
465 DBG("ethumb=%p, x=%f, y=%f", e, x, y);
471 ethumb_thumb_crop_align_get(const Ethumb *e, float *x, float *y)
473 EINA_SAFETY_ON_NULL_RETURN(e);
475 if (x) *x = e->crop_x;
476 if (y) *y = e->crop_y;
480 ethumb_thumb_quality_set(Ethumb *e, int quality)
482 EINA_SAFETY_ON_NULL_RETURN(e);
484 DBG("ethumb=%p, quality=%d", e, quality);
485 e->quality = quality;
489 ethumb_thumb_quality_get(const Ethumb *e)
491 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
496 ethumb_thumb_compress_set(Ethumb *e, int compress)
498 EINA_SAFETY_ON_NULL_RETURN(e);
500 DBG("ethumb=%p, compress=%d", e, compress);
501 e->compress = compress;
505 ethumb_thumb_compress_get(const Ethumb *e)
507 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
512 ethumb_frame_set(Ethumb *e, const char *theme_file, const char *group, const char *swallow)
514 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
519 DBG("ethumb=%p, theme_file=%s, group=%s, swallow=%s",
520 e, theme_file ? theme_file : "", group ? group : "",
521 swallow ? swallow : "");
525 edje_object_part_unswallow(frame->edje, e->img);
527 _ethumb_frame_free(frame);
538 frame = calloc(1, sizeof(Ethumb_Frame));
541 ERR("could not allocate Ethumb_Frame structure.");
545 frame->edje = edje_object_add(e->sub_e);
548 ERR("could not create edje frame object.");
549 _ethumb_frame_free(frame);
555 if (!edje_object_file_set(frame->edje, theme_file, group))
557 ERR("could not load frame theme.");
558 _ethumb_frame_free(frame);
563 edje_object_part_swallow(frame->edje, swallow, e->img);
564 if (!edje_object_part_swallow_get(frame->edje, swallow))
566 ERR("could not swallow image to edje frame.");
567 _ethumb_frame_free(frame);
572 eina_stringshare_replace(&frame->file, theme_file);
573 eina_stringshare_replace(&frame->group, group);
574 eina_stringshare_replace(&frame->swallow, swallow);
582 ethumb_frame_get(const Ethumb *e, const char **theme_file, const char **group, const char **swallow)
584 EINA_SAFETY_ON_NULL_RETURN(e);
588 if (theme_file) *theme_file = e->frame->file;
589 if (group) *group = e->frame->group;
590 if (swallow) *swallow = e->frame->swallow;
594 if (theme_file) *theme_file = NULL;
595 if (group) *group = NULL;
596 if (swallow) *swallow = NULL;
601 _ethumb_build_absolute_path(const char *path, char buf[PATH_MAX])
613 else if (path[0] == '~')
615 const char *home = getenv("HOME");
627 if (!getcwd(p, PATH_MAX))
640 ethumb_thumb_dir_path_set(Ethumb *e, const char *path)
643 EINA_SAFETY_ON_NULL_RETURN(e);
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);
651 ethumb_thumb_dir_path_get(const Ethumb *e)
653 EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
659 ethumb_thumb_category_set(Ethumb *e, const char *category)
661 EINA_SAFETY_ON_NULL_RETURN(e);
663 DBG("ethumb=%p, category=%s", e, category ? category : "");
664 eina_stringshare_replace(&e->category, category);
668 ethumb_thumb_category_get(const Ethumb *e)
670 EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
676 ethumb_video_start_set(Ethumb *e, float start)
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);
682 DBG("ethumb=%p, video_start=%f", e, start);
683 e->video.start = start;
687 ethumb_video_start_get(const Ethumb *e)
689 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
691 return e->video.start;
695 ethumb_video_time_set(Ethumb *e, float t)
697 EINA_SAFETY_ON_NULL_RETURN(e);
699 DBG("ethumb=%p, video_start=%f", e, t);
704 ethumb_video_time_get(const Ethumb *e)
706 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
708 return e->video.time;
712 ethumb_video_interval_set(Ethumb *e, float interval)
714 EINA_SAFETY_ON_NULL_RETURN(e);
716 DBG("ethumb=%p, video_interval=%f", e, interval);
717 e->video.interval = interval;
721 ethumb_video_interval_get(const Ethumb *e)
723 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
725 return e->video.interval;
729 ethumb_video_ntimes_set(Ethumb *e, unsigned int ntimes)
731 EINA_SAFETY_ON_NULL_RETURN(e);
732 EINA_SAFETY_ON_FALSE_RETURN(ntimes > 0);
734 DBG("ethumb=%p, video_ntimes=%d", e, ntimes);
735 e->video.ntimes = ntimes;
739 ethumb_video_ntimes_get(const Ethumb *e)
741 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
743 return e->video.ntimes;
747 ethumb_video_fps_set(Ethumb *e, unsigned int fps)
749 EINA_SAFETY_ON_NULL_RETURN(e);
750 EINA_SAFETY_ON_FALSE_RETURN(fps > 0);
752 DBG("ethumb=%p, video_fps=%d", e, fps);
757 ethumb_video_fps_get(const Ethumb *e)
759 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
765 ethumb_document_page_set(Ethumb *e, unsigned int page)
767 EINA_SAFETY_ON_NULL_RETURN(e);
769 DBG("ethumb=%p, document_page=%d", e, page);
770 e->document.page = page;
774 ethumb_document_page_get(const Ethumb *e)
776 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
778 return e->document.page;
782 ethumb_file_set(Ethumb *e, const char *path, const char *key)
785 EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
787 eina_stringshare_replace(&e->thumb_path, NULL);
788 eina_stringshare_replace(&e->thumb_key, NULL);
790 DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
791 if (path && access(path, R_OK))
793 ERR("couldn't access file \"%s\"", path);
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);
806 ethumb_file_get(const Ethumb *e, const char **path, const char **key)
808 EINA_SAFETY_ON_NULL_RETURN(e);
810 if (path) *path = e->src_path;
811 if (key) *key = e->src_key;
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
830 _ethumb_generate_hash(const char *file)
834 char md5out[(2 * MD5_HASHBYTES) + 1];
835 unsigned char hash[MD5_HASHBYTES];
836 static const char hex[] = "0123456789abcdef";
840 const unsigned char *c;
845 length = getxattr(file, "user.e.md5", NULL, 0);
851 tmp = alloca(length);
852 length = getxattr(file, "user.e.md5", tmp, length);
854 /* check if we have at least something that look like a md5 hash */
855 if (length > 0 && (length == MD5_HASHBYTES * 2 + 1))
858 return eina_stringshare_add(tmp);
863 #define _check_uri_char(c) \
864 ((c) >= 32 && (c) < 128 && (ACCEPTABLE_URI_CHARS[(c) - 32] & 0x08))
866 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
868 uri = alloca(3 * strlen(file) + 9);
869 memcpy(uri, "file://", sizeof("file://") - 1);
870 t = uri + sizeof("file://") - 1;
872 for (c = (const unsigned char *)file; *c != '\0'; c++)
874 if (!_check_uri_char(*c))
885 #undef _check_uri_char
888 MD5Update (&ctx, (unsigned char const*)uri, (unsigned)strlen (uri));
889 MD5Final (hash, &ctx);
891 for (n = 0; n < MD5_HASHBYTES; n++)
893 md5out[2 * n] = hex[hash[n] >> 4];
894 md5out[2 * n + 1] = hex[hash[n] & 0x0f];
896 md5out[2 * n] = '\0';
899 setxattr(file, "user.e.md5", md5out, 2 * n + 1, 0);
902 DBG("md5=%s, file=%s", md5out, file);
903 return eina_stringshare_add(md5out);
907 _ethumb_file_check_fdo(Ethumb *e)
909 if (!((e->tw == THUMB_SIZE_NORMAL && e->th == THUMB_SIZE_NORMAL) ||
910 (e->tw == THUMB_SIZE_LARGE && e->th == THUMB_SIZE_LARGE)))
913 if (e->format != ETHUMB_THUMB_FDO)
916 if (e->aspect != ETHUMB_THUMB_KEEP_ASPECT)
926 _ethumb_file_generate_custom_category(Ethumb *e)
929 const char *aspect, *format;
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";
939 if (e->format == ETHUMB_THUMB_FDO)
941 else if (e->format == ETHUMB_THUMB_JPEG)
951 snprintf(buf, sizeof(buf), "%dx%d-%s%s-%s",
952 e->tw, e->th, aspect, frame, format);
954 return eina_stringshare_add(buf);
958 _ethumb_file_generate_path(Ethumb *e)
961 const char *thumb_dir, *category;
965 fdo_format = _ethumb_file_check_fdo(e);
968 thumb_dir = eina_stringshare_ref(e->thumb_dir);
970 thumb_dir = eina_stringshare_ref(_home_thumb_dir);
973 category = eina_stringshare_ref(e->category);
974 else if (!fdo_format)
975 category = _ethumb_file_generate_custom_category(e);
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);
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";
990 if (e->format == ETHUMB_THUMB_FDO)
992 else if (e->format == ETHUMB_THUMB_JPEG)
1001 fullname = ecore_file_realpath(e->src_path);
1002 e->src_hash = _ethumb_generate_hash(fullname);
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");
1012 eina_stringshare_del(e->thumb_key);
1013 e->thumb_key = NULL;
1016 eina_stringshare_del(thumb_dir);
1017 eina_stringshare_del(category);
1021 ethumb_file_free(Ethumb *e)
1023 EINA_SAFETY_ON_NULL_RETURN(e);
1024 DBG("ethumb=%p", e);
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);
1034 ethumb_thumb_path_set(Ethumb *e, const char *path, const char *key)
1038 EINA_SAFETY_ON_NULL_RETURN(e);
1039 DBG("ethumb=%p, path=%s, key=%s", e, path ? path : "", key ? key : "");
1043 eina_stringshare_replace(&e->thumb_path, NULL);
1044 eina_stringshare_replace(&e->thumb_key, NULL);
1048 path = _ethumb_build_absolute_path(path, buf);
1049 eina_stringshare_replace(&e->thumb_path, path);
1050 eina_stringshare_replace(&e->thumb_key, key);
1055 ethumb_thumb_path_get(Ethumb *e, const char **path, const char **key)
1057 EINA_SAFETY_ON_NULL_RETURN(e);
1059 _ethumb_file_generate_path(e);
1061 if (path) *path = e->thumb_path;
1062 if (key) *key = e->thumb_key;
1066 ethumb_thumb_hash(Ethumb *e)
1068 EINA_SAFETY_ON_NULL_RETURN(e);
1073 fullname = ecore_file_realpath(e->src_path);
1074 e->src_hash = _ethumb_generate_hash(fullname);
1080 ethumb_thumb_hash_copy(Ethumb *dst, const Ethumb *src)
1082 EINA_SAFETY_ON_NULL_RETURN(dst);
1083 EINA_SAFETY_ON_NULL_RETURN(src);
1085 if (src == dst) return ;
1087 eina_stringshare_del(dst->src_hash);
1088 dst->src_hash = eina_stringshare_ref(src->src_hash);
1092 ethumb_calculate_aspect_from_ratio(Ethumb *e, float ia, int *w, int *h)
1102 a = e->tw / (float)e->th;
1104 if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
1106 if ((ia > a && e->tw > 0) || e->th <= 0)
1114 ethumb_calculate_aspect(Ethumb *e, int iw, int ih, int *w, int *h)
1118 ia = iw / (float)ih;
1120 ethumb_calculate_aspect_from_ratio(e, ia, w, h);
1124 ethumb_calculate_fill_from_ratio(Ethumb *e, float ia, int *fx, int *fy, int *fw, int *fh)
1136 a = e->tw / (float)e->th;
1138 if (e->aspect == ETHUMB_THUMB_CROP)
1140 if ((ia > a && e->tw > 0) || e->th <= 0)
1145 *fx = - e->crop_x * (*fw - e->tw);
1146 *fy = - e->crop_y * (*fh - e->th);
1148 else if (e->aspect == ETHUMB_THUMB_KEEP_ASPECT)
1150 if ((ia > a && e->tw > 0) || e->th <= 0)
1158 ethumb_calculate_fill(Ethumb *e, int iw, int ih, int *fx, int *fy, int *fw, int *fh)
1161 ia = iw / (float)ih;
1163 ethumb_calculate_fill_from_ratio(e, ia, fx, fy, fw, fh);
1167 _ethumb_plugin_generate(Ethumb *e)
1171 Ethumb_Plugin *plugin;
1174 extp = strrchr(e->src_path, '.');
1177 ERR("could not get extension for file \"%s\"", e->src_path);
1181 for (i = 0; extp[i] != '\0'; i++)
1182 ext[i] = tolower(extp[i + 1]);
1184 plugin = eina_hash_find(_plugins_ext, ext);
1187 DBG("no plugin for extension: \"%s\"", ext);
1192 evas_object_hide(e->frame->edje);
1194 evas_object_hide(e->img);
1197 e->pdata = plugin->thumb_generate(e);
1203 ethumb_plugin_image_resize(Ethumb *e, int w, int h)
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);
1219 evas_object_move(img, 0, 0);
1220 evas_object_resize(img, w, h);
1223 evas_object_image_size_set(e->o, w, h);
1224 ecore_evas_resize(e->sub_ee, w, h);
1233 ethumb_image_save(Ethumb *e)
1239 evas_damage_rectangle_add(e->sub_e, 0, 0, e->rw, e->rh);
1240 evas_render(e->sub_e);
1243 _ethumb_file_generate_path(e);
1247 ERR("could not create file path...");
1251 dname = ecore_file_dir_get(e->thumb_path);
1252 r = ecore_file_mkpath(dname);
1256 ERR("could not create directory '%s'", dname);
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);
1266 ERR("could not save image: path=%s, key=%s", e->thumb_path,
1275 _ethumb_image_orient(Ethumb *e, int orientation)
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;
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);
1287 switch (orientation)
1289 case ETHUMB_THUMB_FLIP_HORIZONTAL:
1290 for (y = 0; y < ih; y++)
1292 p1 = data + (y * iw);
1293 p2 = data + ((y + 1) * iw) - 1;
1294 for (x = 0; x < (iw >> 1); x++)
1303 evas_object_image_data_set(img, data);
1304 evas_object_image_data_update_add(img, 0, 0, iw, ih);
1306 case ETHUMB_THUMB_FLIP_VERTICAL:
1307 for (y = 0; y < (ih >> 1); y++)
1309 p1 = data + (y * iw);
1310 p2 = data + ((ih - 1 - y) * iw);
1311 for (x = 0; x < iw; x++)
1320 evas_object_image_data_set(img, data);
1321 evas_object_image_data_update_add(img, 0, 0, iw, ih);
1323 case ETHUMB_THUMB_ROTATE_180:
1336 evas_object_image_data_set(img, data);
1337 evas_object_image_data_update_add(img, 0, 0, iw, ih);
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);
1351 evas_object_image_size_set(img, iw, ih);
1352 data = evas_object_image_data_get(img, 1);
1354 switch (orientation)
1356 case ETHUMB_THUMB_FLIP_TRANSPOSE:
1360 case ETHUMB_THUMB_FLIP_TRANSVERSE:
1365 case ETHUMB_THUMB_ROTATE_90_CW:
1369 case ETHUMB_THUMB_ROTATE_90_CCW:
1375 ERR("unknown orient %d", orientation);
1376 evas_object_del(tmp);
1377 evas_object_image_data_set(img, data); // give it back
1381 for (x = iw; --x >= 0;)
1383 for (y = ih; --y >= 0;)
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);
1397 _ethumb_image_load(Ethumb *e)
1400 Evas_Coord w, h, ww, hh, fx, fy, fw, fh;
1402 int orientation = ETHUMB_THUMB_ORIENT_NONE;
1407 evas_object_hide(e->frame->edje);
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);
1415 evas_object_show(e->frame->edje);
1417 evas_object_show(img);
1419 error = evas_object_image_load_error_get(img);
1420 if (error != EVAS_LOAD_ERROR_NONE)
1422 ERR("could not load image '%s': %d", e->src_path, error);
1426 if (e->orientation == ETHUMB_THUMB_ORIENT_ORIGINAL)
1429 ExifData *exif = exif_data_new_from_file(e->src_path);
1430 ExifEntry *entry = NULL;
1436 entry = exif_data_get_entry(exif, EXIF_TAG_ORIENTATION);
1439 bo = exif_data_get_byte_order(exif);
1440 o = exif_get_short(entry->data, bo);
1442 exif_data_free(exif);
1446 orientation = ETHUMB_THUMB_FLIP_HORIZONTAL;
1449 orientation = ETHUMB_THUMB_ROTATE_180;
1452 orientation = ETHUMB_THUMB_FLIP_VERTICAL;
1455 orientation = ETHUMB_THUMB_FLIP_TRANSPOSE;
1458 orientation = ETHUMB_THUMB_ROTATE_90_CW;
1461 orientation = ETHUMB_THUMB_FLIP_TRANSVERSE;
1464 orientation = ETHUMB_THUMB_ROTATE_90_CCW;
1471 if (orientation != ETHUMB_THUMB_ORIENT_NONE)
1472 _ethumb_image_orient(e, orientation);
1474 evas_object_image_size_get(img, &w, &h);
1475 if ((w <= 0) || (h <= 0))
1478 ethumb_calculate_aspect(e, w, h, &ww, &hh);
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);
1490 evas_object_move(img, 0, 0);
1491 evas_object_resize(img, ww, hh);
1494 ethumb_calculate_fill(e, w, h, &fx, &fy, &fw, &fh);
1495 evas_object_image_fill_set(img, fx, fy, fw, fh);
1497 evas_object_image_size_set(e->o, ww, hh);
1498 ecore_evas_resize(e->sub_ee, ww, hh);
1507 _ethumb_finished_idler_cb(void *data)
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;
1517 e->cb_data_free = NULL;
1523 ethumb_finished_callback_call(Ethumb *e, int result)
1525 EINA_SAFETY_ON_NULL_RETURN(e);
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);
1536 ethumb_generate(Ethumb *e, Ethumb_Generate_Cb finished_cb, const void *data, Eina_Free_Cb free_data)
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 : "");
1546 if (e->finished_idler)
1548 ERR("thumbnail generation already in progress.");
1553 e->plugin->thumb_cancel(e, e->pdata);
1558 e->finished_cb = finished_cb;
1559 e->cb_data = (void *)data;
1560 e->cb_data_free = free_data;
1564 ERR("no file set.");
1565 ethumb_finished_callback_call(e, 0);
1569 r = _ethumb_plugin_generate(e);
1570 fprintf(stderr, "ethumb generate: %i: %p\n", r, e->pdata);
1576 if (!_ethumb_image_load(e))
1578 ERR("could not load input image.");
1579 ethumb_finished_callback_call(e, 0);
1583 r = ethumb_image_save(e);
1585 ethumb_finished_callback_call(e, r);
1591 ethumb_exists(Ethumb *e)
1593 struct stat thumb, src;
1595 Eina_Bool r = EINA_FALSE;
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 : "");
1602 _ethumb_file_generate_path(e);
1604 EINA_SAFETY_ON_NULL_RETURN_VAL(e->thumb_path, 0);
1606 r_thumb = stat(e->thumb_path, &thumb);
1607 r_src = stat(e->src_path, &src);
1609 EINA_SAFETY_ON_TRUE_RETURN_VAL(r_src, 0);
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)
1620 ethumb_evas_get(const Ethumb *e)
1622 EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
1628 ethumb_ecore_evas_get(const Ethumb *e)
1630 EINA_SAFETY_ON_NULL_RETURN_VAL(e, NULL);
1636 ethumb_dup(const Ethumb *e)
1646 r = malloc(sizeof (Ethumb));
1647 if (!r) return NULL;
1649 memcpy(r, e, sizeof (Ethumb));
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);
1659 ee = ecore_evas_buffer_new(1, 1);
1660 ev = ecore_evas_get(ee);
1663 ERR("could not create ecore evas buffer");
1668 evas_image_cache_set(ev, 0);
1669 evas_font_cache_set(ev, 0);
1671 o = ecore_evas_object_image_new(ee);
1674 ERR("could not create sub ecore evas buffer");
1675 ecore_evas_free(ee);
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);
1684 evas_image_cache_set(sub_ev, 0);
1685 evas_font_cache_set(sub_ev, 0);
1687 img = evas_object_image_add(sub_ev);
1690 ERR("could not create source objects.");
1691 ecore_evas_free(ee);
1704 r->finished_idler = NULL;
1705 r->finished_cb = NULL;
1707 r->cb_data_free = NULL;
1715 #define CHECK_DELTA(Param) \
1716 if (e1->Param != e2->Param) \
1720 ethumb_cmp(const Ethumb *e1, const Ethumb *e2)
1722 CHECK_DELTA(thumb_dir);
1723 CHECK_DELTA(category);
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);
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);
1746 ethumb_length(__UNUSED__ const void *key)
1748 return sizeof (Ethumb);
1751 #define CMP_PARAM(Param) \
1752 if (e1->Param != e2->Param) \
1753 return e1->Param - e2->Param;
1756 ethumb_key_cmp(const void *key1, __UNUSED__ int key1_length,
1757 const void *key2, __UNUSED__ int key2_length)
1759 const Ethumb *e1 = key1;
1760 const Ethumb *e2 = key2;
1762 CMP_PARAM(thumb_dir);
1763 CMP_PARAM(category);
1768 CMP_PARAM(orientation);
1772 CMP_PARAM(compress);
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);
1789 #define HASH_PARAM_I(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
1791 # define HASH_PARAM_P(Param) r ^= eina_hash_int64((unsigned long int*) &e->Param, 0);
1793 # define HASH_PARAM_P(Param) r ^= eina_hash_int32((unsigned int*) &e->Param, 0);
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);
1799 ethumb_hash(const void *key, int key_length __UNUSED__)
1801 const Ethumb *e = key;
1804 HASH_PARAM_P(thumb_dir);
1805 HASH_PARAM_P(category);
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);
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);