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!
17 #include "image-load.h"
19 #include "color-man.h"
21 #include "pixbuf-renderer.h"
22 #include "pixbuf_util.h"
23 #include "ui_fileops.h"
28 /* size of the image loader buffer (512 bytes x defined number) */
29 #define IMAGE_LOAD_BUFFER_COUNT 8
31 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
32 #define IMAGE_THROTTLE_LARGER_IMAGES 1
34 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
35 #define IMAGE_THROTTLE_FACTOR 4
37 /* the file size at which throttling take place */
38 #define IMAGE_THROTTLE_THRESHOLD 1048576
40 #define IMAGE_AUTO_REFRESH_TIME 3000
43 static GList *image_list = NULL;
46 static void image_update_title(ImageWindow *imd);
47 static void image_post_process(ImageWindow *imd, gint clamp);
48 static void image_read_ahead_start(ImageWindow *imd);
51 *-------------------------------------------------------------------
53 *-------------------------------------------------------------------
56 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
58 ImageWindow *imd = data;
62 imd->func_button(imd, event->button, event->time,
63 event->x, event->y, event->state, imd->data_button);
67 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
69 ImageWindow *imd = data;
71 if (imd->func_scroll_notify && pr->scale)
73 imd->func_scroll_notify(imd,
74 (gint)((gdouble)pr->x_scroll / pr->scale),
75 (gint)((gdouble)pr->y_scroll / pr->scale),
76 (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale),
77 (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale),
78 imd->data_scroll_notify);
82 static void image_update_util(ImageWindow *imd)
84 if (imd->func_update) imd->func_update(imd, imd->data_update);
87 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
89 ImageWindow *imd = data;
91 if (imd->title_show_zoom) image_update_title(imd);
92 image_update_util(imd);
95 static void image_complete_util(ImageWindow *imd, gint preload)
97 if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
99 if (debug) printf("image load completed \"%s\" (%s)\n",
100 (preload) ? imd->read_ahead_path : imd->image_path,
101 (preload) ? "preload" : "current");
103 if (!preload) imd->completed = TRUE;
104 if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
107 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
109 ImageWindow *imd = data;
111 image_complete_util(imd, FALSE);
114 static void image_state_set(ImageWindow *imd, ImageState state)
116 if (state == IMAGE_STATE_NONE)
124 if (imd->func_state) imd->func_state(imd, state, imd->data_state);
127 static void image_state_unset(ImageWindow *imd, ImageState state)
129 imd->state &= ~state;
130 if (imd->func_state) imd->func_state(imd, state, imd->data_state);
134 *-------------------------------------------------------------------
136 *-------------------------------------------------------------------
139 static void image_update_title(ImageWindow *imd)
143 gchar *collection = NULL;
145 if (!imd->top_window) return;
147 if (imd->collection && collection_to_number(imd->collection) >= 0)
150 name = imd->collection->name;
151 if (!name) name = _("Untitled");
152 collection = g_strdup_printf(" (Collection %s)", name);
155 if (imd->title_show_zoom)
157 gchar *buf = image_zoom_get_as_text(imd);
158 zoom = g_strconcat(" [", buf, "]", NULL);
162 title = g_strdup_printf("%s%s%s%s%s%s",
163 imd->title ? imd->title : "",
164 imd->image_name ? imd->image_name : "",
166 collection ? collection : "",
167 imd->image_name ? " - " : "",
168 imd->title_right ? imd->title_right : "");
170 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
178 *-------------------------------------------------------------------
179 * rotation, flip, etc.
180 *-------------------------------------------------------------------
183 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
186 GdkPixbuf *new = NULL;
191 pr = (PixbufRenderer *)imd->pr;
193 exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
194 imd->delay_alter_type = ALTER_NONE;
196 if (!pr->pixbuf) return;
198 x = pr->x_scroll + (pr->vis_width / 2);
199 y = pr->y_scroll + (pr->vis_height / 2);
203 case ALTER_ROTATE_90:
204 new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
209 case ALTER_ROTATE_90_CC:
210 new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
215 case ALTER_ROTATE_180:
216 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
221 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
225 new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
228 case ALTER_DESATURATE:
229 pixbuf_desaturate_rect(pr->pixbuf,
230 0, 0, pr->image_width, pr->image_height);
231 image_area_changed(imd, 0, 0, pr->image_width, pr->image_height);
241 pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
244 if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
248 switch (pr->scroll_reset)
250 case PR_SCROLL_RESET_NOCHANGE:
252 case PR_SCROLL_RESET_CENTER:
253 x = (gint)((gdouble)pr->image_width / 2.0 * pr->scale);
254 y = (gint)((gdouble)pr->image_height / 2.0 * pr->scale);
256 case PR_SCROLL_RESET_TOPLEFT:
263 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)x / pr->scale),
264 (gint)((gdouble)y / pr->scale),
269 static void image_post_process_alter(ImageWindow *imd, gint clamp)
271 if (imd->delay_alter_type != ALTER_NONE)
273 image_alter_real(imd, imd->delay_alter_type, clamp);
277 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
279 ImageWindow *imd = data;
282 if (type == COLOR_RETURN_IMAGE_CHANGED)
284 if (cm == imd->cm) imd->cm = NULL;
289 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
291 image_post_process_alter(imd, FALSE);
293 image_read_ahead_start(imd);
296 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
299 ColorManProfileType input_type;
300 ColorManProfileType screen_type;
301 const gchar *input_file;
302 const gchar *screen_file;
303 ExifItem *item = NULL;
305 if (imd->cm) return FALSE;
307 if (imd->color_profile_input >= 1 &&
308 imd->color_profile_input <= COLOR_PROFILE_INPUTS)
312 n = imd->color_profile_input - 1;
313 if (!color_profile_input_file[n]) return FALSE;
315 input_type = COLOR_PROFILE_FILE;
316 input_file = color_profile_input_file[n];
318 else if (imd->color_profile_input == 0)
320 input_type = COLOR_PROFILE_SRGB;
328 if (imd->color_profile_screen == 1 &&
329 color_profile_screen_file)
331 screen_type = COLOR_PROFILE_FILE;
332 screen_file = color_profile_screen_file;
334 else if (imd->color_profile_screen == 0)
336 screen_type = COLOR_PROFILE_SRGB;
344 if (imd->color_profile_use_image && exif)
346 item = exif_get_item(exif, "ColorProfile");
351 /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
352 if (exif_get_integer(exif, "ColorSpace", &cs) &&
355 input_type = COLOR_PROFILE_SRGB;
358 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
362 if (item && item->format == EXIF_FORMAT_UNDEFINED)
364 if (debug) printf("Found embedded color profile\n");
366 cm = color_man_new_embedded(imd, NULL,
367 item->data, item->data_len,
368 screen_type, screen_file,
369 image_post_process_color_cb, imd);
373 cm = color_man_new(imd, NULL,
374 input_type, input_file,
375 screen_type, screen_file,
376 image_post_process_color_cb, imd);
384 cm->incremental_sync = TRUE;
387 imd->cm = (gpointer)cm;
394 static void image_post_process(ImageWindow *imd, gint clamp)
396 ExifData *exif = NULL;
398 if (!image_get_pixbuf(imd)) return;
400 if (exif_rotate_enable ||
401 (imd->color_profile_enable && imd->color_profile_use_image) )
403 exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
406 if (exif_rotate_enable && exif)
410 if (exif_get_integer(exif, "Orientation", &orientation))
414 /* see http://jpegclub.org/exif_orientation.html
417 888888 888888 88 88 8888888888 88 88 8888888888
418 88 88 88 88 88 88 88 88 88 88 88 88
419 8888 8888 8888 8888 88 8888888888 8888888888 88
425 case EXIF_ORIENTATION_TOP_LEFT:
426 /* normal -- nothing to do */
429 case EXIF_ORIENTATION_TOP_RIGHT:
431 imd->delay_alter_type = ALTER_MIRROR;
433 case EXIF_ORIENTATION_BOTTOM_RIGHT:
435 imd->delay_alter_type = ALTER_ROTATE_180;
437 case EXIF_ORIENTATION_BOTTOM_LEFT:
439 imd->delay_alter_type = ALTER_FLIP;
441 case EXIF_ORIENTATION_LEFT_TOP:
442 /* not implemented -- too wacky to fix in one step */
445 case EXIF_ORIENTATION_RIGHT_TOP:
446 /* rotated -90 (270) */
447 imd->delay_alter_type = ALTER_ROTATE_90;
449 case EXIF_ORIENTATION_RIGHT_BOTTOM:
450 /* not implemented -- too wacky to fix in one step */
453 case EXIF_ORIENTATION_LEFT_BOTTOM:
455 imd->delay_alter_type = ALTER_ROTATE_90_CC;
458 /* The other values are out of range */
463 if (rotate) image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
467 if (imd->color_profile_enable)
469 if (!image_post_process_color(imd, 0, exif))
471 /* fixme: note error to user */
472 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
476 if (!imd->cm) image_post_process_alter(imd, clamp);
482 *-------------------------------------------------------------------
483 * read ahead (prebuffer)
484 *-------------------------------------------------------------------
487 static void image_read_ahead_cancel(ImageWindow *imd)
489 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
491 image_loader_free(imd->read_ahead_il);
492 imd->read_ahead_il = NULL;
494 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
495 imd->read_ahead_pixbuf = NULL;
497 g_free(imd->read_ahead_path);
498 imd->read_ahead_path = NULL;
501 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
503 ImageWindow *imd = data;
505 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
507 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
508 if (imd->read_ahead_pixbuf)
510 g_object_ref(imd->read_ahead_pixbuf);
514 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
516 image_loader_free(imd->read_ahead_il);
517 imd->read_ahead_il = NULL;
519 image_complete_util(imd, TRUE);
522 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
524 /* we even treat errors as success, maybe at least some of the file was ok */
525 image_read_ahead_done_cb(il, data);
528 static void image_read_ahead_start(ImageWindow *imd)
530 /* already started ? */
531 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
533 /* still loading ?, do later */
534 if (imd->il || imd->cm) return;
536 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
538 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
540 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
541 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
543 image_read_ahead_cancel(imd);
544 image_complete_util(imd, TRUE);
548 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
550 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
552 image_read_ahead_cancel(imd);
554 imd->read_ahead_path = g_strdup(path);
556 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
558 image_read_ahead_start(imd);
562 *-------------------------------------------------------------------
564 *-------------------------------------------------------------------
567 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
569 g_free(imd->prev_path);
570 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
574 imd->prev_path = g_strdup(path);
576 g_object_ref(pixbuf);
577 imd->prev_pixbuf = pixbuf;
578 imd->prev_color_row = color_row;
582 imd->prev_path = NULL;
583 imd->prev_pixbuf = NULL;
584 imd->prev_color_row = -1;
587 if (debug) printf("post buffer set: %s\n", path);
590 static gint image_post_buffer_get(ImageWindow *imd)
594 if (imd->prev_pixbuf &&
595 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
597 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
598 if (imd->prev_color_row >= 0)
600 ExifData *exif = NULL;
602 if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
603 image_post_process_color(imd, imd->prev_color_row, exif);
613 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
614 imd->prev_pixbuf = NULL;
616 g_free(imd->prev_path);
617 imd->prev_path = NULL;
623 *-------------------------------------------------------------------
625 *-------------------------------------------------------------------
628 static void image_load_pixbuf_ready(ImageWindow *imd)
630 if (image_get_pixbuf(imd) || !imd->il) return;
632 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
635 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
637 ImageWindow *imd = data;
640 pr = (PixbufRenderer *)imd->pr;
642 if (imd->delay_flip &&
643 pr->pixbuf != image_loader_get_pixbuf(il))
648 if (!pr->pixbuf) image_load_pixbuf_ready(imd);
650 pixbuf_renderer_area_changed(pr, x, y, w, h);
653 static void image_load_done_cb(ImageLoader *il, gpointer data)
655 ImageWindow *imd = data;
657 if (debug) printf ("image done\n");
659 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
660 image_state_unset(imd, IMAGE_STATE_LOADING);
662 if (imd->delay_flip &&
663 image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
665 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
666 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
669 image_loader_free(imd->il);
672 image_post_process(imd, TRUE);
674 image_read_ahead_start(imd);
677 static void image_load_error_cb(ImageLoader *il, gpointer data)
679 if (debug) printf ("image error\n");
681 /* even on error handle it like it was done,
682 * since we have a pixbuf with _something_ */
684 image_load_done_cb(il, data);
687 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
688 static void image_load_buffer_throttle(ImageLoader *il)
690 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
692 /* Larger image files usually have larger chunks of data per pixel...
693 * So increase the buffer read size so that the rendering chunks called
697 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
701 /* this read ahead is located here merely for the callbacks, above */
703 static gint image_read_ahead_check(ImageWindow *imd)
705 if (!imd->read_ahead_path) return FALSE;
706 if (imd->il) return FALSE;
708 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
710 image_read_ahead_cancel(imd);
714 if (imd->read_ahead_il)
716 imd->il = imd->read_ahead_il;
717 imd->read_ahead_il = NULL;
719 /* override the old signals */
720 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
721 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
722 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
724 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
725 image_load_buffer_throttle(imd->il);
728 /* do this one directly (probably should add a set func) */
729 imd->il->func_done = image_load_done_cb;
731 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
732 image_state_set(imd, IMAGE_STATE_LOADING);
734 if (!imd->delay_flip)
736 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
739 image_read_ahead_cancel(imd);
742 else if (imd->read_ahead_pixbuf)
744 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
745 g_object_unref(imd->read_ahead_pixbuf);
746 imd->read_ahead_pixbuf = NULL;
748 image_read_ahead_cancel(imd);
750 image_post_process(imd, FALSE);
754 image_read_ahead_cancel(imd);
758 static gint image_load_begin(ImageWindow *imd, const gchar *path)
760 if (debug) printf ("image begin \n");
762 if (imd->il) return FALSE;
764 imd->completed = FALSE;
765 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
767 if (image_post_buffer_get(imd))
769 if (debug) printf("from post buffer: %s\n", imd->image_path);
773 if (image_read_ahead_check(imd))
775 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
779 if (!imd->delay_flip && image_get_pixbuf(imd))
783 pr = PIXBUF_RENDERER(imd->pr);
784 if (pr->pixbuf) g_object_unref(pr->pixbuf);
788 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
790 imd->il = image_loader_new(path);
792 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
793 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
794 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
796 if (!image_loader_start(imd->il, image_load_done_cb, imd))
798 if (debug) printf("image start error\n");
800 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
802 image_loader_free(imd->il);
805 image_complete_util(imd, FALSE);
810 image_state_set(imd, IMAGE_STATE_LOADING);
812 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
813 image_load_buffer_throttle(imd->il);
816 if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
821 static void image_reset(ImageWindow *imd)
823 /* stops anything currently being done */
825 if (debug) printf("image reset\n");
827 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
829 image_loader_free(imd->il);
832 color_man_free((ColorMan *)imd->cm);
835 imd->delay_alter_type = ALTER_NONE;
837 image_state_set(imd, IMAGE_STATE_NONE);
841 *-------------------------------------------------------------------
843 *-------------------------------------------------------------------
846 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
850 if (imd->image_path && isfile(imd->image_path))
854 pr = PIXBUF_RENDERER(imd->pr);
855 pr->zoom = zoom; /* store the zoom, needed by the loader */
857 if (image_load_begin(imd, imd->image_path))
859 imd->unknown = FALSE;
865 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
866 image_change_pixbuf(imd, pixbuf, zoom);
867 g_object_unref(pixbuf);
871 imd->size = filesize(imd->image_path);
872 imd->mtime = filetime(imd->image_path);
880 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
881 image_change_pixbuf(imd, pixbuf, zoom);
882 g_object_unref(pixbuf);
883 imd->mtime = filetime(imd->image_path);
887 image_change_pixbuf(imd, NULL, zoom);
894 image_update_util(imd);
897 static void image_change_real(ImageWindow *imd, const gchar *path,
898 CollectionData *cd, CollectInfo *info, gdouble zoom)
901 GdkPixbuf *prev_pixbuf = NULL;
902 gchar *prev_path = NULL;
903 gint prev_clear = FALSE;
904 gint prev_color_row = -1;
906 imd->collection = cd;
907 imd->collection_info = info;
909 pixbuf = image_get_pixbuf(imd);
911 if (enable_read_ahead && imd->image_path && pixbuf)
915 /* current image is not finished */
920 prev_path = g_strdup(imd->image_path);
921 prev_pixbuf = pixbuf;
922 g_object_ref(prev_pixbuf);
928 cm = (ColorMan *)imd->cm;
929 prev_color_row = cm->row;
934 g_free(imd->image_path);
935 imd->image_path = g_strdup(path);
936 imd->image_name = filename_from_path(imd->image_path);
938 image_change_complete(imd, zoom, TRUE);
942 image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
944 g_object_unref(prev_pixbuf);
948 image_post_buffer_set(imd, NULL, NULL, -1);
951 image_update_title(imd);
952 image_state_set(imd, IMAGE_STATE_IMAGE);
956 *-------------------------------------------------------------------
958 *-------------------------------------------------------------------
961 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
965 widget = imd->widget;
966 if (!widget->window) return;
970 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
971 area, widget, "image_window",
972 widget->allocation.x, widget->allocation.y,
973 widget->allocation.width - 1, widget->allocation.height - 1);
977 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
978 area, widget, "image_window",
979 widget->allocation.x, widget->allocation.y,
980 widget->allocation.width - 1, widget->allocation.height - 1);
984 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
986 ImageWindow *imd = data;
988 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
992 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
994 ImageWindow *imd = data;
996 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
997 image_focus_paint(imd, TRUE, NULL);
1002 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1004 ImageWindow *imd = data;
1006 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1007 image_focus_paint(imd, FALSE, NULL);
1012 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
1013 gint relative, gint always)
1015 return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1018 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1020 pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1023 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1025 return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1028 void image_overlay_remove(ImageWindow *imd, gint id)
1030 pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1033 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1035 ImageWindow *imd = data;
1037 if (imd->func_scroll &&
1038 event && event->type == GDK_SCROLL)
1040 imd->func_scroll(imd, event->direction, event->time,
1041 event->x, event->y, event->state, imd->data_scroll);
1049 *-------------------------------------------------------------------
1051 *-------------------------------------------------------------------
1054 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1055 const gchar *title, const gchar *title_right, gint show_zoom)
1057 imd->top_window = window;
1059 imd->title = g_strdup(title);
1060 g_free(imd->title_right);
1061 imd->title_right = g_strdup(title_right);
1062 imd->title_show_zoom = show_zoom;
1064 if (!fit_window) window = NULL;
1066 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1068 image_update_title(imd);
1071 void image_set_update_func(ImageWindow *imd,
1072 void (*func)(ImageWindow *imd, gpointer data),
1075 imd->func_update = func;
1076 imd->data_update = data;
1079 void image_set_complete_func(ImageWindow *imd,
1080 void (*func)(ImageWindow *imd, gint preload, gpointer data),
1083 imd->func_complete = func;
1084 imd->data_complete = data;
1087 void image_set_state_func(ImageWindow *imd,
1088 void (*func)(ImageWindow *imd, ImageState state, gpointer data),
1091 imd->func_state = func;
1092 imd->data_state = data;
1096 void image_set_button_func(ImageWindow *imd,
1097 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1100 imd->func_button = func;
1101 imd->data_button = data;
1104 void image_set_scroll_func(ImageWindow *imd,
1105 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1108 imd->func_scroll = func;
1109 imd->data_scroll = data;
1112 void image_set_scroll_notify_func(ImageWindow *imd,
1113 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1116 imd->func_scroll_notify = func;
1117 imd->data_scroll_notify = data;
1122 const gchar *image_get_path(ImageWindow *imd)
1124 return imd->image_path;
1127 const gchar *image_get_name(ImageWindow *imd)
1129 return imd->image_name;
1132 /* merely changes path string, does not change the image! */
1133 void image_set_path(ImageWindow *imd, const gchar *newpath)
1135 g_free(imd->image_path);
1136 imd->image_path = g_strdup(newpath);
1137 imd->image_name = filename_from_path(imd->image_path);
1139 image_update_title(imd);
1140 image_state_set(imd, IMAGE_STATE_IMAGE);
1143 /* load a new image */
1145 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
1147 if (imd->image_path == path ||
1148 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
1150 image_change_real(imd, path, NULL, NULL, zoom);
1153 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1155 return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1158 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1160 pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1161 image_state_set(imd, IMAGE_STATE_IMAGE);
1164 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1166 if (!cd || !info || !g_list_find(cd->list, info)) return;
1168 image_change_real(imd, info->path, cd, info, zoom);
1171 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1173 if (collection_to_number(imd->collection) >= 0)
1175 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1177 if (info) *info = imd->collection_info;
1181 if (info) *info = NULL;
1183 return imd->collection;
1186 if (info) *info = NULL;
1190 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1192 /* change data for the callbacks directly */
1194 il->data_area_ready = data;
1195 il->data_error = data;
1196 il->data_done = data;
1197 il->data_percent = data;
1200 /* this is more like a move function
1201 * it moves most data from source to imd
1203 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1205 if (imd == source) return;
1207 imd->unknown = source->unknown;
1209 imd->collection = source->collection;
1210 imd->collection_info = source->collection_info;
1211 imd->size = source->size;
1212 imd->mtime = source->mtime;
1214 image_set_path(imd, image_get_path(source));
1216 image_loader_free(imd->il);
1221 imd->il = source->il;
1224 image_loader_sync_data(imd->il, imd);
1226 imd->delay_alter_type = source->delay_alter_type;
1227 source->delay_alter_type = ALTER_NONE;
1230 imd->color_profile_enable = source->color_profile_enable;
1231 imd->color_profile_input = source->color_profile_input;
1232 imd->color_profile_screen = source->color_profile_screen;
1233 imd->color_profile_use_image = source->color_profile_use_image;
1234 color_man_free((ColorMan *)imd->cm);
1240 imd->cm = source->cm;
1243 cm = (ColorMan *)imd->cm;
1245 cm->func_done_data = imd;
1248 image_loader_free(imd->read_ahead_il);
1249 imd->read_ahead_il = source->read_ahead_il;
1250 source->read_ahead_il = NULL;
1251 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1253 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1254 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1255 source->read_ahead_pixbuf = NULL;
1257 g_free(imd->read_ahead_path);
1258 imd->read_ahead_path = source->read_ahead_path;
1259 source->read_ahead_path = NULL;
1261 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1262 imd->prev_pixbuf = source->prev_pixbuf;
1263 source->prev_pixbuf = NULL;
1264 imd->prev_color_row = source->prev_color_row;
1265 source->prev_color_row = -1;
1267 g_free(imd->prev_path);
1268 imd->prev_path = source->prev_path;
1269 source->prev_path = NULL;
1271 imd->completed = source->completed;
1272 imd->state = source->state;
1273 source->state = IMAGE_STATE_NONE;
1275 pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1280 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1282 pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1285 void image_reload(ImageWindow *imd)
1287 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1289 image_change_complete(imd, image_zoom_get(imd), FALSE);
1292 void image_scroll(ImageWindow *imd, gint x, gint y)
1294 pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1297 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1298 gdouble x_align, gdouble y_align)
1300 pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1303 void image_alter(ImageWindow *imd, AlterType type)
1305 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1307 if (imd->il || imd->cm)
1309 /* still loading, wait till done */
1310 imd->delay_alter_type = type;
1311 image_state_set(imd, IMAGE_STATE_ROTATE_USER);
1313 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1315 image_state_unset(imd, IMAGE_STATE_ROTATE_AUTO);
1320 image_alter_real(imd, type, TRUE);
1323 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1325 pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1328 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1330 pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1333 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1335 pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1338 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1340 pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1343 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1349 pr = (PixbufRenderer *)imd->pr;
1351 if (!pixbuf_renderer_get_pixbuf(pr) ||
1352 !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1356 zoom = (gdouble)pr->window_height / height;
1360 zoom = (gdouble)pr->window_width / width;
1365 zoom = 0.0 - 1.0 / zoom;
1368 pixbuf_renderer_zoom_set(pr, zoom);
1371 gdouble image_zoom_get(ImageWindow *imd)
1373 return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1376 gdouble image_zoom_get_real(ImageWindow *imd)
1378 return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1381 gchar *image_zoom_get_as_text(ImageWindow *imd)
1389 gchar *approx = " ";
1391 zoom = image_zoom_get(imd);
1392 scale = image_zoom_get_real(imd);
1398 else if (zoom < 0.0)
1402 else if (zoom == 0.0 && scale != 0.0)
1415 if (rint(l) != l) pl = 1;
1416 if (rint(r) != r) pr = 1;
1418 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1421 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1425 if (mode == ZOOM_RESET_ORIGINAL)
1429 else if (mode == ZOOM_RESET_FIT_WINDOW)
1437 zoom = image_zoom_get(imd);
1450 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1452 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1456 image_read_ahead_set(imd, path);
1460 image_read_ahead_cancel(imd);
1464 static gint image_auto_refresh_cb(gpointer data)
1466 ImageWindow *imd = data;
1469 if (!imd || !image_get_pixbuf(imd) ||
1470 imd->il || !imd->image_path ||
1471 !update_on_time_change) return TRUE;
1473 newtime = filetime(imd->image_path);
1474 if (newtime > 0 && newtime != imd->mtime)
1476 imd->mtime = newtime;
1483 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1485 void image_auto_refresh(ImageWindow *imd, gint interval)
1488 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1490 if (imd->auto_refresh_id > -1)
1492 g_source_remove(imd->auto_refresh_id);
1493 imd->auto_refresh_id = -1;
1494 imd->auto_refresh_interval = -1;
1497 if (interval < 0) return;
1499 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1501 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1502 imd->auto_refresh_interval = interval;
1505 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1507 imd->top_window_sync = allow_sync;
1509 g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1512 void image_background_set_black(ImageWindow *imd, gint black)
1514 pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1517 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1519 pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1522 void image_color_profile_set(ImageWindow *imd,
1523 gint input_type, gint screen_type,
1528 if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1529 screen_type < 0 || screen_type > 1)
1534 imd->color_profile_input = input_type;
1535 imd->color_profile_screen = screen_type;
1536 imd->color_profile_use_image = use_image;
1539 gint image_color_profile_get(ImageWindow *imd,
1540 gint *input_type, gint *screen_type,
1543 if (!imd) return FALSE;
1545 if (input_type) *input_type = imd->color_profile_input;
1546 if (screen_type) *screen_type = imd->color_profile_screen;
1547 if (use_image) *use_image = imd->color_profile_use_image;
1552 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1556 if (imd->color_profile_enable == enable) return;
1558 imd->color_profile_enable = enable;
1561 gint image_color_profile_get_use(ImageWindow *imd)
1563 if (!imd) return FALSE;
1565 return imd->color_profile_enable;
1568 void image_set_delay_flip(ImageWindow *imd, gint delay)
1571 imd->delay_flip == delay) return;
1573 imd->delay_flip = delay;
1575 g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1577 if (!imd->delay_flip && imd->il)
1581 pr = PIXBUF_RENDERER(imd->pr);
1582 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1585 image_load_pixbuf_ready(imd);
1589 void image_to_root_window(ImageWindow *imd, gint scaled)
1592 GdkWindow *rootwindow;
1600 pixbuf = image_get_pixbuf(imd);
1601 if (!pixbuf) return;
1603 screen = gtk_widget_get_screen(imd->widget);
1604 rootwindow = gdk_screen_get_root_window(screen);
1605 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1609 width = gdk_screen_width();
1610 height = gdk_screen_height();
1614 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1617 pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1619 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1620 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1621 gdk_window_clear(rootwindow);
1623 g_object_unref(pixmap);
1629 *-------------------------------------------------------------------
1631 *-------------------------------------------------------------------
1634 static void image_options_set(ImageWindow *imd)
1636 g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1637 "zoom_2pass", two_pass_zoom,
1638 "zoom_expand", zoom_to_fit_expands,
1639 "dither_quality", dither_quality,
1640 "scroll_reset", scroll_reset_method,
1641 "cache_display", tile_cache_max,
1642 "window_fit", (imd->top_window_sync && fit_window),
1643 "window_limit", limit_window_size,
1644 "window_limit_value", max_window_size,
1647 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1650 void image_options_sync(void)
1662 image_options_set(imd);
1667 *-------------------------------------------------------------------
1669 *-------------------------------------------------------------------
1672 static void image_free(ImageWindow *imd)
1674 image_list = g_list_remove(image_list, imd);
1678 image_read_ahead_cancel(imd);
1679 image_post_buffer_set(imd, NULL, NULL, -1);
1680 image_auto_refresh(imd, -1);
1682 g_free(imd->image_path);
1684 g_free(imd->title_right);
1689 static void image_destroy_cb(GtkObject *widget, gpointer data)
1691 ImageWindow *imd = data;
1695 ImageWindow *image_new(gint frame)
1699 imd = g_new0(ImageWindow, 1);
1701 imd->top_window = NULL;
1703 imd->title_right = NULL;
1704 imd->title_show_zoom = FALSE;
1706 imd->unknown = TRUE;
1708 imd->has_frame = frame;
1709 imd->top_window_sync = FALSE;
1711 imd->delay_alter_type = ALTER_NONE;
1713 imd->read_ahead_il = NULL;
1714 imd->read_ahead_pixbuf = NULL;
1715 imd->read_ahead_path = NULL;
1717 imd->completed = FALSE;
1718 imd->state = IMAGE_STATE_NONE;
1720 imd->color_profile_enable = FALSE;
1721 imd->color_profile_input = 0;
1722 imd->color_profile_screen = 0;
1723 imd->color_profile_use_image = FALSE;
1725 imd->auto_refresh_id = -1;
1726 imd->auto_refresh_interval = -1;
1728 imd->delay_flip = FALSE;
1730 imd->func_update = NULL;
1731 imd->func_complete = NULL;
1732 imd->func_tile_request = NULL;
1733 imd->func_tile_dispose = NULL;
1735 imd->func_button = NULL;
1736 imd->func_scroll = NULL;
1738 imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1740 image_options_set(imd);
1744 imd->widget = gtk_frame_new(NULL);
1745 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1746 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1747 gtk_widget_show(imd->pr);
1749 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1750 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1751 G_CALLBACK(image_focus_in_cb), imd);
1752 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1753 G_CALLBACK(image_focus_out_cb), imd);
1755 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1756 G_CALLBACK(image_focus_expose), imd);
1760 imd->widget = imd->pr;
1763 g_signal_connect(G_OBJECT(imd->pr), "clicked",
1764 G_CALLBACK(image_click_cb), imd);
1765 g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1766 G_CALLBACK(image_scroll_notify_cb), imd);
1768 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1769 G_CALLBACK(image_scroll_cb), imd);
1771 g_signal_connect(G_OBJECT(imd->pr), "destroy",
1772 G_CALLBACK(image_destroy_cb), imd);
1774 g_signal_connect(G_OBJECT(imd->pr), "zoom",
1775 G_CALLBACK(image_zoom_cb), imd);
1776 g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1777 G_CALLBACK(image_render_complete_cb), imd);
1779 image_list = g_list_append(image_list, imd);