Thu May 26 13:57:19 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             format_raw_img_exif_offsets_fd(il->load_fd, 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                 il->bytes_read += b + offset;
237
238                 if (b < sizeof(buf))
239                         {
240                         /* end of file already */
241
242                         image_loader_stop(il);
243
244                         if (!il->pixbuf) return FALSE;
245
246                         image_loader_done_delay(il);
247                         return TRUE;
248                         }
249                 else
250                         {
251                         /* larger file */
252
253                         /* read until size is known */
254                         while(il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
255                                 {
256                                 b = read(il->load_fd, &buf, sizeof(buf));
257                                 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, buf, b, NULL)))
258                                         {
259                                         image_loader_stop(il);
260                                         return FALSE;
261                                         }
262                                 il->bytes_read += b;
263                                 }
264                         if (!il->pixbuf) image_loader_sync_pixbuf(il);
265
266                         if (il->bytes_read == il->bytes_total || b < sizeof(buf))
267                                 {
268                                 /* done, handle (broken) loaders that do not have pixbuf till close */
269                                 image_loader_stop(il);
270
271                                 if (!il->pixbuf) return FALSE;
272
273                                 image_loader_done_delay(il);
274                                 return TRUE;
275                                 }
276
277                         if (!il->pixbuf)
278                                 {
279                                 image_loader_stop(il);
280                                 return FALSE;
281                                 }
282
283                         /* finally, progressive loading :) */
284                         il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
285                         return TRUE;
286                         }
287                 }
288         else
289                 {
290                 image_loader_stop(il);
291                 return FALSE;
292                 }
293
294         return TRUE;
295 }
296
297 static gint image_loader_setup(ImageLoader *il)
298 {
299         struct stat st;
300         gchar *pathl;
301
302         if (!il || il->load_fd != -1 || il->loader) return FALSE;
303
304         pathl = path_from_utf8(il->path);
305         il->load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
306         g_free(pathl);
307         if (il->load_fd == -1) return FALSE;
308
309         if (fstat(il->load_fd, &st) == 0)
310                 {
311                 il->bytes_total = st.st_size;
312                 }
313
314         il->loader = gdk_pixbuf_loader_new();
315         g_signal_connect(G_OBJECT(il->loader), "area_updated",
316                          G_CALLBACK(image_loader_area_cb), il);
317         g_signal_connect(G_OBJECT(il->loader), "size_prepared",
318                          G_CALLBACK(image_loader_size_cb), il);
319
320         il->shrunk = FALSE;
321
322         return image_loader_begin(il);
323 }
324
325 ImageLoader *image_loader_new(const gchar *path)
326 {
327         ImageLoader *il;
328
329         if (!path) return NULL;
330
331         il = g_new0(ImageLoader, 1);
332         if (path) il->path = g_strdup(path);
333         il->pixbuf = NULL;
334         il->idle_id = -1;
335         il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
336         il->done = FALSE;
337         il->loader = NULL;
338         il->load_fd = -1;
339
340         il->bytes_read = 0;
341         il->bytes_total = 0;
342
343         il->idle_done_id = -1;
344
345         il->buffer_size = IMAGE_LOADER_BUFFER_DEFAULT_COUNT;
346
347         il->requested_width = 0;
348         il->requested_height = 0;
349         il->shrunk = FALSE;
350
351         return il;
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         g_free(il->path);
362         g_free(il);
363 }
364
365 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
366 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
367 {
368         if (!il) return NULL;
369
370         return il->pixbuf;
371 }
372
373 gchar *image_loader_get_format(ImageLoader *il)
374 {
375         GdkPixbufFormat *format;
376         gchar **mimev;
377         gchar *mime;
378
379         if (!il || !il->loader) return NULL;
380
381         format = gdk_pixbuf_loader_get_format(il->loader);
382         if (!format) return NULL;
383
384         mimev = gdk_pixbuf_format_get_mime_types(format);
385         if (!mimev) return NULL;
386
387         /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
388         mime = g_strdup(mimev[0]);
389         g_strfreev(mimev);
390
391         return mime;
392 }
393
394 void image_loader_set_area_ready_func(ImageLoader *il,
395                                       void (*func_area_ready)(ImageLoader *, guint, guint, guint, guint, gpointer),
396                                       gpointer data_area_ready)
397 {
398         if (!il) return;
399
400         il->func_area_ready = func_area_ready;
401         il->data_area_ready = data_area_ready;
402 }
403
404 void image_loader_set_error_func(ImageLoader *il,
405                                  void (*func_error)(ImageLoader *, gpointer),
406                                  gpointer data_error)
407 {
408         if (!il) return;
409
410         il->func_error = func_error;
411         il->data_error = data_error;
412 }
413
414 void image_loader_set_percent_func(ImageLoader *il,
415                                    void (*func_percent)(ImageLoader *, gdouble, gpointer),
416                                    gpointer data_percent)
417 {
418         if (!il) return;
419
420         il->func_percent = func_percent;
421         il->data_percent = data_percent;
422 }
423
424 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
425 {
426         if (!il) return;
427
428         il->requested_width = width;
429         il->requested_height = height;
430 }
431
432 void image_loader_set_buffer_size(ImageLoader *il, guint size)
433 {
434         if (!il) return;
435
436         il->buffer_size = size ? size : 1;
437 }
438
439 void image_loader_set_priority(ImageLoader *il, gint priority)
440 {
441         if (!il) return;
442
443         il->idle_priority = priority;
444 }
445
446 gint image_loader_start(ImageLoader *il, void (*func_done)(ImageLoader *, gpointer), gpointer data_done)
447 {
448         if (!il) return FALSE;
449
450         if (!il->path) return FALSE;
451
452         il->func_done = func_done;
453         il->data_done = data_done;
454
455         return image_loader_setup(il);
456 }
457
458 gdouble image_loader_get_percent(ImageLoader *il)
459 {
460         if (!il || il->bytes_total == 0) return 0.0;
461
462         return (gdouble)il->bytes_read / il->bytes_total;
463 }
464
465 gint image_loader_get_is_done(ImageLoader *il)
466 {
467         if (!il) return FALSE;
468
469         return il->done;
470 }
471
472 gint image_load_dimensions(const gchar *path, gint *width, gint *height)
473 {
474         ImageLoader *il;
475         gint success;
476
477         il = image_loader_new(path);
478
479         success = image_loader_start(il, NULL, NULL);
480
481         if (success && il->pixbuf)
482                 {
483                 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
484                 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
485                 }
486         else
487                 {
488                 if (width) *width = -1;
489                 if (height) *height = -1;
490                 }
491
492         image_loader_free(il);
493
494         return success;
495 }
496