Add two new options to control image read buffer at runtime.
[geeqie.git] / src / image-load.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "main.h"
14 #include "image-load.h"
15 #include "filelist.h"
16
17 #include "exif.h"
18 #include "ui_fileops.h"
19
20 #include <fcntl.h>
21
22
23 static const gchar *image_loader_path(ImageLoader *il)
24 {
25         if (il->fd)
26                 return il->fd->path;
27         return il->path;
28 }
29
30 static void image_loader_sync_pixbuf(ImageLoader *il)
31 {
32         GdkPixbuf *pb;
33
34         if (!il->loader) return;
35
36         pb = gdk_pixbuf_loader_get_pixbuf(il->loader);
37
38         if (pb == il->pixbuf) return;
39
40         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
41         il->pixbuf = pb;
42         if (il->pixbuf) gdk_pixbuf_ref(il->pixbuf);
43 }
44
45 static void image_loader_area_cb(GdkPixbufLoader *loader,
46                                  guint x, guint y, guint w, guint h,
47                                  gpointer data)
48 {
49         ImageLoader *il = data;
50
51         if (il->func_area_ready)
52                 {
53                 if (!il->pixbuf)
54                         {
55                         image_loader_sync_pixbuf(il);
56                         if (!il->pixbuf)
57                                 {
58                                 printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
59                                 }
60                         }
61                 il->func_area_ready(il, x, y, w, h, il->data_area_ready);
62                 }
63 }
64
65 static void image_loader_size_cb(GdkPixbufLoader *loader,
66                                  gint width, gint height, gpointer data)
67 {
68         ImageLoader *il = data;
69         GdkPixbufFormat *format;
70         gchar **mime_types;
71         gint scale = FALSE;
72         gint n;
73
74         if (il->requested_width < 1 || il->requested_height < 1) return;
75
76         format = gdk_pixbuf_loader_get_format(loader);
77         if (!format) return;
78
79         mime_types = gdk_pixbuf_format_get_mime_types(format);
80         n = 0;
81         while (mime_types[n])
82                 {
83                 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
84                 n++;
85                 }
86         g_strfreev(mime_types);
87         
88         if (!scale) return;
89
90         if (width > il->requested_width || height > il->requested_height)
91                 {
92                 gint nw, nh;
93
94                 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
95                         {
96                         nw = il->requested_width;
97                         nh = (gdouble)nw / width * height;
98                         if (nh < 1) nh = 1;
99                         }
100                 else
101                         {
102                         nh = il->requested_height;
103                         nw = (gdouble)nh / height * width;
104                         if (nw < 1) nw = 1;
105                         }
106                 
107                 gdk_pixbuf_loader_set_size(loader, nw, nh);
108                 il->shrunk = TRUE;
109                 }
110 }
111
112 static void image_loader_stop(ImageLoader *il)
113 {
114         if (!il) return;
115
116         if (il->idle_id != -1)
117                 {
118                 g_source_remove(il->idle_id);
119                 il->idle_id = -1;
120                 }
121
122         if (il->loader)
123                 {
124                 /* some loaders do not have a pixbuf till close, order is important here */
125                 gdk_pixbuf_loader_close(il->loader, NULL);
126                 image_loader_sync_pixbuf(il);
127                 g_object_unref(G_OBJECT(il->loader));
128                 il->loader = NULL;
129                 }
130
131         if (il->load_fd != -1)
132                 {
133                 close(il->load_fd);
134                 il->load_fd = -1;
135                 }
136
137         il->done = TRUE;
138 }
139
140 static void image_loader_done(ImageLoader *il)
141 {
142         image_loader_stop(il);
143
144         if (il->func_done) il->func_done(il, il->data_done);
145 }
146
147 static gint image_loader_done_delay_cb(gpointer data)
148 {
149         ImageLoader *il = data;
150
151         il->idle_done_id = -1;
152         image_loader_done(il);
153         return FALSE;
154 }
155
156 static void image_loader_done_delay(ImageLoader *il)
157 {
158         if (il->idle_done_id == -1) il->idle_done_id = g_idle_add_full(il->idle_priority,
159                                                                        image_loader_done_delay_cb, il, NULL);
160 }
161
162 static void image_loader_error(ImageLoader *il)
163 {
164         image_loader_stop(il);
165
166         if (debug) printf("pixbuf_loader reported load error for: %s\n", image_loader_path(il));
167
168         if (il->func_error) il->func_error(il, il->data_error);
169 }
170
171 static gint image_loader_idle_cb(gpointer data)
172 {
173         ImageLoader *il = data;
174         gint b;
175         gint c;
176
177         if (!il) return FALSE;
178
179         if (il->idle_id == -1) return FALSE;
180
181         c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
182         while (c > 0)
183                 {
184                 b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
185
186                 if (b == 0)
187                         {
188                         image_loader_done(il);
189                         return FALSE;
190                         }
191
192                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL)))
193                         {
194                         image_loader_error(il);
195                         return FALSE;
196                         }
197
198                 il->bytes_read += b;
199
200                 c--;
201                 }
202
203         if (il->func_percent && il->bytes_total > 0)
204                 {
205                 il->func_percent(il, (gdouble)il->bytes_read / il->bytes_total, il->data_percent);
206                 }
207
208         return TRUE;
209 }
210
211 static gint image_loader_begin(ImageLoader *il)
212 {
213         int b;
214         unsigned int offset = 0;
215
216         if (!il->loader || il->pixbuf) return FALSE;
217         
218         b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
219
220         if (b > 0 &&
221             format_raw_img_exif_offsets_fd(il->load_fd, image_loader_path(il), il->read_buffer, b, &offset, NULL))
222                 {
223                 if (debug) printf("Raw file %s contains embedded image\n", image_loader_path(il));
224
225                 b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
226                 }
227
228         if (b < 1)
229                 {
230                 image_loader_stop(il);
231                 return FALSE;
232                 }
233
234         if (!gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL))
235                 {
236                 image_loader_stop(il);
237                 return FALSE;
238                 }
239
240         il->bytes_read += b + offset;
241
242         /* read until size is known */
243         while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
244                 {
245                 b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
246                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL)))
247                         {
248                         image_loader_stop(il);
249                         return FALSE;
250                         }
251                 il->bytes_read += b;
252                 }
253         if (!il->pixbuf) image_loader_sync_pixbuf(il);
254
255         if (il->bytes_read == il->bytes_total || b < 1)
256                 {
257                 /* done, handle (broken) loaders that do not have pixbuf till close */
258                 image_loader_stop(il);
259
260                 if (!il->pixbuf) return FALSE;
261
262                 image_loader_done_delay(il);
263                 return TRUE;
264                 }
265
266         if (!il->pixbuf)
267                 {
268                 image_loader_stop(il);
269                 return FALSE;
270                 }
271
272         /* finally, progressive loading :) */
273         il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
274
275         return TRUE;
276 }
277
278 static gint image_loader_setup(ImageLoader *il)
279 {
280         struct stat st;
281         gchar *pathl;
282
283         if (!il || il->load_fd != -1 || il->loader) return FALSE;
284
285         pathl = path_from_utf8(image_loader_path(il));
286         il->load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
287         g_free(pathl);
288         if (il->load_fd == -1) return FALSE;
289
290         if (fstat(il->load_fd, &st) == 0)
291                 {
292                 il->bytes_total = st.st_size;
293                 }
294
295         il->loader = gdk_pixbuf_loader_new();
296         g_signal_connect(G_OBJECT(il->loader), "area_updated",
297                          G_CALLBACK(image_loader_area_cb), il);
298         g_signal_connect(G_OBJECT(il->loader), "size_prepared",
299                          G_CALLBACK(image_loader_size_cb), il);
300
301         il->shrunk = FALSE;
302
303         return image_loader_begin(il);
304 }
305
306 static ImageLoader *image_loader_new_real(FileData *fd, const gchar *path)
307 {
308         ImageLoader *il;
309
310         if (!fd && !path) return NULL;
311
312         il = g_new0(ImageLoader, 1);
313         if (fd) il->fd = file_data_ref(fd);
314         if (path) il->path = g_strdup(path);
315         il->pixbuf = NULL;
316         il->idle_id = -1;
317         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
318         il->done = FALSE;
319         il->loader = NULL;
320         il->load_fd = -1;
321
322         il->bytes_read = 0;
323         il->bytes_total = 0;
324
325         il->idle_done_id = -1;
326
327         il->idle_read_loop_count = options->image.idle_read_loop_count;
328         il->read_buffer_size = options->image.read_buffer_size;
329         il->read_buffer = g_new(guchar, il->read_buffer_size);
330
331         il->requested_width = 0;
332         il->requested_height = 0;
333         il->shrunk = FALSE;
334         if (debug) printf("new image loader %p, bufsize=%u idle_loop=%u\n", il, il->read_buffer_size, il->idle_read_loop_count);
335         return il;
336 }
337
338 ImageLoader *image_loader_new(FileData *fd)
339 {
340         return image_loader_new_real(fd, NULL);
341 }
342
343 ImageLoader *image_loader_new_from_path(const gchar *path)
344 {
345         return image_loader_new_real(NULL, path);
346 }
347
348 void image_loader_free(ImageLoader *il)
349 {
350         if (!il) return;
351
352         image_loader_stop(il);
353         if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
354         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
355         if (il->fd) file_data_unref(il->fd);
356         if (il->path) g_free(il->path);
357         if (il->read_buffer) g_free(il->read_buffer);
358         if (debug) printf("freeing image loader %p bytes_read=%d\n", il, il->bytes_read);
359         g_free(il);
360 }
361
362 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
363 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
364 {
365         if (!il) return NULL;
366
367         return il->pixbuf;
368 }
369
370 gchar *image_loader_get_format(ImageLoader *il)
371 {
372         GdkPixbufFormat *format;
373         gchar **mimev;
374         gchar *mime;
375
376         if (!il || !il->loader) return NULL;
377
378         format = gdk_pixbuf_loader_get_format(il->loader);
379         if (!format) return NULL;
380
381         mimev = gdk_pixbuf_format_get_mime_types(format);
382         if (!mimev) return NULL;
383
384         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
385         mime = g_strdup(mimev[0]);
386         g_strfreev(mimev);
387
388         return mime;
389 }
390
391 void image_loader_set_area_ready_func(ImageLoader *il,
392                                       void (*func_area_ready)(ImageLoader *, guint, guint, guint, guint, gpointer),
393                                       gpointer data_area_ready)
394 {
395         if (!il) return;
396
397         il->func_area_ready = func_area_ready;
398         il->data_area_ready = data_area_ready;
399 }
400
401 void image_loader_set_error_func(ImageLoader *il,
402                                  void (*func_error)(ImageLoader *, gpointer),
403                                  gpointer data_error)
404 {
405         if (!il) return;
406
407         il->func_error = func_error;
408         il->data_error = data_error;
409 }
410
411 void image_loader_set_percent_func(ImageLoader *il,
412                                    void (*func_percent)(ImageLoader *, gdouble, gpointer),
413                                    gpointer data_percent)
414 {
415         if (!il) return;
416
417         il->func_percent = func_percent;
418         il->data_percent = data_percent;
419 }
420
421 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
422 {
423         if (!il) return;
424
425         il->requested_width = width;
426         il->requested_height = height;
427 }
428
429 void image_loader_set_buffer_size(ImageLoader *il, guint count)
430 {
431         if (!il) return;
432
433         il->idle_read_loop_count = count ? count : 1;
434 }
435
436 void image_loader_set_priority(ImageLoader *il, gint priority)
437 {
438         if (!il) return;
439
440         il->idle_priority = priority;
441 }
442
443 gint image_loader_start(ImageLoader *il, void (*func_done)(ImageLoader *, gpointer), gpointer data_done)
444 {
445         if (!il) return FALSE;
446
447         if (!image_loader_path(il)) return FALSE;
448
449         il->func_done = func_done;
450         il->data_done = data_done;
451
452         return image_loader_setup(il);
453 }
454
455 gdouble image_loader_get_percent(ImageLoader *il)
456 {
457         if (!il || il->bytes_total == 0) return 0.0;
458
459         return (gdouble)il->bytes_read / il->bytes_total;
460 }
461
462 gint image_loader_get_is_done(ImageLoader *il)
463 {
464         if (!il) return FALSE;
465
466         return il->done;
467 }
468
469 gint image_load_dimensions(FileData *fd, gint *width, gint *height)
470 {
471         ImageLoader *il;
472         gint success;
473
474         il = image_loader_new(fd);
475
476         success = image_loader_start(il, NULL, NULL);
477
478         if (success && il->pixbuf)
479                 {
480                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
481                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
482                 }
483         else
484                 {
485                 if (width) *width = -1;
486                 if (height) *height = -1;
487                 }
488
489         image_loader_free(il);
490
491         return success;
492 }
493