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