prepared image loader code for threads
[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                         };
61                 type = g_type_register_static (G_TYPE_OBJECT, "ImageLoaderType", &info, 0);
62                 }
63         return type;
64 }
65
66 static void image_loader_init (GTypeInstance *instance, gpointer g_class)
67 {
68         ImageLoader *il = (ImageLoader *)instance;
69
70         il->pixbuf = NULL;
71         il->idle_id = -1;
72         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
73         il->done = FALSE;
74         il->loader = NULL;
75
76         il->bytes_read = 0;
77         il->bytes_total = 0;
78
79         il->idle_done_id = -1;
80
81         il->idle_read_loop_count = options->image.idle_read_loop_count;
82         il->read_buffer_size = options->image.read_buffer_size;
83         il->mapped_file = NULL;
84
85         il->requested_width = 0;
86         il->requested_height = 0;
87         il->shrunk = FALSE;
88         DEBUG_1("new image loader %p, bufsize=%u idle_loop=%u", il, il->read_buffer_size, il->idle_read_loop_count);
89 }
90
91 static void image_loader_class_init (ImageLoaderClass *class)
92 {
93         GObjectClass *gobject_class = G_OBJECT_CLASS (class);
94
95 //      gobject_class->set_property = image_loader_set_property;
96 //      gobject_class->get_property = image_loader_get_property;
97
98         gobject_class->finalize = image_loader_finalize;
99
100
101         signals[SIGNAL_AREA_READY] =
102                 g_signal_new("area_ready",
103                              G_OBJECT_CLASS_TYPE(gobject_class),
104                              G_SIGNAL_RUN_LAST,
105                              G_STRUCT_OFFSET(ImageLoaderClass, area_ready),
106                              NULL, NULL,
107                              gq_marshal_VOID__INT_INT_INT_INT,
108                              G_TYPE_NONE, 4,
109                              G_TYPE_INT,
110                              G_TYPE_INT,
111                              G_TYPE_INT,
112                              G_TYPE_INT);
113
114         signals[SIGNAL_ERROR] =
115                 g_signal_new("error",
116                              G_OBJECT_CLASS_TYPE(gobject_class),
117                              G_SIGNAL_RUN_LAST,
118                              G_STRUCT_OFFSET(ImageLoaderClass, error),
119                              NULL, NULL,
120                              g_cclosure_marshal_VOID__VOID,
121                              G_TYPE_NONE, 1,
122                              GDK_TYPE_EVENT);
123
124         signals[SIGNAL_DONE] =
125                 g_signal_new("done",
126                              G_OBJECT_CLASS_TYPE(gobject_class),
127                              G_SIGNAL_RUN_LAST,
128                              G_STRUCT_OFFSET(ImageLoaderClass, done),
129                              NULL, NULL,
130                              g_cclosure_marshal_VOID__VOID,
131                              G_TYPE_NONE, 0);
132
133         signals[SIGNAL_PERCENT] =
134                 g_signal_new("percent",
135                              G_OBJECT_CLASS_TYPE(gobject_class),
136                              G_SIGNAL_RUN_LAST,
137                              G_STRUCT_OFFSET(ImageLoaderClass, percent),
138                              NULL, NULL,
139                              g_cclosure_marshal_VOID__DOUBLE,
140                              G_TYPE_NONE, 1,
141                              G_TYPE_DOUBLE);
142
143 }
144
145 static void image_loader_finalize(GObject *object)
146 {
147         ImageLoader *il = (ImageLoader *)object;
148
149         image_loader_stop(il);
150
151         DEBUG_1("freeing image loader %p bytes_read=%d", il, il->bytes_read);
152
153         if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
154
155         while (g_source_remove_by_user_data(il)) 
156                 {
157                 DEBUG_1("pending signals detected");
158                 }
159         
160         while (il->area_param_list) 
161                 {
162                 DEBUG_1("pending area_ready signals detected");
163                 while (g_source_remove_by_user_data(il->area_param_list->data)) {}
164                 g_free(il->area_param_list->data);
165                 il->area_param_list = g_list_delete_link(il->area_param_list, il->area_param_list);
166                 }
167
168         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
169         file_data_unref(il->fd);
170 }
171
172 void image_loader_free(ImageLoader *il)
173 {
174         if (!il) return;
175         g_object_unref(G_OBJECT(il));
176 }
177
178
179 ImageLoader *image_loader_new(FileData *fd)
180 {
181         ImageLoader *il;
182
183         if (!fd) return NULL;
184
185         il = (ImageLoader *) g_object_new(TYPE_IMAGE_LOADER, NULL);
186         
187         il->fd = file_data_ref(fd);
188         
189         return il;
190 }
191
192 /**************************************************************************************/
193 /* send signals via idle calbacks - the callback are executed in the main thread */
194
195 typedef struct _ImageLoaderAreaParam ImageLoaderAreaParam;
196 struct _ImageLoaderAreaParam {
197         ImageLoader *il;
198         guint x;
199         guint y;
200         guint w;
201         guint h;
202 };
203
204
205 static gint image_loader_emit_area_ready_cb(gpointer data)
206 {
207         ImageLoaderAreaParam *par = data;
208         ImageLoader *il = par->il;
209         g_signal_emit(il, signals[SIGNAL_AREA_READY], 0, par->x, par->y, par->w, par->h);
210         il->area_param_list = g_list_remove(il->area_param_list, par);
211         g_free(par);
212         
213         return FALSE;
214 }
215
216 static gint image_loader_emit_done_cb(gpointer data)
217 {
218         ImageLoader *il = data;
219         g_signal_emit(il, signals[SIGNAL_DONE], 0);
220         return FALSE;
221 }
222
223 static gint image_loader_emit_error_cb(gpointer data)
224 {
225         ImageLoader *il = data;
226         g_signal_emit(il, signals[SIGNAL_ERROR], 0);
227         return FALSE;
228 }
229
230 static gint image_loader_emit_percent_cb(gpointer data)
231 {
232         ImageLoader *il = data;
233         g_signal_emit(il, signals[SIGNAL_PERCENT], 0, (gdouble)il->bytes_read / il->bytes_total);
234         return FALSE;
235 }
236
237 /* DONE and ERROR are emited only once, thus they can have normal priority
238    PERCENT and AREA_READY should be processed ASAP
239 */
240
241 static void image_loader_emit_done(ImageLoader *il)
242 {
243         g_idle_add_full(il->idle_priority, image_loader_emit_done_cb, il, NULL);
244 }
245
246 static void image_loader_emit_error(ImageLoader *il)
247 {
248         g_idle_add_full(il->idle_priority, image_loader_emit_error_cb, il, NULL);
249 }
250
251 static void image_loader_emit_percent(ImageLoader *il)
252 {
253         g_idle_add_full(G_PRIORITY_HIGH, image_loader_emit_percent_cb, il, NULL);
254 }
255
256 static void image_loader_emit_area_ready(ImageLoader *il, guint x, guint y, guint w, guint h)
257 {
258         ImageLoaderAreaParam *par = g_new0(ImageLoaderAreaParam, 1);
259         par->il = il;
260         par->x = x;
261         par->y = y;
262         par->w = w;
263         par->h = h;
264         
265         il->area_param_list = g_list_prepend(il->area_param_list, par);
266         g_idle_add_full(G_PRIORITY_HIGH, image_loader_emit_area_ready_cb, par, NULL);
267 }
268
269 /**************************************************************************************/
270 /* the following functions may be executed in separate thread */
271
272 static void image_loader_sync_pixbuf(ImageLoader *il)
273 {
274         GdkPixbuf *pb;
275
276         if (!il->loader) return;
277
278         pb = gdk_pixbuf_loader_get_pixbuf(il->loader);
279
280         if (pb == il->pixbuf) return;
281
282         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
283         il->pixbuf = pb;
284         if (il->pixbuf) gdk_pixbuf_ref(il->pixbuf);
285 }
286
287 static void image_loader_area_updated_cb(GdkPixbufLoader *loader,
288                                  guint x, guint y, guint w, guint h,
289                                  gpointer data)
290 {
291         ImageLoader *il = data;
292
293         if (!il->pixbuf)
294                 {
295                 image_loader_sync_pixbuf(il);
296                 if (!il->pixbuf)
297                         {
298                         log_printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
299                         }
300                 }
301         image_loader_emit_area_ready(il, x, y, w, h);
302 }
303
304 static void image_loader_area_prepared_cb(GdkPixbufLoader *loader, gpointer data)
305 {
306         GdkPixbuf *pb;
307         guchar *pix;
308         size_t h, rs;
309         
310         /* a workaround for http://bugzilla.gnome.org/show_bug.cgi?id=547669 */
311         gchar *format = gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(loader));
312         if (strcmp(format, "svg") == 0)
313                 {
314                 g_free(format);
315                 return;
316                 }
317         
318         g_free(format);
319
320         pb = gdk_pixbuf_loader_get_pixbuf(loader);
321         
322         h = gdk_pixbuf_get_height(pb);
323         rs = gdk_pixbuf_get_rowstride(pb);
324         pix = gdk_pixbuf_get_pixels(pb);
325         
326         memset(pix, 0, rs * h); /*this should be faster than pixbuf_fill */
327
328 }
329
330 static void image_loader_size_cb(GdkPixbufLoader *loader,
331                                  gint width, gint height, gpointer data)
332 {
333         ImageLoader *il = data;
334         GdkPixbufFormat *format;
335         gchar **mime_types;
336         gint scale = FALSE;
337         gint n;
338
339         if (il->requested_width < 1 || il->requested_height < 1) return;
340
341         format = gdk_pixbuf_loader_get_format(loader);
342         if (!format) return;
343
344         mime_types = gdk_pixbuf_format_get_mime_types(format);
345         n = 0;
346         while (mime_types[n])
347                 {
348                 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
349                 n++;
350                 }
351         g_strfreev(mime_types);
352
353         if (!scale) return;
354
355         if (width > il->requested_width || height > il->requested_height)
356                 {
357                 gint nw, nh;
358
359                 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
360                         {
361                         nw = il->requested_width;
362                         nh = (gdouble)nw / width * height;
363                         if (nh < 1) nh = 1;
364                         }
365                 else
366                         {
367                         nh = il->requested_height;
368                         nw = (gdouble)nh / height * width;
369                         if (nw < 1) nw = 1;
370                         }
371
372                 gdk_pixbuf_loader_set_size(loader, nw, nh);
373                 il->shrunk = TRUE;
374                 }
375 }
376
377 static void image_loader_stop_loader(ImageLoader *il)
378 {
379         if (!il) return;
380
381         if (il->loader)
382                 {
383                 /* some loaders do not have a pixbuf till close, order is important here */
384                 gdk_pixbuf_loader_close(il->loader, NULL);
385                 image_loader_sync_pixbuf(il);
386                 g_object_unref(G_OBJECT(il->loader));
387                 il->loader = NULL;
388                 }
389
390         il->done = TRUE;
391 }
392
393 static void image_loader_setup_loader(ImageLoader *il)
394 {
395         il->loader = gdk_pixbuf_loader_new();
396         g_signal_connect(G_OBJECT(il->loader), "area_updated",
397                          G_CALLBACK(image_loader_area_updated_cb), il);
398         g_signal_connect(G_OBJECT(il->loader), "size_prepared",
399                          G_CALLBACK(image_loader_size_cb), il);
400         g_signal_connect(G_OBJECT(il->loader), "area_prepared",
401                          G_CALLBACK(image_loader_area_prepared_cb), il);
402 }
403
404
405 static void image_loader_done(ImageLoader *il)
406 {
407         image_loader_stop_loader(il);
408
409         image_loader_emit_done(il);
410 }
411
412 static void image_loader_error(ImageLoader *il)
413 {
414         image_loader_stop_loader(il);
415
416         DEBUG_1("pixbuf_loader reported load error for: %s", il->fd->path);
417
418         image_loader_emit_error(il);
419 }
420
421 static gint image_loader_continue(ImageLoader *il)
422 {
423         gint b;
424         gint c;
425
426         if (!il) return FALSE;
427
428         c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
429         while (c > 0)
430                 {
431                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
432
433                 if (b == 0)
434                         {
435                         image_loader_done(il);
436                         return FALSE;
437                         }
438
439                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
440                         {
441                         image_loader_error(il);
442                         return FALSE;
443                         }
444
445                 il->bytes_read += b;
446
447                 c--;
448                 }
449
450         if (il->bytes_total > 0)
451                 {
452                 image_loader_emit_percent(il);
453                 }
454
455         return TRUE;
456 }
457
458 static gint image_loader_begin(ImageLoader *il)
459 {
460         gint b;
461         
462         if (il->pixbuf) return FALSE;
463
464         b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
465         if (b < 1) return FALSE;
466
467         image_loader_setup_loader(il);
468
469         if (!gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL))
470                 {
471                 image_loader_stop_loader(il);
472                 return FALSE;
473                 }
474
475         il->bytes_read += b;
476
477         /* read until size is known */
478         while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
479                 {
480                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
481                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
482                         {
483                         image_loader_stop_loader(il);
484                         return FALSE;
485                         }
486                 il->bytes_read += b;
487                 }
488         if (!il->pixbuf) image_loader_sync_pixbuf(il);
489
490         if (il->bytes_read == il->bytes_total || b < 1)
491                 {
492                 /* done, handle (broken) loaders that do not have pixbuf till close */
493                 image_loader_stop_loader(il);
494
495                 if (!il->pixbuf) return FALSE;
496
497                 image_loader_done(il);
498                 return TRUE;
499                 }
500
501         if (!il->pixbuf)
502                 {
503                 image_loader_stop_loader(il);
504                 return FALSE;
505                 }
506
507         return TRUE;
508 }
509
510 /**************************************************************************************/
511 /* the following functions are always executed in the main thread */
512
513
514 static gint image_loader_setup_source(ImageLoader *il)
515 {
516         struct stat st;
517         gchar *pathl;
518
519         if (!il || il->loader || il->mapped_file) return FALSE;
520
521         il->mapped_file = NULL;
522
523         if (il->fd)
524                 {
525                 ExifData *exif = exif_read_fd(il->fd);
526
527                 il->mapped_file = exif_get_preview(exif, &il->bytes_total);
528                 
529                 if (il->mapped_file)
530                         {
531                         il->preview = TRUE;
532                         DEBUG_1("Raw file %s contains embedded image", il->fd->path);
533                         }
534                 exif_free_fd(il->fd, exif);
535                 }
536
537         
538         if (!il->mapped_file)
539                 {
540                 /* normal file */
541                 gint load_fd;
542         
543                 pathl = path_from_utf8(il->fd->path);
544                 load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
545                 g_free(pathl);
546                 if (load_fd == -1) return FALSE;
547
548                 if (fstat(load_fd, &st) == 0)
549                         {
550                         il->bytes_total = st.st_size;
551                         }
552                 else
553                         {
554                         close(load_fd);
555                         return FALSE;
556                         }
557                 
558                 il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0);
559                 close(load_fd);
560                 if (il->mapped_file == MAP_FAILED)
561                         {
562                         il->mapped_file = 0;
563                         return FALSE;
564                         }
565                 il->preview = FALSE;
566                 }
567                 
568         return TRUE;
569 }
570
571 static void image_loader_stop_source(ImageLoader *il)
572 {
573         if (!il) return;
574         
575         if (il->mapped_file)
576                 {
577                 if (il->preview)
578                         {
579                         exif_free_preview(il->mapped_file);
580                         }
581                 else
582                         {
583                         munmap(il->mapped_file, il->bytes_total);
584                         }
585                 il->mapped_file = NULL;
586                 }
587 }
588
589
590 /*
591 static gint image_loader_setup(ImageLoader *il)
592 {
593         if (!image_loader_setup_source(il)) return FALSE;
594         
595         return image_loader_begin(il);
596 }
597 */
598
599 static void image_loader_stop(ImageLoader *il)
600 {
601         if (!il) return;
602
603         if (il->idle_id != -1)
604                 {
605                 g_source_remove(il->idle_id);
606                 il->idle_id = -1;
607                 }
608                 
609
610         image_loader_stop_loader(il);
611         image_loader_stop_source(il);
612         
613 }
614
615 /**************************************************************************************/
616 /* execution via idle calls */
617
618 static gint image_loader_idle_cb(gpointer data)
619 {
620         gint ret = FALSE;
621         ImageLoader *il = data;
622         if (il->idle_id != -1)
623                 {
624                 ret = image_loader_continue(il);
625                 }
626         if (!ret)
627                 {
628                 image_loader_stop_source(il);
629                 }
630         return ret;
631 }
632
633
634 gint image_loader_start_idle(ImageLoader *il)
635 {
636         gint ret;
637         if (!il) return FALSE;
638
639         if (!il->fd) return FALSE;
640
641         if (!image_loader_setup_source(il)) return FALSE;
642         
643         ret = image_loader_begin(il);
644
645         if (ret && !il->done) il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
646         return ret;
647 }
648
649 /**************************************************************************************/
650 /* public interface */
651
652
653 gint image_loader_start(ImageLoader *il)
654 {
655         if (!il) return FALSE;
656
657         if (!il->fd) return FALSE;
658
659         return image_loader_start_idle(il);
660 }
661
662
663 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
664 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
665 {
666         if (!il) return NULL;
667
668         return il->pixbuf;
669 }
670
671 gchar *image_loader_get_format(ImageLoader *il)
672 {
673         GdkPixbufFormat *format;
674         gchar **mimev;
675         gchar *mime;
676
677         if (!il || !il->loader) return NULL;
678
679         format = gdk_pixbuf_loader_get_format(il->loader);
680         if (!format) return NULL;
681
682         mimev = gdk_pixbuf_format_get_mime_types(format);
683         if (!mimev) return NULL;
684
685         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
686         mime = g_strdup(mimev[0]);
687         g_strfreev(mimev);
688
689         return mime;
690 }
691
692 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
693 {
694         if (!il) return;
695
696         il->requested_width = width;
697         il->requested_height = height;
698 }
699
700 void image_loader_set_buffer_size(ImageLoader *il, guint count)
701 {
702         if (!il) return;
703
704         il->idle_read_loop_count = count ? count : 1;
705 }
706
707 void image_loader_set_priority(ImageLoader *il, gint priority)
708 {
709         if (!il) return;
710
711         il->idle_priority = priority;
712 }
713
714
715 gdouble image_loader_get_percent(ImageLoader *il)
716 {
717         if (!il || il->bytes_total == 0) return 0.0;
718
719         return (gdouble)il->bytes_read / il->bytes_total;
720 }
721
722 gint image_loader_get_is_done(ImageLoader *il)
723 {
724         if (!il) return FALSE;
725
726         return il->done;
727 }
728
729 FileData *image_loader_get_fd(ImageLoader *il)
730 {
731         if (!il) return NULL;
732
733         return il->fd;
734         
735 }
736
737 gint image_loader_get_shrunk(ImageLoader *il)
738 {
739         if (!il) return FALSE;
740
741         return il->shrunk;
742         
743 }
744
745
746 /* FIXME - this can be rather slow and blocks until the size is known */
747 gint image_load_dimensions(FileData *fd, gint *width, gint *height)
748 {
749         ImageLoader *il;
750         gint success;
751
752         il = image_loader_new(fd);
753
754         success = image_loader_start_idle(il);
755
756         if (success && il->pixbuf)
757                 {
758                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
759                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
760                 }
761         else
762                 {
763                 if (width) *width = -1;
764                 if (height) *height = -1;
765                 }
766
767         image_loader_free(il);
768
769         return success;
770 }