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_new_util(ImageWindow *imd)
116 if (imd->func_new) imd->func_new(imd, imd->data_new);
120 *-------------------------------------------------------------------
122 *-------------------------------------------------------------------
125 static void image_update_title(ImageWindow *imd)
129 gchar *collection = NULL;
131 if (!imd->top_window) return;
133 if (imd->collection && collection_to_number(imd->collection) >= 0)
136 name = imd->collection->name;
137 if (!name) name = _("Untitled");
138 collection = g_strdup_printf(" (Collection %s)", name);
141 if (imd->title_show_zoom)
143 gchar *buf = image_zoom_get_as_text(imd);
144 zoom = g_strconcat(" [", buf, "]", NULL);
148 title = g_strdup_printf("%s%s%s%s%s%s",
149 imd->title ? imd->title : "",
150 imd->image_name ? imd->image_name : "",
152 collection ? collection : "",
153 imd->image_name ? " - " : "",
154 imd->title_right ? imd->title_right : "");
156 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
164 *-------------------------------------------------------------------
165 * rotation, flip, etc.
166 *-------------------------------------------------------------------
169 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
172 GdkPixbuf *new = NULL;
177 pr = (PixbufRenderer *)imd->pr;
179 exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
180 imd->delay_alter_type = ALTER_NONE;
182 if (!pr->pixbuf) return;
184 x = pr->x_scroll + (pr->vis_width / 2);
185 y = pr->y_scroll + (pr->vis_height / 2);
189 case ALTER_ROTATE_90:
190 new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
195 case ALTER_ROTATE_90_CC:
196 new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
201 case ALTER_ROTATE_180:
202 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
207 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
211 new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
214 case ALTER_DESATURATE:
215 pixbuf_desaturate_rect(pr->pixbuf,
216 0, 0, pr->image_width, pr->image_height);
217 image_area_changed(imd, 0, 0, pr->image_width, pr->image_height);
227 pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
230 if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
234 switch (pr->scroll_reset)
236 case PR_SCROLL_RESET_NOCHANGE:
238 case PR_SCROLL_RESET_CENTER:
239 x = (gint)((gdouble)pr->image_width / 2.0 * pr->scale);
240 y = (gint)((gdouble)pr->image_height / 2.0 * pr->scale);
242 case PR_SCROLL_RESET_TOPLEFT:
249 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)x / pr->scale),
250 (gint)((gdouble)y / pr->scale),
255 static void image_post_process_alter(ImageWindow *imd, gint clamp)
257 if (imd->delay_alter_type != ALTER_NONE)
259 image_alter_real(imd, imd->delay_alter_type, clamp);
263 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
265 ImageWindow *imd = data;
268 if (type == COLOR_RETURN_IMAGE_CHANGED)
270 if (cm == imd->cm) imd->cm = NULL;
275 imd->state |= IMAGE_STATE_COLOR_ADJ;
277 image_post_process_alter(imd, FALSE);
279 image_read_ahead_start(imd);
282 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
285 ColorManProfileType input_type;
286 ColorManProfileType screen_type;
287 const gchar *input_file;
288 const gchar *screen_file;
289 ExifItem *item = NULL;
291 if (imd->cm) return FALSE;
293 if (imd->color_profile_input >= 1 &&
294 imd->color_profile_input <= COLOR_PROFILE_INPUTS)
298 n = imd->color_profile_input - 1;
299 if (!color_profile_input_file[n]) return FALSE;
301 input_type = COLOR_PROFILE_FILE;
302 input_file = color_profile_input_file[n];
304 else if (imd->color_profile_input == 0)
306 input_type = COLOR_PROFILE_SRGB;
314 if (imd->color_profile_screen == 1 &&
315 color_profile_screen_file)
317 screen_type = COLOR_PROFILE_FILE;
318 screen_file = color_profile_screen_file;
320 else if (imd->color_profile_screen == 0)
322 screen_type = COLOR_PROFILE_SRGB;
330 if (imd->color_profile_use_image && exif)
332 item = exif_get_item(exif, "ColorProfile");
337 /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
338 if (exif_get_integer(exif, "ColorSpace", &cs) &&
341 input_type = COLOR_PROFILE_SRGB;
344 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
348 if (item && item->format == EXIF_FORMAT_UNDEFINED)
350 if (debug) printf("Found embedded color profile\n");
352 cm = color_man_new_embedded(imd, NULL,
353 item->data, item->data_len,
354 screen_type, screen_file,
355 image_post_process_color_cb, imd);
359 cm = color_man_new(imd, NULL,
360 input_type, input_file,
361 screen_type, screen_file,
362 image_post_process_color_cb, imd);
370 cm->incremental_sync = TRUE;
373 imd->cm = (gpointer)cm;
380 static void image_post_process(ImageWindow *imd, gint clamp)
382 ExifData *exif = NULL;
384 if (!image_get_pixbuf(imd)) return;
386 if (exif_rotate_enable ||
387 (imd->color_profile_enable && imd->color_profile_use_image) )
389 exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
392 if (exif_rotate_enable && exif)
396 if (exif_get_integer(exif, "Orientation", &orientation))
400 /* see http://jpegclub.org/exif_orientation.html
403 888888 888888 88 88 8888888888 88 88 8888888888
404 88 88 88 88 88 88 88 88 88 88 88 88
405 8888 8888 8888 8888 88 8888888888 8888888888 88
411 case EXIF_ORIENTATION_TOP_LEFT:
412 /* normal -- nothing to do */
415 case EXIF_ORIENTATION_TOP_RIGHT:
417 imd->delay_alter_type = ALTER_MIRROR;
419 case EXIF_ORIENTATION_BOTTOM_RIGHT:
421 imd->delay_alter_type = ALTER_ROTATE_180;
423 case EXIF_ORIENTATION_BOTTOM_LEFT:
425 imd->delay_alter_type = ALTER_FLIP;
427 case EXIF_ORIENTATION_LEFT_TOP:
428 /* not implemented -- too wacky to fix in one step */
431 case EXIF_ORIENTATION_RIGHT_TOP:
432 /* rotated -90 (270) */
433 imd->delay_alter_type = ALTER_ROTATE_90;
435 case EXIF_ORIENTATION_RIGHT_BOTTOM:
436 /* not implemented -- too wacky to fix in one step */
439 case EXIF_ORIENTATION_LEFT_BOTTOM:
441 imd->delay_alter_type = ALTER_ROTATE_90_CC;
444 /* The other values are out of range */
449 if (rotate) imd->state |= IMAGE_STATE_COLOR_ADJ;
453 if (imd->color_profile_enable)
455 if (!image_post_process_color(imd, 0, exif))
457 /* fixme: note error to user */
458 imd->state |= IMAGE_STATE_COLOR_ADJ;
462 if (!imd->cm) image_post_process_alter(imd, clamp);
468 *-------------------------------------------------------------------
469 * read ahead (prebuffer)
470 *-------------------------------------------------------------------
473 static void image_read_ahead_cancel(ImageWindow *imd)
475 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
477 image_loader_free(imd->read_ahead_il);
478 imd->read_ahead_il = NULL;
480 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
481 imd->read_ahead_pixbuf = NULL;
483 g_free(imd->read_ahead_path);
484 imd->read_ahead_path = NULL;
487 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
489 ImageWindow *imd = data;
491 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
493 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
494 if (imd->read_ahead_pixbuf)
496 g_object_ref(imd->read_ahead_pixbuf);
500 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
502 image_loader_free(imd->read_ahead_il);
503 imd->read_ahead_il = NULL;
505 image_complete_util(imd, TRUE);
508 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
510 /* we even treat errors as success, maybe at least some of the file was ok */
511 image_read_ahead_done_cb(il, data);
514 static void image_read_ahead_start(ImageWindow *imd)
516 /* already started ? */
517 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
519 /* still loading ?, do later */
520 if (imd->il || imd->cm) return;
522 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
524 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
526 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
527 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
529 image_read_ahead_cancel(imd);
530 image_complete_util(imd, TRUE);
534 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
536 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
538 image_read_ahead_cancel(imd);
540 imd->read_ahead_path = g_strdup(path);
542 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
544 image_read_ahead_start(imd);
548 *-------------------------------------------------------------------
550 *-------------------------------------------------------------------
553 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
555 g_free(imd->prev_path);
556 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
560 imd->prev_path = g_strdup(path);
562 g_object_ref(pixbuf);
563 imd->prev_pixbuf = pixbuf;
564 imd->prev_color_row = color_row;
568 imd->prev_path = NULL;
569 imd->prev_pixbuf = NULL;
570 imd->prev_color_row = -1;
573 if (debug) printf("post buffer set: %s\n", path);
576 static gint image_post_buffer_get(ImageWindow *imd)
580 if (imd->prev_pixbuf &&
581 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
583 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
584 if (imd->prev_color_row >= 0)
586 ExifData *exif = NULL;
588 if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
589 image_post_process_color(imd, imd->prev_color_row, exif);
599 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
600 imd->prev_pixbuf = NULL;
602 g_free(imd->prev_path);
603 imd->prev_path = NULL;
609 *-------------------------------------------------------------------
611 *-------------------------------------------------------------------
614 static void image_load_pixbuf_ready(ImageWindow *imd)
616 if (image_get_pixbuf(imd) || !imd->il) return;
618 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
621 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
623 ImageWindow *imd = data;
626 pr = (PixbufRenderer *)imd->pr;
628 if (imd->delay_flip &&
629 pr->pixbuf != image_loader_get_pixbuf(il))
634 if (!pr->pixbuf) image_load_pixbuf_ready(imd);
636 pixbuf_renderer_area_changed(pr, x, y, w, h);
639 static void image_load_done_cb(ImageLoader *il, gpointer data)
641 ImageWindow *imd = data;
643 if (debug) printf ("image done\n");
645 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
647 if (imd->delay_flip &&
648 image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
650 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
651 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
654 image_loader_free(imd->il);
657 image_post_process(imd, TRUE);
659 image_read_ahead_start(imd);
662 static void image_load_error_cb(ImageLoader *il, gpointer data)
664 if (debug) printf ("image error\n");
666 /* even on error handle it like it was done,
667 * since we have a pixbuf with _something_ */
669 image_load_done_cb(il, data);
672 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
673 static void image_load_buffer_throttle(ImageLoader *il)
675 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
677 /* Larger image files usually have larger chunks of data per pixel...
678 * So increase the buffer read size so that the rendering chunks called
682 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
686 /* this read ahead is located here merely for the callbacks, above */
688 static gint image_read_ahead_check(ImageWindow *imd)
690 if (!imd->read_ahead_path) return FALSE;
691 if (imd->il) return FALSE;
693 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
695 image_read_ahead_cancel(imd);
699 if (imd->read_ahead_il)
701 imd->il = imd->read_ahead_il;
702 imd->read_ahead_il = NULL;
704 /* override the old signals */
705 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
706 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
707 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
709 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
710 image_load_buffer_throttle(imd->il);
713 /* do this one directly (probably should add a set func) */
714 imd->il->func_done = image_load_done_cb;
716 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
718 if (!imd->delay_flip)
720 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
723 image_read_ahead_cancel(imd);
726 else if (imd->read_ahead_pixbuf)
728 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
729 g_object_unref(imd->read_ahead_pixbuf);
730 imd->read_ahead_pixbuf = NULL;
732 image_read_ahead_cancel(imd);
734 image_post_process(imd, FALSE);
738 image_read_ahead_cancel(imd);
742 static gint image_load_begin(ImageWindow *imd, const gchar *path)
744 if (debug) printf ("image begin \n");
746 if (imd->il) return FALSE;
748 imd->completed = FALSE;
749 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
751 if (image_post_buffer_get(imd))
753 if (debug) printf("from post buffer: %s\n", imd->image_path);
757 if (image_read_ahead_check(imd))
759 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
763 if (!imd->delay_flip && image_get_pixbuf(imd))
767 pr = PIXBUF_RENDERER(imd->pr);
768 if (pr->pixbuf) g_object_unref(pr->pixbuf);
772 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
774 imd->il = image_loader_new(path);
776 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
777 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
778 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
780 if (!image_loader_start(imd->il, image_load_done_cb, imd))
782 if (debug) printf("image start error\n");
784 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
786 image_loader_free(imd->il);
789 image_complete_util(imd, FALSE);
794 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
795 image_load_buffer_throttle(imd->il);
798 if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
803 static void image_reset(ImageWindow *imd)
805 /* stops anything currently being done */
807 if (debug) printf("image reset\n");
809 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
811 image_loader_free(imd->il);
814 color_man_free((ColorMan *)imd->cm);
817 imd->delay_alter_type = ALTER_NONE;
819 imd->state = IMAGE_STATE_NONE;
823 *-------------------------------------------------------------------
825 *-------------------------------------------------------------------
828 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
832 if (imd->image_path && isfile(imd->image_path))
836 pr = PIXBUF_RENDERER(imd->pr);
837 pr->zoom = zoom; /* store the zoom, needed by the loader */
839 if (image_load_begin(imd, imd->image_path))
841 imd->unknown = FALSE;
847 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
848 image_change_pixbuf(imd, pixbuf, zoom);
849 g_object_unref(pixbuf);
853 imd->size = filesize(imd->image_path);
854 imd->mtime = filetime(imd->image_path);
862 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
863 image_change_pixbuf(imd, pixbuf, zoom);
864 g_object_unref(pixbuf);
865 imd->mtime = filetime(imd->image_path);
869 image_change_pixbuf(imd, NULL, zoom);
876 image_update_util(imd);
879 static void image_change_real(ImageWindow *imd, const gchar *path,
880 CollectionData *cd, CollectInfo *info, gdouble zoom)
883 GdkPixbuf *prev_pixbuf = NULL;
884 gchar *prev_path = NULL;
885 gint prev_clear = FALSE;
886 gint prev_color_row = -1;
888 imd->collection = cd;
889 imd->collection_info = info;
891 pixbuf = image_get_pixbuf(imd);
893 if (enable_read_ahead && imd->image_path && pixbuf)
897 /* current image is not finished */
902 prev_path = g_strdup(imd->image_path);
903 prev_pixbuf = pixbuf;
904 g_object_ref(prev_pixbuf);
910 cm = (ColorMan *)imd->cm;
911 prev_color_row = cm->row;
916 g_free(imd->image_path);
917 imd->image_path = g_strdup(path);
918 imd->image_name = filename_from_path(imd->image_path);
920 image_change_complete(imd, zoom, TRUE);
924 image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
926 g_object_unref(prev_pixbuf);
930 image_post_buffer_set(imd, NULL, NULL, -1);
933 image_update_title(imd);
938 *-------------------------------------------------------------------
940 *-------------------------------------------------------------------
943 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
947 widget = imd->widget;
948 if (!widget->window) return;
952 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
953 area, widget, "image_window",
954 widget->allocation.x, widget->allocation.y,
955 widget->allocation.width - 1, widget->allocation.height - 1);
959 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
960 area, widget, "image_window",
961 widget->allocation.x, widget->allocation.y,
962 widget->allocation.width - 1, widget->allocation.height - 1);
966 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
968 ImageWindow *imd = data;
970 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
974 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
976 ImageWindow *imd = data;
978 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
979 image_focus_paint(imd, TRUE, NULL);
984 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
986 ImageWindow *imd = data;
988 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
989 image_focus_paint(imd, FALSE, NULL);
994 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
995 gint relative, gint always)
997 return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1000 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1002 pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1005 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1007 return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1010 void image_overlay_remove(ImageWindow *imd, gint id)
1012 pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1015 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1017 ImageWindow *imd = data;
1019 if (imd->func_scroll &&
1020 event && event->type == GDK_SCROLL)
1022 imd->func_scroll(imd, event->direction, event->time,
1023 event->x, event->y, event->state, imd->data_scroll);
1031 *-------------------------------------------------------------------
1033 *-------------------------------------------------------------------
1036 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1037 const gchar *title, const gchar *title_right, gint show_zoom)
1039 imd->top_window = window;
1041 imd->title = g_strdup(title);
1042 g_free(imd->title_right);
1043 imd->title_right = g_strdup(title_right);
1044 imd->title_show_zoom = show_zoom;
1046 if (!fit_window) window = NULL;
1048 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1050 image_update_title(imd);
1053 void image_set_update_func(ImageWindow *imd,
1054 void (*func)(ImageWindow *imd, gpointer data),
1057 imd->func_update = func;
1058 imd->data_update = data;
1061 void image_set_complete_func(ImageWindow *imd,
1062 void (*func)(ImageWindow *, gint preload, gpointer),
1065 imd->func_complete = func;
1066 imd->data_complete = data;
1069 void image_set_new_func(ImageWindow *imd,
1070 void (*func)(ImageWindow *, gpointer),
1073 imd->func_new = func;
1074 imd->data_new = data;
1078 void image_set_button_func(ImageWindow *imd,
1079 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1082 imd->func_button = func;
1083 imd->data_button = data;
1086 void image_set_scroll_func(ImageWindow *imd,
1087 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1090 imd->func_scroll = func;
1091 imd->data_scroll = data;
1094 void image_set_scroll_notify_func(ImageWindow *imd,
1095 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1098 imd->func_scroll_notify = func;
1099 imd->data_scroll_notify = data;
1104 const gchar *image_get_path(ImageWindow *imd)
1106 return imd->image_path;
1109 const gchar *image_get_name(ImageWindow *imd)
1111 return imd->image_name;
1114 /* merely changes path string, does not change the image! */
1115 void image_set_path(ImageWindow *imd, const gchar *newpath)
1117 g_free(imd->image_path);
1118 imd->image_path = g_strdup(newpath);
1119 imd->image_name = filename_from_path(imd->image_path);
1121 image_update_title(imd);
1122 image_new_util(imd);
1125 /* load a new image */
1127 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
1129 if (imd->image_path == path ||
1130 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
1132 image_change_real(imd, path, NULL, NULL, zoom);
1135 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1137 return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1140 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1142 pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1143 image_new_util(imd);
1146 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1148 if (!cd || !info || !g_list_find(cd->list, info)) return;
1150 image_change_real(imd, info->path, cd, info, zoom);
1153 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1155 if (collection_to_number(imd->collection) >= 0)
1157 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1159 if (info) *info = imd->collection_info;
1163 if (info) *info = NULL;
1165 return imd->collection;
1168 if (info) *info = NULL;
1172 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1174 /* change data for the callbacks directly */
1176 il->data_area_ready = data;
1177 il->data_error = data;
1178 il->data_done = data;
1179 il->data_percent = data;
1182 /* this is more like a move function
1183 * it moves most data from source to imd
1185 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1187 if (imd == source) return;
1189 imd->unknown = source->unknown;
1191 imd->collection = source->collection;
1192 imd->collection_info = source->collection_info;
1193 imd->size = source->size;
1194 imd->mtime = source->mtime;
1196 image_set_path(imd, image_get_path(source));
1198 image_loader_free(imd->il);
1203 imd->il = source->il;
1206 image_loader_sync_data(imd->il, imd);
1208 imd->delay_alter_type = source->delay_alter_type;
1209 source->delay_alter_type = ALTER_NONE;
1212 imd->color_profile_enable = source->color_profile_enable;
1213 imd->color_profile_input = source->color_profile_input;
1214 imd->color_profile_screen = source->color_profile_screen;
1215 imd->color_profile_use_image = source->color_profile_use_image;
1216 color_man_free((ColorMan *)imd->cm);
1222 imd->cm = source->cm;
1225 cm = (ColorMan *)imd->cm;
1227 cm->func_done_data = imd;
1230 image_loader_free(imd->read_ahead_il);
1231 imd->read_ahead_il = source->read_ahead_il;
1232 source->read_ahead_il = NULL;
1233 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1235 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1236 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1237 source->read_ahead_pixbuf = NULL;
1239 g_free(imd->read_ahead_path);
1240 imd->read_ahead_path = source->read_ahead_path;
1241 source->read_ahead_path = NULL;
1243 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1244 imd->prev_pixbuf = source->prev_pixbuf;
1245 source->prev_pixbuf = NULL;
1246 imd->prev_color_row = source->prev_color_row;
1247 source->prev_color_row = -1;
1249 g_free(imd->prev_path);
1250 imd->prev_path = source->prev_path;
1251 source->prev_path = NULL;
1253 imd->completed = source->completed;
1254 imd->state = source->state;
1255 source->state = IMAGE_STATE_NONE;
1257 pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1262 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1264 pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1267 void image_reload(ImageWindow *imd)
1269 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1271 image_change_complete(imd, image_zoom_get(imd), FALSE);
1274 void image_scroll(ImageWindow *imd, gint x, gint y)
1276 pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1279 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1280 gdouble x_align, gdouble y_align)
1282 pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1285 void image_alter(ImageWindow *imd, AlterType type)
1287 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1289 if (imd->il || imd->cm)
1291 /* still loading, wait till done */
1292 imd->delay_alter_type = type;
1293 imd->state |= IMAGE_STATE_ROTATE_USER;
1295 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1297 imd->state &= ~IMAGE_STATE_ROTATE_AUTO;
1302 image_alter_real(imd, type, TRUE);
1305 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1307 pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1310 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1312 pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1315 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1317 pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1320 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1322 pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1325 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1331 pr = (PixbufRenderer *)imd->pr;
1333 if (!pixbuf_renderer_get_pixbuf(pr) ||
1334 !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1338 zoom = (gdouble)pr->window_height / height;
1342 zoom = (gdouble)pr->window_width / width;
1347 zoom = 0.0 - 1.0 / zoom;
1350 pixbuf_renderer_zoom_set(pr, zoom);
1353 gdouble image_zoom_get(ImageWindow *imd)
1355 return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1358 gdouble image_zoom_get_real(ImageWindow *imd)
1360 return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1363 gchar *image_zoom_get_as_text(ImageWindow *imd)
1371 gchar *approx = " ";
1373 zoom = image_zoom_get(imd);
1374 scale = image_zoom_get_real(imd);
1380 else if (zoom < 0.0)
1384 else if (zoom == 0.0 && scale != 0.0)
1397 if (rint(l) != l) pl = 1;
1398 if (rint(r) != r) pr = 1;
1400 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1403 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1407 if (mode == ZOOM_RESET_ORIGINAL)
1411 else if (mode == ZOOM_RESET_FIT_WINDOW)
1419 zoom = image_zoom_get(imd);
1432 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1434 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1438 image_read_ahead_set(imd, path);
1442 image_read_ahead_cancel(imd);
1446 static gint image_auto_refresh_cb(gpointer data)
1448 ImageWindow *imd = data;
1451 if (!imd || !image_get_pixbuf(imd) ||
1452 imd->il || !imd->image_path ||
1453 !update_on_time_change) return TRUE;
1455 newtime = filetime(imd->image_path);
1456 if (newtime > 0 && newtime != imd->mtime)
1458 imd->mtime = newtime;
1465 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1467 void image_auto_refresh(ImageWindow *imd, gint interval)
1470 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1472 if (imd->auto_refresh_id > -1)
1474 g_source_remove(imd->auto_refresh_id);
1475 imd->auto_refresh_id = -1;
1476 imd->auto_refresh_interval = -1;
1479 if (interval < 0) return;
1481 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1483 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1484 imd->auto_refresh_interval = interval;
1487 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1489 imd->top_window_sync = allow_sync;
1491 g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1494 void image_background_set_black(ImageWindow *imd, gint black)
1496 pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1499 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1501 pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1504 void image_color_profile_set(ImageWindow *imd,
1505 gint input_type, gint screen_type,
1510 if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1511 screen_type < 0 || screen_type > 1)
1516 imd->color_profile_input = input_type;
1517 imd->color_profile_screen = screen_type;
1518 imd->color_profile_use_image = use_image;
1521 gint image_color_profile_get(ImageWindow *imd,
1522 gint *input_type, gint *screen_type,
1525 if (!imd) return FALSE;
1527 if (input_type) *input_type = imd->color_profile_input;
1528 if (screen_type) *screen_type = imd->color_profile_screen;
1529 if (use_image) *use_image = imd->color_profile_use_image;
1534 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1538 if (imd->color_profile_enable == enable) return;
1540 imd->color_profile_enable = enable;
1543 gint image_color_profile_get_use(ImageWindow *imd)
1545 if (!imd) return FALSE;
1547 return imd->color_profile_enable;
1550 void image_set_delay_flip(ImageWindow *imd, gint delay)
1553 imd->delay_flip == delay) return;
1555 imd->delay_flip = delay;
1557 g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1559 if (!imd->delay_flip && imd->il)
1563 pr = PIXBUF_RENDERER(imd->pr);
1564 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1567 image_load_pixbuf_ready(imd);
1571 void image_to_root_window(ImageWindow *imd, gint scaled)
1574 GdkWindow *rootwindow;
1582 pixbuf = image_get_pixbuf(imd);
1583 if (!pixbuf) return;
1585 screen = gtk_widget_get_screen(imd->widget);
1586 rootwindow = gdk_screen_get_root_window(screen);
1587 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1591 width = gdk_screen_width();
1592 height = gdk_screen_height();
1596 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1599 pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1601 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1602 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1603 gdk_window_clear(rootwindow);
1605 g_object_unref(pixmap);
1611 *-------------------------------------------------------------------
1613 *-------------------------------------------------------------------
1616 static void image_options_set(ImageWindow *imd)
1618 g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1619 "zoom_2pass", two_pass_zoom,
1620 "zoom_expand", zoom_to_fit_expands,
1621 "dither_quality", dither_quality,
1622 "scroll_reset", scroll_reset_method,
1623 "cache_display", tile_cache_max,
1624 "window_fit", (imd->top_window_sync && fit_window),
1625 "window_limit", limit_window_size,
1626 "window_limit_value", max_window_size,
1629 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1632 void image_options_sync(void)
1644 image_options_set(imd);
1649 *-------------------------------------------------------------------
1651 *-------------------------------------------------------------------
1654 static void image_free(ImageWindow *imd)
1656 image_list = g_list_remove(image_list, imd);
1660 image_read_ahead_cancel(imd);
1661 image_post_buffer_set(imd, NULL, NULL, -1);
1662 image_auto_refresh(imd, -1);
1664 g_free(imd->image_path);
1666 g_free(imd->title_right);
1671 static void image_destroy_cb(GtkObject *widget, gpointer data)
1673 ImageWindow *imd = data;
1677 ImageWindow *image_new(gint frame)
1681 imd = g_new0(ImageWindow, 1);
1683 imd->top_window = NULL;
1685 imd->title_right = NULL;
1686 imd->title_show_zoom = FALSE;
1688 imd->unknown = TRUE;
1690 imd->has_frame = frame;
1691 imd->top_window_sync = FALSE;
1693 imd->delay_alter_type = ALTER_NONE;
1695 imd->read_ahead_il = NULL;
1696 imd->read_ahead_pixbuf = NULL;
1697 imd->read_ahead_path = NULL;
1699 imd->completed = FALSE;
1700 imd->state = IMAGE_STATE_NONE;
1702 imd->color_profile_enable = FALSE;
1703 imd->color_profile_input = 0;
1704 imd->color_profile_screen = 0;
1705 imd->color_profile_use_image = FALSE;
1707 imd->auto_refresh_id = -1;
1708 imd->auto_refresh_interval = -1;
1710 imd->delay_flip = FALSE;
1712 imd->func_update = NULL;
1713 imd->func_complete = NULL;
1714 imd->func_tile_request = NULL;
1715 imd->func_tile_dispose = NULL;
1717 imd->func_button = NULL;
1718 imd->func_scroll = NULL;
1720 imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1722 image_options_set(imd);
1726 imd->widget = gtk_frame_new(NULL);
1727 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1728 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1729 gtk_widget_show(imd->pr);
1731 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1732 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1733 G_CALLBACK(image_focus_in_cb), imd);
1734 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1735 G_CALLBACK(image_focus_out_cb), imd);
1737 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1738 G_CALLBACK(image_focus_expose), imd);
1742 imd->widget = imd->pr;
1745 g_signal_connect(G_OBJECT(imd->pr), "clicked",
1746 G_CALLBACK(image_click_cb), imd);
1747 g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1748 G_CALLBACK(image_scroll_notify_cb), imd);
1750 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1751 G_CALLBACK(image_scroll_cb), imd);
1753 g_signal_connect(G_OBJECT(imd->pr), "destroy",
1754 G_CALLBACK(image_destroy_cb), imd);
1756 g_signal_connect(G_OBJECT(imd->pr), "zoom",
1757 G_CALLBACK(image_zoom_cb), imd);
1758 g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1759 G_CALLBACK(image_render_complete_cb), imd);
1761 image_list = g_list_append(image_list, imd);