4 * Copyright (C) 2008 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
15 #include "image-load.h"
19 #include "ui_fileops.h"
20 #include "gq-marshal.h"
26 SIGNAL_AREA_READY = 0,
33 static guint signals[SIGNAL_COUNT] = { 0 };
35 static void image_loader_init (GTypeInstance *instance, gpointer g_class);
36 static void image_loader_class_init (ImageLoaderClass *class);
37 static void image_loader_finalize(GObject *object);
38 static void image_loader_stop(ImageLoader *il);
40 GType image_loader_get_type (void)
42 static GType type = 0;
45 static const GTypeInfo info = {
46 sizeof (ImageLoaderClass),
48 NULL, /* base_finalize */
49 (GClassInitFunc)image_loader_class_init, /* class_init */
50 NULL, /* class_finalize */
51 NULL, /* class_data */
54 (GInstanceInitFunc)image_loader_init, /* instance_init */
56 type = g_type_register_static (G_TYPE_OBJECT, "ImageLoaderType", &info, 0);
61 static void image_loader_init (GTypeInstance *instance, gpointer g_class)
63 ImageLoader *il = (ImageLoader *)instance;
67 il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
74 il->idle_done_id = -1;
76 il->idle_read_loop_count = options->image.idle_read_loop_count;
77 il->read_buffer_size = options->image.read_buffer_size;
78 il->mapped_file = NULL;
80 il->requested_width = 0;
81 il->requested_height = 0;
83 DEBUG_1("new image loader %p, bufsize=%u idle_loop=%u", il, il->read_buffer_size, il->idle_read_loop_count);
86 static void image_loader_class_init (ImageLoaderClass *class)
88 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
90 // gobject_class->set_property = image_loader_set_property;
91 // gobject_class->get_property = image_loader_get_property;
93 gobject_class->finalize = image_loader_finalize;
96 signals[SIGNAL_AREA_READY] =
97 g_signal_new("area_ready",
98 G_OBJECT_CLASS_TYPE(gobject_class),
100 G_STRUCT_OFFSET(ImageLoaderClass, area_ready),
102 gq_marshal_VOID__INT_INT_INT_INT,
109 signals[SIGNAL_ERROR] =
110 g_signal_new("error",
111 G_OBJECT_CLASS_TYPE(gobject_class),
113 G_STRUCT_OFFSET(ImageLoaderClass, error),
115 g_cclosure_marshal_VOID__VOID,
119 signals[SIGNAL_DONE] =
121 G_OBJECT_CLASS_TYPE(gobject_class),
123 G_STRUCT_OFFSET(ImageLoaderClass, done),
125 g_cclosure_marshal_VOID__VOID,
128 signals[SIGNAL_PERCENT] =
129 g_signal_new("percent",
130 G_OBJECT_CLASS_TYPE(gobject_class),
132 G_STRUCT_OFFSET(ImageLoaderClass, percent),
134 g_cclosure_marshal_VOID__DOUBLE,
140 static void image_loader_finalize(GObject *object)
142 ImageLoader *il = (ImageLoader *)object;
144 image_loader_stop(il);
145 if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
146 if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
147 file_data_unref(il->fd);
148 DEBUG_1("freeing image loader %p bytes_read=%d", il, il->bytes_read);
151 void image_loader_free(ImageLoader *il)
154 g_object_unref(G_OBJECT(il));
158 ImageLoader *image_loader_new(FileData *fd)
162 if (!fd) return NULL;
164 il = (ImageLoader *) g_object_new(TYPE_IMAGE_LOADER, NULL);
166 il->fd = file_data_ref(fd);
171 static void image_loader_sync_pixbuf(ImageLoader *il)
175 if (!il->loader) return;
177 pb = gdk_pixbuf_loader_get_pixbuf(il->loader);
179 if (pb == il->pixbuf) return;
181 if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
183 if (il->pixbuf) gdk_pixbuf_ref(il->pixbuf);
186 static void image_loader_area_updated_cb(GdkPixbufLoader *loader,
187 guint x, guint y, guint w, guint h,
190 ImageLoader *il = data;
194 image_loader_sync_pixbuf(il);
197 log_printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
200 g_signal_emit(il, signals[SIGNAL_AREA_READY], 0, x, y, w, h);
203 static void image_loader_area_prepared_cb(GdkPixbufLoader *loader, gpointer data)
209 /* a workaround for http://bugzilla.gnome.org/show_bug.cgi?id=547669 */
210 gchar *format = gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(loader));
211 if (strcmp(format, "svg") == 0)
219 pb = gdk_pixbuf_loader_get_pixbuf(loader);
221 h = gdk_pixbuf_get_height(pb);
222 rs = gdk_pixbuf_get_rowstride(pb);
223 pix = gdk_pixbuf_get_pixels(pb);
225 memset(pix, 0, rs * h); /*this should be faster than pixbuf_fill */
229 static void image_loader_size_cb(GdkPixbufLoader *loader,
230 gint width, gint height, gpointer data)
232 ImageLoader *il = data;
233 GdkPixbufFormat *format;
238 if (il->requested_width < 1 || il->requested_height < 1) return;
240 format = gdk_pixbuf_loader_get_format(loader);
243 mime_types = gdk_pixbuf_format_get_mime_types(format);
245 while (mime_types[n])
247 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
250 g_strfreev(mime_types);
254 if (width > il->requested_width || height > il->requested_height)
258 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
260 nw = il->requested_width;
261 nh = (gdouble)nw / width * height;
266 nh = il->requested_height;
267 nw = (gdouble)nh / height * width;
271 gdk_pixbuf_loader_set_size(loader, nw, nh);
276 static void image_loader_stop(ImageLoader *il)
280 if (il->idle_id != -1)
282 g_source_remove(il->idle_id);
288 /* some loaders do not have a pixbuf till close, order is important here */
289 gdk_pixbuf_loader_close(il->loader, NULL);
290 image_loader_sync_pixbuf(il);
291 g_object_unref(G_OBJECT(il->loader));
299 exif_free_preview(il->mapped_file);
303 munmap(il->mapped_file, il->bytes_total);
305 il->mapped_file = NULL;
311 static void image_loader_done(ImageLoader *il)
313 image_loader_stop(il);
315 g_signal_emit(il, signals[SIGNAL_DONE], 0);
318 static gint image_loader_done_delay_cb(gpointer data)
320 ImageLoader *il = data;
322 il->idle_done_id = -1;
323 image_loader_done(il);
327 static void image_loader_done_delay(ImageLoader *il)
329 if (il->idle_done_id == -1) il->idle_done_id = g_idle_add_full(il->idle_priority,
330 image_loader_done_delay_cb, il, NULL);
333 static void image_loader_error(ImageLoader *il)
335 image_loader_stop(il);
337 DEBUG_1("pixbuf_loader reported load error for: %s", il->fd->path);
339 g_signal_emit(il, signals[SIGNAL_ERROR], 0);
342 static gint image_loader_idle_cb(gpointer data)
344 ImageLoader *il = data;
348 if (!il) return FALSE;
350 if (il->idle_id == -1) return FALSE;
352 c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
355 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
359 image_loader_done(il);
363 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
365 image_loader_error(il);
374 if (il->bytes_total > 0)
376 g_signal_emit(il, signals[SIGNAL_PERCENT], 0, (gdouble)il->bytes_read / il->bytes_total);
382 static gint image_loader_begin(ImageLoader *il)
386 if (!il->loader || il->pixbuf) return FALSE;
388 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
392 image_loader_stop(il);
396 if (!gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL))
398 image_loader_stop(il);
404 /* read until size is known */
405 while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
407 b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
408 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
410 image_loader_stop(il);
415 if (!il->pixbuf) image_loader_sync_pixbuf(il);
417 if (il->bytes_read == il->bytes_total || b < 1)
419 /* done, handle (broken) loaders that do not have pixbuf till close */
420 image_loader_stop(il);
422 if (!il->pixbuf) return FALSE;
424 image_loader_done_delay(il);
430 image_loader_stop(il);
434 /* finally, progressive loading :) */
435 il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
440 static gint image_loader_setup(ImageLoader *il)
445 if (!il || il->loader || il->mapped_file) return FALSE;
447 il->mapped_file = NULL;
451 ExifData *exif = exif_read_fd(il->fd);
453 il->mapped_file = exif_get_preview(exif, &il->bytes_total);
458 DEBUG_1("Raw file %s contains embedded image", il->fd->path);
460 exif_free_fd(il->fd, exif);
464 if (!il->mapped_file)
469 pathl = path_from_utf8(il->fd->path);
470 load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
472 if (load_fd == -1) return FALSE;
474 if (fstat(load_fd, &st) == 0)
476 il->bytes_total = st.st_size;
484 il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0);
486 if (il->mapped_file == MAP_FAILED)
495 il->loader = gdk_pixbuf_loader_new();
496 g_signal_connect(G_OBJECT(il->loader), "area_updated",
497 G_CALLBACK(image_loader_area_updated_cb), il);
498 g_signal_connect(G_OBJECT(il->loader), "size_prepared",
499 G_CALLBACK(image_loader_size_cb), il);
500 g_signal_connect(G_OBJECT(il->loader), "area_prepared",
501 G_CALLBACK(image_loader_area_prepared_cb), il);
505 return image_loader_begin(il);
509 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
510 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
512 if (!il) return NULL;
517 gchar *image_loader_get_format(ImageLoader *il)
519 GdkPixbufFormat *format;
523 if (!il || !il->loader) return NULL;
525 format = gdk_pixbuf_loader_get_format(il->loader);
526 if (!format) return NULL;
528 mimev = gdk_pixbuf_format_get_mime_types(format);
529 if (!mimev) return NULL;
531 /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
532 mime = g_strdup(mimev[0]);
538 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
542 il->requested_width = width;
543 il->requested_height = height;
546 void image_loader_set_buffer_size(ImageLoader *il, guint count)
550 il->idle_read_loop_count = count ? count : 1;
553 void image_loader_set_priority(ImageLoader *il, gint priority)
557 il->idle_priority = priority;
560 gint image_loader_start(ImageLoader *il)
562 if (!il) return FALSE;
564 if (!il->fd) return FALSE;
566 return image_loader_setup(il);
569 gdouble image_loader_get_percent(ImageLoader *il)
571 if (!il || il->bytes_total == 0) return 0.0;
573 return (gdouble)il->bytes_read / il->bytes_total;
576 gint image_loader_get_is_done(ImageLoader *il)
578 if (!il) return FALSE;
583 FileData *image_loader_get_fd(ImageLoader *il)
585 if (!il) return NULL;
591 gint image_loader_get_shrunk(ImageLoader *il)
593 if (!il) return FALSE;
600 gint image_load_dimensions(FileData *fd, gint *width, gint *height)
605 il = image_loader_new(fd);
607 success = image_loader_start(il);
609 if (success && il->pixbuf)
611 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
612 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
616 if (width) *width = -1;
617 if (height) *height = -1;
620 image_loader_free(il);