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