Mon Jun 13 17:31:46 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 > 0 &&
221             format_raw_img_exif_offsets_fd(il->load_fd, il->path, buf, b, &offset, NULL))
222                 {
223                 if (debug) printf("Raw file %s contains embedded image\n", il->path);
224
225                 b = read(il->load_fd, &buf, sizeof(buf));
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, buf, 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, &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 < 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(il->path);
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 ImageLoader *image_loader_new(const gchar *path)
307 {
308         ImageLoader *il;
309
310         if (!path) return NULL;
311
312         il = g_new0(ImageLoader, 1);
313         if (path) il->path = g_strdup(path);
314         il->pixbuf = NULL;
315         il->idle_id = -1;
316         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
317         il->done = FALSE;
318         il->loader = NULL;
319         il->load_fd = -1;
320
321         il->bytes_read = 0;
322         il->bytes_total = 0;
323
324         il->idle_done_id = -1;
325
326         il->buffer_size = IMAGE_LOADER_BUFFER_DEFAULT_COUNT;
327
328         il->requested_width = 0;
329         il->requested_height = 0;
330         il->shrunk = FALSE;
331
332         return il;
333 }
334
335 void image_loader_free(ImageLoader *il)
336 {
337         if (!il) return;
338
339         image_loader_stop(il);
340         if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
341         if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
342         g_free(il->path);
343         g_free(il);
344 }
345
346 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
347 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
348 {
349         if (!il) return NULL;
350
351         return il->pixbuf;
352 }
353
354 gchar *image_loader_get_format(ImageLoader *il)
355 {
356         GdkPixbufFormat *format;
357         gchar **mimev;
358         gchar *mime;
359
360         if (!il || !il->loader) return NULL;
361
362         format = gdk_pixbuf_loader_get_format(il->loader);
363         if (!format) return NULL;
364
365         mimev = gdk_pixbuf_format_get_mime_types(format);
366         if (!mimev) return NULL;
367
368         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
369         mime = g_strdup(mimev[0]);
370         g_strfreev(mimev);
371
372         return mime;
373 }
374
375 void image_loader_set_area_ready_func(ImageLoader *il,
376                                       void (*func_area_ready)(ImageLoader *, guint, guint, guint, guint, gpointer),
377                                       gpointer data_area_ready)
378 {
379         if (!il) return;
380
381         il->func_area_ready = func_area_ready;
382         il->data_area_ready = data_area_ready;
383 }
384
385 void image_loader_set_error_func(ImageLoader *il,
386                                  void (*func_error)(ImageLoader *, gpointer),
387                                  gpointer data_error)
388 {
389         if (!il) return;
390
391         il->func_error = func_error;
392         il->data_error = data_error;
393 }
394
395 void image_loader_set_percent_func(ImageLoader *il,
396                                    void (*func_percent)(ImageLoader *, gdouble, gpointer),
397                                    gpointer data_percent)
398 {
399         if (!il) return;
400
401         il->func_percent = func_percent;
402         il->data_percent = data_percent;
403 }
404
405 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
406 {
407         if (!il) return;
408
409         il->requested_width = width;
410         il->requested_height = height;
411 }
412
413 void image_loader_set_buffer_size(ImageLoader *il, guint size)
414 {
415         if (!il) return;
416
417         il->buffer_size = size ? size : 1;
418 }
419
420 void image_loader_set_priority(ImageLoader *il, gint priority)
421 {
422         if (!il) return;
423
424         il->idle_priority = priority;
425 }
426
427 gint image_loader_start(ImageLoader *il, void (*func_done)(ImageLoader *, gpointer), gpointer data_done)
428 {
429         if (!il) return FALSE;
430
431         if (!il->path) return FALSE;
432
433         il->func_done = func_done;
434         il->data_done = data_done;
435
436         return image_loader_setup(il);
437 }
438
439 gdouble image_loader_get_percent(ImageLoader *il)
440 {
441         if (!il || il->bytes_total == 0) return 0.0;
442
443         return (gdouble)il->bytes_read / il->bytes_total;
444 }
445
446 gint image_loader_get_is_done(ImageLoader *il)
447 {
448         if (!il) return FALSE;
449
450         return il->done;
451 }
452
453 gint image_load_dimensions(const gchar *path, gint *width, gint *height)
454 {
455         ImageLoader *il;
456         gint success;
457
458         il = image_loader_new(path);
459
460         success = image_loader_start(il, NULL, NULL);
461
462         if (success && il->pixbuf)
463                 {
464                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
465                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
466                 }
467         else
468                 {
469                 if (width) *width = -1;
470                 if (height) *height = -1;
471                 }
472
473         image_loader_free(il);
474
475         return success;
476 }
477