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