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