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