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