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