converted image loader to a GObject and use signals for notification
[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 enum {
26         SIGNAL_AREA_READY = 0,
27         SIGNAL_ERROR,
28         SIGNAL_DONE,
29         SIGNAL_PERCENT,
30         SIGNAL_COUNT
31 };
32
33 static guint signals[SIGNAL_COUNT] = { 0 };
34
35 static void image_loader_init (GTypeInstance *instance, gpointer g_class);
36 static void image_loader_class_init (ImageLoaderClass *class);
37 static void image_loader_finalize(GObject *object);
38 static void image_loader_stop(ImageLoader *il);
39
40 GType image_loader_get_type (void)
41 {
42         static GType type = 0;
43         if (type == 0) 
44                 {
45                 static const GTypeInfo info = {
46                         sizeof (ImageLoaderClass),
47                         NULL,   /* base_init */
48                         NULL,   /* base_finalize */
49                         (GClassInitFunc)image_loader_class_init, /* class_init */
50                         NULL,   /* class_finalize */
51                         NULL,   /* class_data */
52                         sizeof (ImageLoader),
53                         0,      /* n_preallocs */
54                         (GInstanceInitFunc)image_loader_init, /* instance_init */
55                         };
56                 type = g_type_register_static (G_TYPE_OBJECT, "ImageLoaderType", &info, 0);
57                 }
58         return type;
59 }
60
61 static void image_loader_init (GTypeInstance *instance, gpointer g_class)
62 {
63         ImageLoader *il = (ImageLoader *)instance;
64
65         il->pixbuf = NULL;
66         il->idle_id = -1;
67         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
68         il->done = FALSE;
69         il->loader = NULL;
70
71         il->bytes_read = 0;
72         il->bytes_total = 0;
73
74         il->idle_done_id = -1;
75
76         il->idle_read_loop_count = options->image.idle_read_loop_count;
77         il->read_buffer_size = options->image.read_buffer_size;
78         il->mapped_file = NULL;
79
80         il->requested_width = 0;
81         il->requested_height = 0;
82         il->shrunk = FALSE;
83         DEBUG_1("new image loader %p, bufsize=%u idle_loop=%u", il, il->read_buffer_size, il->idle_read_loop_count);
84 }
85
86 static void image_loader_class_init (ImageLoaderClass *class)
87 {
88         GObjectClass *gobject_class = G_OBJECT_CLASS (class);
89
90 //      gobject_class->set_property = image_loader_set_property;
91 //      gobject_class->get_property = image_loader_get_property;
92
93         gobject_class->finalize = image_loader_finalize;
94
95
96         signals[SIGNAL_AREA_READY] =
97                 g_signal_new("area_ready",
98                              G_OBJECT_CLASS_TYPE(gobject_class),
99                              G_SIGNAL_RUN_LAST,
100                              G_STRUCT_OFFSET(ImageLoaderClass, area_ready),
101                              NULL, NULL,
102                              gq_marshal_VOID__INT_INT_INT_INT,
103                              G_TYPE_NONE, 4,
104                              G_TYPE_INT,
105                              G_TYPE_INT,
106                              G_TYPE_INT,
107                              G_TYPE_INT);
108
109         signals[SIGNAL_ERROR] =
110                 g_signal_new("error",
111                              G_OBJECT_CLASS_TYPE(gobject_class),
112                              G_SIGNAL_RUN_LAST,
113                              G_STRUCT_OFFSET(ImageLoaderClass, error),
114                              NULL, NULL,
115                              g_cclosure_marshal_VOID__VOID,
116                              G_TYPE_NONE, 1,
117                              GDK_TYPE_EVENT);
118
119         signals[SIGNAL_DONE] =
120                 g_signal_new("done",
121                              G_OBJECT_CLASS_TYPE(gobject_class),
122                              G_SIGNAL_RUN_LAST,
123                              G_STRUCT_OFFSET(ImageLoaderClass, done),
124                              NULL, NULL,
125                              g_cclosure_marshal_VOID__VOID,
126                              G_TYPE_NONE, 0);
127
128         signals[SIGNAL_PERCENT] =
129                 g_signal_new("percent",
130                              G_OBJECT_CLASS_TYPE(gobject_class),
131                              G_SIGNAL_RUN_LAST,
132                              G_STRUCT_OFFSET(ImageLoaderClass, percent),
133                              NULL, NULL,
134                              g_cclosure_marshal_VOID__DOUBLE,
135                              G_TYPE_NONE, 1,
136                              G_TYPE_DOUBLE);
137
138 }
139
140 static void image_loader_finalize(GObject *object)
141 {
142         ImageLoader *il = (ImageLoader *)object;
143
144         image_loader_stop(il);
145         if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
146         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
147         file_data_unref(il->fd);
148         DEBUG_1("freeing image loader %p bytes_read=%d", il, il->bytes_read);
149 }
150
151 void image_loader_free(ImageLoader *il)
152 {
153         if (!il) return;
154         g_object_unref(G_OBJECT(il));
155 }
156
157
158 ImageLoader *image_loader_new(FileData *fd)
159 {
160         ImageLoader *il;
161
162         if (!fd) return NULL;
163
164         il = (ImageLoader *) g_object_new(TYPE_IMAGE_LOADER, NULL);
165         
166         il->fd = file_data_ref(fd);
167         
168         return il;
169 }
170
171 static void image_loader_sync_pixbuf(ImageLoader *il)
172 {
173         GdkPixbuf *pb;
174
175         if (!il->loader) return;
176
177         pb = gdk_pixbuf_loader_get_pixbuf(il->loader);
178
179         if (pb == il->pixbuf) return;
180
181         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
182         il->pixbuf = pb;
183         if (il->pixbuf) gdk_pixbuf_ref(il->pixbuf);
184 }
185
186 static void image_loader_area_updated_cb(GdkPixbufLoader *loader,
187                                  guint x, guint y, guint w, guint h,
188                                  gpointer data)
189 {
190         ImageLoader *il = data;
191
192         if (!il->pixbuf)
193                 {
194                 image_loader_sync_pixbuf(il);
195                 if (!il->pixbuf)
196                         {
197                         log_printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
198                         }
199                 }
200         g_signal_emit(il, signals[SIGNAL_AREA_READY], 0, x, y, w, h);
201 }
202
203 static void image_loader_area_prepared_cb(GdkPixbufLoader *loader, gpointer data)
204 {
205         GdkPixbuf *pb;
206         guchar *pix;
207         size_t h, rs;
208         
209         /* a workaround for http://bugzilla.gnome.org/show_bug.cgi?id=547669 */
210         gchar *format = gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(loader));
211         if (strcmp(format, "svg") == 0)
212                 {
213                 g_free(format);
214                 return;
215                 }
216         
217         g_free(format);
218
219         pb = gdk_pixbuf_loader_get_pixbuf(loader);
220         
221         h = gdk_pixbuf_get_height(pb);
222         rs = gdk_pixbuf_get_rowstride(pb);
223         pix = gdk_pixbuf_get_pixels(pb);
224         
225         memset(pix, 0, rs * h); /*this should be faster than pixbuf_fill */
226
227 }
228
229 static void image_loader_size_cb(GdkPixbufLoader *loader,
230                                  gint width, gint height, gpointer data)
231 {
232         ImageLoader *il = data;
233         GdkPixbufFormat *format;
234         gchar **mime_types;
235         gint scale = FALSE;
236         gint n;
237
238         if (il->requested_width < 1 || il->requested_height < 1) return;
239
240         format = gdk_pixbuf_loader_get_format(loader);
241         if (!format) return;
242
243         mime_types = gdk_pixbuf_format_get_mime_types(format);
244         n = 0;
245         while (mime_types[n])
246                 {
247                 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
248                 n++;
249                 }
250         g_strfreev(mime_types);
251
252         if (!scale) return;
253
254         if (width > il->requested_width || height > il->requested_height)
255                 {
256                 gint nw, nh;
257
258                 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
259                         {
260                         nw = il->requested_width;
261                         nh = (gdouble)nw / width * height;
262                         if (nh < 1) nh = 1;
263                         }
264                 else
265                         {
266                         nh = il->requested_height;
267                         nw = (gdouble)nh / height * width;
268                         if (nw < 1) nw = 1;
269                         }
270
271                 gdk_pixbuf_loader_set_size(loader, nw, nh);
272                 il->shrunk = TRUE;
273                 }
274 }
275
276 static void image_loader_stop(ImageLoader *il)
277 {
278         if (!il) return;
279
280         if (il->idle_id != -1)
281                 {
282                 g_source_remove(il->idle_id);
283                 il->idle_id = -1;
284                 }
285
286         if (il->loader)
287                 {
288                 /* some loaders do not have a pixbuf till close, order is important here */
289                 gdk_pixbuf_loader_close(il->loader, NULL);
290                 image_loader_sync_pixbuf(il);
291                 g_object_unref(G_OBJECT(il->loader));
292                 il->loader = NULL;
293                 }
294
295         if (il->mapped_file)
296                 {
297                 if (il->preview)
298                         {
299                         exif_free_preview(il->mapped_file);
300                         }
301                 else
302                         {
303                         munmap(il->mapped_file, il->bytes_total);
304                         }
305                 il->mapped_file = NULL;
306                 }
307
308         il->done = TRUE;
309 }
310
311 static void image_loader_done(ImageLoader *il)
312 {
313         image_loader_stop(il);
314
315         g_signal_emit(il, signals[SIGNAL_DONE], 0);
316 }
317
318 static gint image_loader_done_delay_cb(gpointer data)
319 {
320         ImageLoader *il = data;
321
322         il->idle_done_id = -1;
323         image_loader_done(il);
324         return FALSE;
325 }
326
327 static void image_loader_done_delay(ImageLoader *il)
328 {
329         if (il->idle_done_id == -1) il->idle_done_id = g_idle_add_full(il->idle_priority,
330                                                                        image_loader_done_delay_cb, il, NULL);
331 }
332
333 static void image_loader_error(ImageLoader *il)
334 {
335         image_loader_stop(il);
336
337         DEBUG_1("pixbuf_loader reported load error for: %s", il->fd->path);
338
339         g_signal_emit(il, signals[SIGNAL_ERROR], 0);
340 }
341
342 static gint image_loader_idle_cb(gpointer data)
343 {
344         ImageLoader *il = data;
345         gint b;
346         gint c;
347
348         if (!il) return FALSE;
349
350         if (il->idle_id == -1) return FALSE;
351
352         c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
353         while (c > 0)
354                 {
355                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
356
357                 if (b == 0)
358                         {
359                         image_loader_done(il);
360                         return FALSE;
361                         }
362
363                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
364                         {
365                         image_loader_error(il);
366                         return FALSE;
367                         }
368
369                 il->bytes_read += b;
370
371                 c--;
372                 }
373
374         if (il->bytes_total > 0)
375                 {
376                 g_signal_emit(il, signals[SIGNAL_PERCENT], 0, (gdouble)il->bytes_read / il->bytes_total);
377                 }
378
379         return TRUE;
380 }
381
382 static gint image_loader_begin(ImageLoader *il)
383 {
384         gint b;
385
386         if (!il->loader || il->pixbuf) return FALSE;
387
388         b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
389
390         if (b < 1)
391                 {
392                 image_loader_stop(il);
393                 return FALSE;
394                 }
395
396         if (!gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL))
397                 {
398                 image_loader_stop(il);
399                 return FALSE;
400                 }
401
402         il->bytes_read += b;
403
404         /* read until size is known */
405         while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
406                 {
407                 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
408                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
409                         {
410                         image_loader_stop(il);
411                         return FALSE;
412                         }
413                 il->bytes_read += b;
414                 }
415         if (!il->pixbuf) image_loader_sync_pixbuf(il);
416
417         if (il->bytes_read == il->bytes_total || b < 1)
418                 {
419                 /* done, handle (broken) loaders that do not have pixbuf till close */
420                 image_loader_stop(il);
421
422                 if (!il->pixbuf) return FALSE;
423
424                 image_loader_done_delay(il);
425                 return TRUE;
426                 }
427
428         if (!il->pixbuf)
429                 {
430                 image_loader_stop(il);
431                 return FALSE;
432                 }
433
434         /* finally, progressive loading :) */
435         il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
436
437         return TRUE;
438 }
439
440 static gint image_loader_setup(ImageLoader *il)
441 {
442         struct stat st;
443         gchar *pathl;
444
445         if (!il || il->loader || il->mapped_file) return FALSE;
446
447         il->mapped_file = NULL;
448
449         if (il->fd)
450                 {
451                 ExifData *exif = exif_read_fd(il->fd);
452
453                 il->mapped_file = exif_get_preview(exif, &il->bytes_total);
454                 
455                 if (il->mapped_file)
456                         {
457                         il->preview = TRUE;
458                         DEBUG_1("Raw file %s contains embedded image", il->fd->path);
459                         }
460                 exif_free_fd(il->fd, exif);
461                 }
462
463         
464         if (!il->mapped_file)
465                 {
466                 /* normal file */
467                 gint load_fd;
468         
469                 pathl = path_from_utf8(il->fd->path);
470                 load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
471                 g_free(pathl);
472                 if (load_fd == -1) return FALSE;
473
474                 if (fstat(load_fd, &st) == 0)
475                         {
476                         il->bytes_total = st.st_size;
477                         }
478                 else
479                         {
480                         close(load_fd);
481                         return FALSE;
482                         }
483                 
484                 il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0);
485                 close(load_fd);
486                 if (il->mapped_file == MAP_FAILED)
487                         {
488                         il->mapped_file = 0;
489                         return FALSE;
490                         }
491                 il->preview = FALSE;
492                 }
493
494
495         il->loader = gdk_pixbuf_loader_new();
496         g_signal_connect(G_OBJECT(il->loader), "area_updated",
497                          G_CALLBACK(image_loader_area_updated_cb), il);
498         g_signal_connect(G_OBJECT(il->loader), "size_prepared",
499                          G_CALLBACK(image_loader_size_cb), il);
500         g_signal_connect(G_OBJECT(il->loader), "area_prepared",
501                          G_CALLBACK(image_loader_area_prepared_cb), il);
502
503         il->shrunk = FALSE;
504
505         return image_loader_begin(il);
506 }
507
508
509 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
510 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
511 {
512         if (!il) return NULL;
513
514         return il->pixbuf;
515 }
516
517 gchar *image_loader_get_format(ImageLoader *il)
518 {
519         GdkPixbufFormat *format;
520         gchar **mimev;
521         gchar *mime;
522
523         if (!il || !il->loader) return NULL;
524
525         format = gdk_pixbuf_loader_get_format(il->loader);
526         if (!format) return NULL;
527
528         mimev = gdk_pixbuf_format_get_mime_types(format);
529         if (!mimev) return NULL;
530
531         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
532         mime = g_strdup(mimev[0]);
533         g_strfreev(mimev);
534
535         return mime;
536 }
537
538 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
539 {
540         if (!il) return;
541
542         il->requested_width = width;
543         il->requested_height = height;
544 }
545
546 void image_loader_set_buffer_size(ImageLoader *il, guint count)
547 {
548         if (!il) return;
549
550         il->idle_read_loop_count = count ? count : 1;
551 }
552
553 void image_loader_set_priority(ImageLoader *il, gint priority)
554 {
555         if (!il) return;
556
557         il->idle_priority = priority;
558 }
559
560 gint image_loader_start(ImageLoader *il)
561 {
562         if (!il) return FALSE;
563
564         if (!il->fd) return FALSE;
565
566         return image_loader_setup(il);
567 }
568
569 gdouble image_loader_get_percent(ImageLoader *il)
570 {
571         if (!il || il->bytes_total == 0) return 0.0;
572
573         return (gdouble)il->bytes_read / il->bytes_total;
574 }
575
576 gint image_loader_get_is_done(ImageLoader *il)
577 {
578         if (!il) return FALSE;
579
580         return il->done;
581 }
582
583 FileData *image_loader_get_fd(ImageLoader *il)
584 {
585         if (!il) return NULL;
586
587         return il->fd;
588         
589 }
590
591 gint image_loader_get_shrunk(ImageLoader *il)
592 {
593         if (!il) return FALSE;
594
595         return il->shrunk;
596         
597 }
598
599
600 gint image_load_dimensions(FileData *fd, gint *width, gint *height)
601 {
602         ImageLoader *il;
603         gint success;
604
605         il = image_loader_new(fd);
606
607         success = image_loader_start(il);
608
609         if (success && il->pixbuf)
610                 {
611                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
612                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
613                 }
614         else
615                 {
616                 if (width) *width = -1;
617                 if (height) *height = -1;
618                 }
619
620         image_loader_free(il);
621
622         return success;
623 }