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