Really fix issue in 2a9e4d6
[geeqie.git] / src / image-load.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "image-load.h"
16 #include "image_load_gdk.h"
17 #include "image_load_jpeg.h"
18 #include "image_load_tiff.h"
19
20 #include "exif.h"
21 #include "filedata.h"
22 #include "ui_fileops.h"
23 #include "gq-marshal.h"
24
25 #include <fcntl.h>
26 #include <sys/mman.h>
27
28 #define IMAGE_LOADER_READ_BUFFER_SIZE_DEFAULT   4096
29 #define IMAGE_LOADER_IDLE_READ_LOOP_COUNT_DEFAULT       1
30
31
32 /**************************************************************************************/
33 /* image loader class */
34
35
36 enum {
37         SIGNAL_AREA_READY = 0,
38         SIGNAL_ERROR,
39         SIGNAL_DONE,
40         SIGNAL_PERCENT,
41         SIGNAL_SIZE,
42         SIGNAL_COUNT
43 };
44
45 static guint signals[SIGNAL_COUNT] = { 0 };
46
47 static void image_loader_init(GTypeInstance *instance, gpointer g_class);
48 static void image_loader_class_init(ImageLoaderClass *class);
49 static void image_loader_finalize(GObject *object);
50 static void image_loader_stop(ImageLoader *il);
51
52 GType image_loader_get_type(void)
53 {
54         static GType type = 0;
55         if (type == 0)
56                 {
57                 static const GTypeInfo info = {
58                         sizeof(ImageLoaderClass),
59                         NULL,   /* base_init */
60                         NULL,   /* base_finalize */
61                         (GClassInitFunc)image_loader_class_init, /* class_init */
62                         NULL,   /* class_finalize */
63                         NULL,   /* class_data */
64                         sizeof(ImageLoader),
65                         0,      /* n_preallocs */
66                         (GInstanceInitFunc)image_loader_init, /* instance_init */
67                         NULL    /* value_table */
68                         };
69                 type = g_type_register_static(G_TYPE_OBJECT, "ImageLoaderType", &info, 0);
70                 }
71         return type;
72 }
73
74 static void image_loader_init(GTypeInstance *instance, gpointer g_class)
75 {
76         ImageLoader *il = (ImageLoader *)instance;
77
78         il->pixbuf = NULL;
79         il->idle_id = 0;
80         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
81         il->done = FALSE;
82         il->loader = NULL;
83
84         il->bytes_read = 0;
85         il->bytes_total = 0;
86
87         il->idle_done_id = 0;
88
89         il->idle_read_loop_count = IMAGE_LOADER_IDLE_READ_LOOP_COUNT_DEFAULT;
90         il->read_buffer_size = IMAGE_LOADER_READ_BUFFER_SIZE_DEFAULT;
91         il->mapped_file = NULL;
92
93         il->requested_width = 0;
94         il->requested_height = 0;
95         il->actual_width = 0;
96         il->actual_height = 0;
97         il->shrunk = FALSE;
98
99         il->can_destroy = TRUE;
100
101 #ifdef HAVE_GTHREAD
102 #if GLIB_CHECK_VERSION(2,32,0)
103         il->data_mutex = g_new(GMutex, 1);
104         g_mutex_init(il->data_mutex);
105         il->can_destroy_cond = g_new(GCond, 1);
106         g_cond_init(il->can_destroy_cond);
107 #else
108         il->data_mutex = g_mutex_new();
109         il->can_destroy_cond = g_cond_new();
110 #endif
111 #endif
112         DEBUG_1("new image loader %p, bufsize=%" G_GSIZE_FORMAT " idle_loop=%u", il, il->read_buffer_size, il->idle_read_loop_count);
113 }
114
115 static void image_loader_class_init(ImageLoaderClass *class)
116 {
117         GObjectClass *gobject_class = G_OBJECT_CLASS (class);
118
119 //      gobject_class->set_property = image_loader_set_property;
120 //      gobject_class->get_property = image_loader_get_property;
121
122         gobject_class->finalize = image_loader_finalize;
123
124
125         signals[SIGNAL_AREA_READY] =
126                 g_signal_new("area_ready",
127                              G_OBJECT_CLASS_TYPE(gobject_class),
128                              G_SIGNAL_RUN_LAST,
129                              G_STRUCT_OFFSET(ImageLoaderClass, area_ready),
130                              NULL, NULL,
131                              gq_marshal_VOID__INT_INT_INT_INT,
132                              G_TYPE_NONE, 4,
133                              G_TYPE_INT,
134                              G_TYPE_INT,
135                              G_TYPE_INT,
136                              G_TYPE_INT);
137
138         signals[SIGNAL_ERROR] =
139                 g_signal_new("error",
140                              G_OBJECT_CLASS_TYPE(gobject_class),
141                              G_SIGNAL_RUN_LAST,
142                              G_STRUCT_OFFSET(ImageLoaderClass, error),
143                              NULL, NULL,
144                              g_cclosure_marshal_VOID__VOID,
145                              G_TYPE_NONE, 0);
146
147         signals[SIGNAL_DONE] =
148                 g_signal_new("done",
149                              G_OBJECT_CLASS_TYPE(gobject_class),
150                              G_SIGNAL_RUN_LAST,
151                              G_STRUCT_OFFSET(ImageLoaderClass, done),
152                              NULL, NULL,
153                              g_cclosure_marshal_VOID__VOID,
154                              G_TYPE_NONE, 0);
155
156         signals[SIGNAL_PERCENT] =
157                 g_signal_new("percent",
158                              G_OBJECT_CLASS_TYPE(gobject_class),
159                              G_SIGNAL_RUN_LAST,
160                              G_STRUCT_OFFSET(ImageLoaderClass, percent),
161                              NULL, NULL,
162                              g_cclosure_marshal_VOID__DOUBLE,
163                              G_TYPE_NONE, 1,
164                              G_TYPE_DOUBLE);
165
166         signals[SIGNAL_SIZE] =
167                 g_signal_new("size_prepared",
168                              G_OBJECT_CLASS_TYPE(gobject_class),
169                              G_SIGNAL_RUN_LAST,
170                              G_STRUCT_OFFSET(ImageLoaderClass, area_ready),
171                              NULL, NULL,
172                              gq_marshal_VOID__INT_INT,
173                              G_TYPE_NONE, 2,
174                              G_TYPE_INT,
175                              G_TYPE_INT);
176
177 }
178
179 static void image_loader_finalize(GObject *object)
180 {
181         ImageLoader *il = (ImageLoader *)object;
182
183         image_loader_stop(il);
184
185         if (il->error) DEBUG_1("%s", image_loader_get_error(il));
186
187         DEBUG_1("freeing image loader %p bytes_read=%" G_GSIZE_FORMAT, il, il->bytes_read);
188
189         if (il->idle_done_id)
190                 {
191                 g_source_remove(il->idle_done_id);
192                 il->idle_done_id = 0;
193                 }
194
195         while (g_source_remove_by_user_data(il))
196                 {
197                 DEBUG_2("pending signals detected");
198                 }
199
200         while (il->area_param_list)
201                 {
202                 DEBUG_1("pending area_ready signals detected");
203                 while (g_source_remove_by_user_data(il->area_param_list->data)) {}
204                 g_free(il->area_param_list->data);
205                 il->area_param_list = g_list_delete_link(il->area_param_list, il->area_param_list);
206                 }
207
208         while (il->area_param_delayed_list)
209                 {
210                 g_free(il->area_param_delayed_list->data);
211                 il->area_param_delayed_list = g_list_delete_link(il->area_param_delayed_list, il->area_param_delayed_list);
212                 }
213
214         if (il->pixbuf) g_object_unref(il->pixbuf);
215
216         if (il->error) g_error_free(il->error);
217
218         file_data_unref(il->fd);
219 #ifdef HAVE_GTHREAD
220 #if GLIB_CHECK_VERSION(2,32,0)
221         g_mutex_clear(il->data_mutex);
222         g_free(il->data_mutex);
223         g_cond_clear(il->can_destroy_cond);
224         g_free(il->can_destroy_cond);
225 #else
226         g_mutex_free(il->data_mutex);
227         g_cond_free(il->can_destroy_cond);
228 #endif
229 #endif
230 }
231
232 void image_loader_free(ImageLoader *il)
233 {
234         if (!il) return;
235         g_object_unref(G_OBJECT(il));
236 }
237
238
239 ImageLoader *image_loader_new(FileData *fd)
240 {
241         ImageLoader *il;
242
243         if (!fd) return NULL;
244
245         il = (ImageLoader *) g_object_new(TYPE_IMAGE_LOADER, NULL);
246
247         il->fd = file_data_ref(fd);
248
249         return il;
250 }
251
252 /**************************************************************************************/
253 /* send signals via idle calbacks - the callback are executed in the main thread */
254
255 typedef struct _ImageLoaderAreaParam ImageLoaderAreaParam;
256 struct _ImageLoaderAreaParam {
257         ImageLoader *il;
258         guint x;
259         guint y;
260         guint w;
261         guint h;
262 };
263
264
265 static gboolean image_loader_emit_area_ready_cb(gpointer data)
266 {
267         ImageLoaderAreaParam *par = data;
268         ImageLoader *il = par->il;
269         guint x, y, w, h;
270         g_mutex_lock(il->data_mutex);
271         il->area_param_list = g_list_remove(il->area_param_list, par);
272         x = par->x;
273         y = par->y;
274         w = par->w;
275         h = par->h;
276         g_free(par);
277         g_mutex_unlock(il->data_mutex);
278
279         g_signal_emit(il, signals[SIGNAL_AREA_READY], 0, x, y, w, h);
280
281         return FALSE;
282 }
283
284 static gboolean image_loader_emit_done_cb(gpointer data)
285 {
286         ImageLoader *il = data;
287         g_signal_emit(il, signals[SIGNAL_DONE], 0);
288         return FALSE;
289 }
290
291 static gboolean image_loader_emit_error_cb(gpointer data)
292 {
293         ImageLoader *il = data;
294         g_signal_emit(il, signals[SIGNAL_ERROR], 0);
295         return FALSE;
296 }
297
298 static gboolean image_loader_emit_percent_cb(gpointer data)
299 {
300         ImageLoader *il = data;
301         g_signal_emit(il, signals[SIGNAL_PERCENT], 0, image_loader_get_percent(il));
302         return FALSE;
303 }
304
305 static gboolean image_loader_emit_size_cb(gpointer data)
306 {
307         gint width, height;
308         ImageLoader *il = data;
309         g_mutex_lock(il->data_mutex);
310         width = il->actual_width;
311         height = il->actual_height;
312         g_mutex_unlock(il->data_mutex);
313         g_signal_emit(il, signals[SIGNAL_SIZE], 0, width, height);
314         return FALSE;
315 }
316
317
318 /* DONE and ERROR are emited only once, thus they can have normal priority
319    PERCENT and AREA_READY should be processed ASAP
320 */
321
322 static void image_loader_emit_done(ImageLoader *il)
323 {
324         g_idle_add_full(il->idle_priority, image_loader_emit_done_cb, il, NULL);
325 }
326
327 static void image_loader_emit_error(ImageLoader *il)
328 {
329         g_idle_add_full(il->idle_priority, image_loader_emit_error_cb, il, NULL);
330 }
331
332 static void image_loader_emit_percent(ImageLoader *il)
333 {
334         g_idle_add_full(G_PRIORITY_HIGH, image_loader_emit_percent_cb, il, NULL);
335 }
336
337 static void image_loader_emit_size(ImageLoader *il)
338 {
339         g_idle_add_full(G_PRIORITY_HIGH, image_loader_emit_size_cb, il, NULL);
340 }
341
342 static ImageLoaderAreaParam *image_loader_queue_area_ready(ImageLoader *il, GList **list, guint x, guint y, guint w, guint h)
343 {
344         if (*list)
345                 {
346                 ImageLoaderAreaParam *prev_par = (*list)->data;
347                 if (prev_par->x == x && prev_par->w == w &&
348                     prev_par->y + prev_par->h == y)
349                         {
350                         /* we can merge the notifications */
351                         prev_par->h += h;
352                         return NULL;
353                         }
354                 if (prev_par->x == x && prev_par->w == w &&
355                     y + h == prev_par->y)
356                         {
357                         /* we can merge the notifications */
358                         prev_par->h += h;
359                         prev_par->y = y;
360                         return NULL;
361                         }
362                 if (prev_par->y == y && prev_par->h == h &&
363                     prev_par->x + prev_par->w == x)
364                         {
365                         /* we can merge the notifications */
366                         prev_par->w += w;
367                         return NULL;
368                         }
369                 if (prev_par->y == y && prev_par->h == h &&
370                     x + w == prev_par->x)
371                         {
372                         /* we can merge the notifications */
373                         prev_par->w += w;
374                         prev_par->x = x;
375                         return NULL;
376                         }
377                 }
378
379         ImageLoaderAreaParam *par = g_new0(ImageLoaderAreaParam, 1);
380         par->il = il;
381         par->x = x;
382         par->y = y;
383         par->w = w;
384         par->h = h;
385
386         *list = g_list_prepend(*list, par);
387         return par;
388 }
389
390 /* this function expects that il->data_mutex is locked by caller */
391 static void image_loader_emit_area_ready(ImageLoader *il, guint x, guint y, guint w, guint h)
392 {
393         ImageLoaderAreaParam *par = image_loader_queue_area_ready(il, &il->area_param_list, x, y, w, h);
394
395         if (par)
396                 {
397                 g_idle_add_full(G_PRIORITY_HIGH, image_loader_emit_area_ready_cb, par, NULL);
398                 }
399 }
400
401 /**************************************************************************************/
402 /* the following functions may be executed in separate thread */
403
404 /* this function expects that il->data_mutex is locked by caller */
405 static void image_loader_queue_delayed_area_ready(ImageLoader *il, guint x, guint y, guint w, guint h)
406 {
407         image_loader_queue_area_ready(il, &il->area_param_delayed_list, x, y, w, h);
408 }
409
410
411
412 static gboolean image_loader_get_stopping(ImageLoader *il)
413 {
414         gboolean ret;
415         if (!il) return FALSE;
416
417         g_mutex_lock(il->data_mutex);
418         ret = il->stopping;
419         g_mutex_unlock(il->data_mutex);
420
421         return ret;
422 }
423
424
425 static void image_loader_sync_pixbuf(ImageLoader *il)
426 {
427         GdkPixbuf *pb;
428
429         g_mutex_lock(il->data_mutex);
430
431         if (!il->loader)
432                 {
433                 g_mutex_unlock(il->data_mutex);
434                 return;
435                 }
436
437         pb = il->backend.get_pixbuf(il->loader);
438
439         if (pb == il->pixbuf)
440                 {
441                 g_mutex_unlock(il->data_mutex);
442                 return;
443                 }
444
445         if (g_ascii_strcasecmp(".jps", il->fd->extension) == 0)
446                 {
447                 g_object_set_data(G_OBJECT(pb), "stereo_data", GINT_TO_POINTER(STEREO_PIXBUF_CROSS));
448                 }
449
450         if (il->pixbuf) g_object_unref(il->pixbuf);
451
452         il->pixbuf = pb;
453         if (il->pixbuf) g_object_ref(il->pixbuf);
454
455         g_mutex_unlock(il->data_mutex);
456 }
457
458 static void image_loader_area_updated_cb(gpointer loader,
459                                  guint x, guint y, guint w, guint h,
460                                  gpointer data)
461 {
462         ImageLoader *il = data;
463
464         if (!image_loader_get_pixbuf(il))
465                 {
466                 image_loader_sync_pixbuf(il);
467                 if (!image_loader_get_pixbuf(il))
468                         {
469                         log_printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
470                         }
471                 }
472
473         g_mutex_lock(il->data_mutex);
474         if (il->delay_area_ready)
475                 image_loader_queue_delayed_area_ready(il, x, y, w, h);
476         else
477                 image_loader_emit_area_ready(il, x, y, w, h);
478
479         if (il->stopping) il->backend.abort(il->loader);
480
481         g_mutex_unlock(il->data_mutex);
482 }
483
484 static void image_loader_area_prepared_cb(gpointer loader, gpointer data)
485 {
486         ImageLoader *il = data;
487         GdkPixbuf *pb;
488         guchar *pix;
489         size_t h, rs;
490
491         /* a workaround for
492            http://bugzilla.gnome.org/show_bug.cgi?id=547669
493            http://bugzilla.gnome.org/show_bug.cgi?id=589334
494         */
495         gchar *format = il->backend.get_format_name(loader);
496         if (strcmp(format, "svg") == 0 ||
497             strcmp(format, "xpm") == 0)
498                 {
499                 g_free(format);
500                 return;
501                 }
502
503         g_free(format);
504
505         pb = il->backend.get_pixbuf(loader);
506
507         h = gdk_pixbuf_get_height(pb);
508         rs = gdk_pixbuf_get_rowstride(pb);
509         pix = gdk_pixbuf_get_pixels(pb);
510
511         memset(pix, 0, rs * h); /*this should be faster than pixbuf_fill */
512
513 }
514
515 static void image_loader_size_cb(gpointer loader,
516                                  gint width, gint height, gpointer data)
517 {
518         ImageLoader *il = data;
519         gchar **mime_types;
520         gboolean scale = FALSE;
521         gint n;
522
523         g_mutex_lock(il->data_mutex);
524         il->actual_width = width;
525         il->actual_height = height;
526         if (il->requested_width < 1 || il->requested_height < 1)
527                 {
528                 g_mutex_unlock(il->data_mutex);
529                 image_loader_emit_size(il);
530                 return;
531                 }
532         g_mutex_unlock(il->data_mutex);
533
534         mime_types = il->backend.get_format_mime_types(loader);
535         n = 0;
536         while (mime_types[n])
537                 {
538                 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
539                 n++;
540                 }
541         g_strfreev(mime_types);
542
543         if (!scale)
544                 {
545                 image_loader_emit_size(il);
546                 return;
547                 }
548
549         g_mutex_lock(il->data_mutex);
550
551         gint nw, nh;
552         if (width > il->requested_width || height > il->requested_height)
553                 {
554
555                 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
556                         {
557                         nw = il->requested_width;
558                         nh = (gdouble)nw / width * height;
559                         if (nh < 1) nh = 1;
560                         }
561                 else
562                         {
563                         nh = il->requested_height;
564                         nw = (gdouble)nh / height * width;
565                         if (nw < 1) nw = 1;
566                         }
567
568                 il->actual_width = nw;
569                 il->actual_height = nh;
570                 il->backend.set_size(loader, nw, nh);
571                 il->shrunk = TRUE;
572                 }
573
574         g_mutex_unlock(il->data_mutex);
575         image_loader_emit_size(il);
576 }
577
578 static void image_loader_stop_loader(ImageLoader *il)
579 {
580         if (!il) return;
581
582         if (il->loader)
583                 {
584                 /* some loaders do not have a pixbuf till close, order is important here */
585                 il->backend.close(il->loader, il->error ? NULL : &il->error); /* we are interested in the first error only */
586                 image_loader_sync_pixbuf(il);
587                 il->backend.free(il->loader);
588                 il->loader = NULL;
589                 }
590         g_mutex_lock(il->data_mutex);
591         il->done = TRUE;
592         g_mutex_unlock(il->data_mutex);
593 }
594
595 static void image_loader_setup_loader(ImageLoader *il)
596 {
597         g_mutex_lock(il->data_mutex);
598 #ifdef HAVE_JPEG
599         if (il->bytes_total >= 2 && il->mapped_file[0] == 0xff && il->mapped_file[1] == 0xd8)
600                 {
601                 DEBUG_1("Using custom jpeg loader");
602                 image_loader_backend_set_jpeg(&il->backend);
603                 }
604         else
605 #endif
606 #ifdef HAVE_TIFF
607         if (il->bytes_total >= 10 &&
608             (memcmp(il->mapped_file, "MM\0*", 4) == 0 ||
609              memcmp(il->mapped_file, "II*\0", 4) == 0))
610                 {
611                 DEBUG_1("Using custom tiff loader");
612                 image_loader_backend_set_tiff(&il->backend);
613                 }
614         else
615 #endif
616                 image_loader_backend_set_default(&il->backend);
617
618         il->loader = il->backend.loader_new(image_loader_area_updated_cb, image_loader_size_cb, image_loader_area_prepared_cb, il);
619         g_mutex_unlock(il->data_mutex);
620 }
621
622
623 static void image_loader_done(ImageLoader *il)
624 {
625         image_loader_stop_loader(il);
626
627         image_loader_emit_done(il);
628 }
629
630 static void image_loader_error(ImageLoader *il)
631 {
632         image_loader_stop_loader(il);
633
634         DEBUG_1("pixbuf_loader reported load error for: %s", il->fd->path);
635
636         image_loader_emit_error(il);
637 }
638
639 static gboolean image_loader_continue(ImageLoader *il)
640 {
641         gint b;
642         gint c;
643
644         if (!il) return FALSE;
645
646         c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
647         while (c > 0 && !image_loader_get_stopping(il))
648                 {
649                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
650
651                 if (b == 0)
652                         {
653                         image_loader_done(il);
654                         return FALSE;
655                         }
656
657                 if (b < 0 || (b > 0 && !il->backend.write(il->loader, il->mapped_file + il->bytes_read, b, &il->error)))
658                         {
659                         image_loader_error(il);
660                         return FALSE;
661                         }
662
663                 il->bytes_read += b;
664
665                 c--;
666                 }
667
668         if (il->bytes_total > 0)
669                 {
670                 image_loader_emit_percent(il);
671                 }
672
673         return TRUE;
674 }
675
676 static gboolean image_loader_begin(ImageLoader *il)
677 {
678         gssize b;
679
680         if (il->pixbuf) return FALSE;
681
682         b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
683         if (b < 1) return FALSE;
684
685         image_loader_setup_loader(il);
686
687         g_assert(il->bytes_read == 0);
688         if (il->backend.load) {
689                 b = il->bytes_total;
690                 if (!il->backend.load(il->loader, il->mapped_file, b, &il->error))
691                         {
692                         image_loader_stop_loader(il);
693                         return FALSE;
694                         }
695         }
696         else if (!il->backend.write(il->loader, il->mapped_file, b, &il->error))
697                 {
698                 image_loader_stop_loader(il);
699                 return FALSE;
700                 }
701
702         il->bytes_read += b;
703
704         /* read until size is known */
705         while (il->loader && !il->backend.get_pixbuf(il->loader) && b > 0 && !image_loader_get_stopping(il))
706                 {
707                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
708                 if (b < 0 || (b > 0 && !il->backend.write(il->loader, il->mapped_file + il->bytes_read, b, &il->error)))
709                         {
710                         image_loader_stop_loader(il);
711                         return FALSE;
712                         }
713                 il->bytes_read += b;
714                 }
715         if (!il->pixbuf) image_loader_sync_pixbuf(il);
716
717         if (il->bytes_read == il->bytes_total || b < 1)
718                 {
719                 /* done, handle (broken) loaders that do not have pixbuf till close */
720                 image_loader_stop_loader(il);
721
722                 if (!il->pixbuf) return FALSE;
723
724                 image_loader_done(il);
725                 return TRUE;
726                 }
727
728         if (!il->pixbuf)
729                 {
730                 image_loader_stop_loader(il);
731                 return FALSE;
732                 }
733
734         return TRUE;
735 }
736
737 /**************************************************************************************/
738 /* the following functions are always executed in the main thread */
739
740
741 static gboolean image_loader_setup_source(ImageLoader *il)
742 {
743         struct stat st;
744         gchar *pathl;
745
746         if (!il || il->loader || il->mapped_file) return FALSE;
747
748         il->mapped_file = NULL;
749
750         if (il->fd)
751                 {
752                 ExifData *exif = exif_read_fd(il->fd);
753
754                 if (options->thumbnails.use_exif)
755                         il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, il->requested_width, il->requested_height);
756                 else
757                         il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, 0, 0); /* get the largest available preview image or NULL for normal images*/
758
759                 if (il->mapped_file)
760                         {
761                         il->preview = TRUE;
762                         DEBUG_1("Usable reduced size (preview) image loaded from file %s", il->fd->path);
763                         }
764                 exif_free_fd(il->fd, exif);
765                 }
766
767
768         if (!il->mapped_file)
769                 {
770                 /* normal file */
771                 gint load_fd;
772
773                 pathl = path_from_utf8(il->fd->path);
774                 load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
775                 g_free(pathl);
776                 if (load_fd == -1) return FALSE;
777
778                 if (fstat(load_fd, &st) == 0)
779                         {
780                         il->bytes_total = st.st_size;
781                         }
782                 else
783                         {
784                         close(load_fd);
785                         return FALSE;
786                         }
787
788                 il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0);
789                 close(load_fd);
790                 if (il->mapped_file == MAP_FAILED)
791                         {
792                         il->mapped_file = 0;
793                         return FALSE;
794                         }
795                 il->preview = FALSE;
796                 }
797
798         return TRUE;
799 }
800
801 static void image_loader_stop_source(ImageLoader *il)
802 {
803         if (!il) return;
804
805         if (il->mapped_file)
806                 {
807                 if (il->preview)
808                         {
809                         exif_free_preview(il->mapped_file);
810                         }
811                 else
812                         {
813                         munmap(il->mapped_file, il->bytes_total);
814                         }
815                 il->mapped_file = NULL;
816                 }
817 }
818
819 static void image_loader_stop(ImageLoader *il)
820 {
821         if (!il) return;
822
823         if (il->idle_id)
824                 {
825                 g_source_remove(il->idle_id);
826                 il->idle_id = 0;
827                 }
828
829         if (il->thread)
830                 {
831                 /* stop loader in the other thread */
832                 g_mutex_lock(il->data_mutex);
833                 il->stopping = TRUE;
834                 while (!il->can_destroy) g_cond_wait(il->can_destroy_cond, il->data_mutex);
835                 g_mutex_unlock(il->data_mutex);
836                 }
837
838         image_loader_stop_loader(il);
839         image_loader_stop_source(il);
840
841 }
842
843 void image_loader_delay_area_ready(ImageLoader *il, gboolean enable)
844 {
845         g_mutex_lock(il->data_mutex);
846         il->delay_area_ready = enable;
847         if (!enable)
848                 {
849                 /* send delayed */
850                 GList *list, *work;
851                 list = g_list_reverse(il->area_param_delayed_list);
852                 il->area_param_delayed_list = NULL;
853                 g_mutex_unlock(il->data_mutex);
854
855                 work = list;
856
857                 while (work)
858                         {
859                         ImageLoaderAreaParam *par = work->data;
860                         work = work->next;
861
862                         g_signal_emit(il, signals[SIGNAL_AREA_READY], 0, par->x, par->y, par->w, par->h);
863                         g_free(par);
864                         }
865                 g_list_free(list);
866                 }
867         else
868                 {
869                 /* just unlock */
870                 g_mutex_unlock(il->data_mutex);
871                 }
872 }
873
874
875 /**************************************************************************************/
876 /* execution via idle calls */
877
878 static gboolean image_loader_idle_cb(gpointer data)
879 {
880         gboolean ret = FALSE;
881         ImageLoader *il = data;
882
883         if (il->idle_id)
884                 {
885                 ret = image_loader_continue(il);
886                 }
887
888         if (!ret)
889                 {
890                 image_loader_stop_source(il);
891                 }
892
893         return ret;
894 }
895
896
897 static gboolean image_loader_start_idle(ImageLoader *il)
898 {
899         gboolean ret;
900
901         if (!il) return FALSE;
902
903         if (!il->fd) return FALSE;
904
905         if (!image_loader_setup_source(il)) return FALSE;
906
907         ret = image_loader_begin(il);
908
909         if (ret && !il->done) il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
910         return ret;
911 }
912
913 /**************************************************************************************/
914 /* execution via thread */
915
916 #ifdef HAVE_GTHREAD
917 static GThreadPool *image_loader_thread_pool = NULL;
918
919 static GCond *image_loader_prio_cond = NULL;
920 static GMutex *image_loader_prio_mutex = NULL;
921 static gint image_loader_prio_num = 0;
922
923
924 static void image_loader_thread_enter_high(void)
925 {
926         g_mutex_lock(image_loader_prio_mutex);
927         image_loader_prio_num++;
928         g_mutex_unlock(image_loader_prio_mutex);
929 }
930
931 static void image_loader_thread_leave_high(void)
932 {
933         g_mutex_lock(image_loader_prio_mutex);
934         image_loader_prio_num--;
935         if (image_loader_prio_num == 0) g_cond_broadcast(image_loader_prio_cond); /* wake up all low prio threads */
936         g_mutex_unlock(image_loader_prio_mutex);
937 }
938
939 static void image_loader_thread_wait_high(void)
940 {
941         g_mutex_lock(image_loader_prio_mutex);
942         while (image_loader_prio_num)
943                 {
944                 g_cond_wait(image_loader_prio_cond, image_loader_prio_mutex);
945                 }
946
947         g_mutex_unlock(image_loader_prio_mutex);
948 }
949
950
951 static void image_loader_thread_run(gpointer data, gpointer user_data)
952 {
953         ImageLoader *il = data;
954         gboolean cont;
955         gboolean err;
956
957         if (il->idle_priority > G_PRIORITY_DEFAULT_IDLE)
958                 {
959                 /* low prio, wait untill high prio tasks finishes */
960                 image_loader_thread_wait_high();
961                 }
962         else
963                 {
964                 /* high prio */
965                 image_loader_thread_enter_high();
966                 }
967
968         err = !image_loader_begin(il);
969
970         if (err)
971                 {
972                 /*
973                 loader failed, we have to send signal
974                 (idle mode returns the image_loader_begin return value directly)
975                 (success is always reported indirectly from image_loader_begin)
976                 */
977                 image_loader_emit_error(il);
978                 }
979
980         cont = !err;
981
982         while (cont && !image_loader_get_is_done(il) && !image_loader_get_stopping(il))
983                 {
984                 if (il->idle_priority > G_PRIORITY_DEFAULT_IDLE)
985                         {
986                         /* low prio, wait untill high prio tasks finishes */
987                         image_loader_thread_wait_high();
988                         }
989                 cont = image_loader_continue(il);
990                 }
991         image_loader_stop_loader(il);
992
993         if (il->idle_priority <= G_PRIORITY_DEFAULT_IDLE)
994                 {
995                 /* high prio */
996                 image_loader_thread_leave_high();
997                 }
998
999         g_mutex_lock(il->data_mutex);
1000         il->can_destroy = TRUE;
1001         g_cond_signal(il->can_destroy_cond);
1002         g_mutex_unlock(il->data_mutex);
1003
1004 }
1005
1006
1007 static gboolean image_loader_start_thread(ImageLoader *il)
1008 {
1009         if (!il) return FALSE;
1010
1011         if (!il->fd) return FALSE;
1012
1013         il->thread = TRUE;
1014
1015         if (!image_loader_setup_source(il)) return FALSE;
1016
1017         if (!image_loader_thread_pool)
1018                 {
1019                 image_loader_thread_pool = g_thread_pool_new(image_loader_thread_run, NULL, -1, FALSE, NULL);
1020 #if GLIB_CHECK_VERSION(2,32,0)
1021                 if (!image_loader_prio_cond) image_loader_prio_cond = g_new(GCond, 1);
1022                 g_cond_init(image_loader_prio_cond);
1023                 if (!image_loader_prio_mutex) image_loader_prio_mutex = g_new(GMutex, 1);
1024                 g_mutex_init(image_loader_prio_mutex);
1025 #else
1026                 image_loader_prio_cond = g_cond_new();
1027                 image_loader_prio_mutex = g_mutex_new();
1028 #endif
1029                 }
1030
1031         il->can_destroy = FALSE; /* ImageLoader can't be freed until image_loader_thread_run finishes */
1032
1033         g_thread_pool_push(image_loader_thread_pool, il, NULL);
1034         DEBUG_1("Thread pool num threads: %d", g_thread_pool_get_num_threads(image_loader_thread_pool));
1035
1036         return TRUE;
1037 }
1038 #endif /* HAVE_GTHREAD */
1039
1040
1041 /**************************************************************************************/
1042 /* public interface */
1043
1044
1045 gboolean image_loader_start(ImageLoader *il)
1046 {
1047         if (!il) return FALSE;
1048
1049         if (!il->fd) return FALSE;
1050
1051 #ifdef HAVE_GTHREAD
1052         return image_loader_start_thread(il);
1053 #else
1054         return image_loader_start_idle(il);
1055 #endif
1056 }
1057
1058
1059 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
1060 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
1061 {
1062         GdkPixbuf *ret;
1063         if (!il) return NULL;
1064
1065         g_mutex_lock(il->data_mutex);
1066         ret = il->pixbuf;
1067         g_mutex_unlock(il->data_mutex);
1068         return ret;
1069 }
1070
1071 gchar *image_loader_get_format(ImageLoader *il)
1072 {
1073         gchar **mimev;
1074         gchar *mime;
1075
1076         if (!il || !il->loader) return NULL;
1077
1078         mimev = il->backend.get_format_mime_types(il->loader);
1079         if (!mimev) return NULL;
1080
1081         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
1082         mime = g_strdup(mimev[0]);
1083         g_strfreev(mimev);
1084
1085         return mime;
1086 }
1087
1088 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
1089 {
1090         if (!il) return;
1091
1092         g_mutex_lock(il->data_mutex);
1093         il->requested_width = width;
1094         il->requested_height = height;
1095         g_mutex_unlock(il->data_mutex);
1096 }
1097
1098 void image_loader_set_buffer_size(ImageLoader *il, guint count)
1099 {
1100         if (!il) return;
1101
1102         g_mutex_lock(il->data_mutex);
1103         il->idle_read_loop_count = count ? count : 1;
1104         g_mutex_unlock(il->data_mutex);
1105 }
1106
1107 void image_loader_set_priority(ImageLoader *il, gint priority)
1108 {
1109         if (!il) return;
1110
1111         if (il->thread) return; /* can't change prio if the thread already runs */
1112         il->idle_priority = priority;
1113 }
1114
1115
1116 gdouble image_loader_get_percent(ImageLoader *il)
1117 {
1118         gdouble ret;
1119         if (!il) return 0.0;
1120
1121         g_mutex_lock(il->data_mutex);
1122         if (il->bytes_total == 0)
1123                 {
1124                 ret = 0.0;
1125                 }
1126         else
1127                 {
1128                 ret = (gdouble)il->bytes_read / il->bytes_total;
1129                 }
1130         g_mutex_unlock(il->data_mutex);
1131         return ret;
1132 }
1133
1134 gboolean image_loader_get_is_done(ImageLoader *il)
1135 {
1136         gboolean ret;
1137         if (!il) return FALSE;
1138
1139         g_mutex_lock(il->data_mutex);
1140         ret = il->done;
1141         g_mutex_unlock(il->data_mutex);
1142
1143         return ret;
1144 }
1145
1146 FileData *image_loader_get_fd(ImageLoader *il)
1147 {
1148         FileData *ret;
1149         if (!il) return NULL;
1150
1151         g_mutex_lock(il->data_mutex);
1152         ret = il->fd;
1153         g_mutex_unlock(il->data_mutex);
1154
1155         return ret;
1156 }
1157
1158 gboolean image_loader_get_shrunk(ImageLoader *il)
1159 {
1160         gboolean ret;
1161         if (!il) return FALSE;
1162
1163         g_mutex_lock(il->data_mutex);
1164         ret = il->shrunk;
1165         g_mutex_unlock(il->data_mutex);
1166         return ret;
1167 }
1168
1169 const gchar *image_loader_get_error(ImageLoader *il)
1170 {
1171         const gchar *ret = NULL;
1172         if (!il) return NULL;
1173         g_mutex_lock(il->data_mutex);
1174         if (il->error) ret = il->error->message;
1175         g_mutex_unlock(il->data_mutex);
1176         return ret;
1177 }
1178
1179
1180 /* FIXME - this can be rather slow and blocks until the size is known */
1181 gboolean image_load_dimensions(FileData *fd, gint *width, gint *height)
1182 {
1183         ImageLoader *il;
1184         gboolean success;
1185
1186         il = image_loader_new(fd);
1187
1188         success = image_loader_start_idle(il);
1189
1190         if (success && il->pixbuf)
1191                 {
1192                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
1193                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
1194                 }
1195         else
1196                 {
1197                 if (width) *width = -1;
1198                 if (height) *height = -1;
1199                 }
1200
1201         image_loader_free(il);
1202
1203         return success;
1204 }
1205 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */