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),
268 if (exif_rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
271 static void image_post_process_alter(ImageWindow *imd, gint clamp)
273 if (imd->delay_alter_type != ALTER_NONE)
275 image_alter_real(imd, imd->delay_alter_type, clamp);
279 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
281 ImageWindow *imd = data;
284 if (type == COLOR_RETURN_IMAGE_CHANGED)
286 if (cm == imd->cm) imd->cm = NULL;
291 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
293 image_post_process_alter(imd, FALSE);
295 image_read_ahead_start(imd);
298 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
301 ColorManProfileType input_type;
302 ColorManProfileType screen_type;
303 const gchar *input_file;
304 const gchar *screen_file;
305 ExifItem *item = NULL;
307 if (imd->cm) return FALSE;
309 if (imd->color_profile_input >= 1 &&
310 imd->color_profile_input <= COLOR_PROFILE_INPUTS)
314 n = imd->color_profile_input - 1;
315 if (!color_profile_input_file[n]) return FALSE;
317 input_type = COLOR_PROFILE_FILE;
318 input_file = color_profile_input_file[n];
320 else if (imd->color_profile_input == 0)
322 input_type = COLOR_PROFILE_SRGB;
330 if (imd->color_profile_screen == 1 &&
331 color_profile_screen_file)
333 screen_type = COLOR_PROFILE_FILE;
334 screen_file = color_profile_screen_file;
336 else if (imd->color_profile_screen == 0)
338 screen_type = COLOR_PROFILE_SRGB;
346 if (imd->color_profile_use_image && exif)
348 item = exif_get_item(exif, "ColorProfile");
353 /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
354 if (exif_get_integer(exif, "ColorSpace", &cs) &&
357 input_type = COLOR_PROFILE_SRGB;
360 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
364 if (item && item->format == EXIF_FORMAT_UNDEFINED)
366 if (debug) printf("Found embedded color profile\n");
368 cm = color_man_new_embedded(imd, NULL,
369 item->data, item->data_len,
370 screen_type, screen_file,
371 image_post_process_color_cb, imd);
375 cm = color_man_new(imd, NULL,
376 input_type, input_file,
377 screen_type, screen_file,
378 image_post_process_color_cb, imd);
386 cm->incremental_sync = TRUE;
389 imd->cm = (gpointer)cm;
396 static void image_post_process(ImageWindow *imd, gint clamp)
398 ExifData *exif = NULL;
400 if (!image_get_pixbuf(imd)) return;
402 if (exif_rotate_enable ||
403 (imd->color_profile_enable && imd->color_profile_use_image) )
405 exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
408 if (exif_rotate_enable && exif)
412 if (exif_get_integer(exif, "Orientation", &orientation))
416 /* see http://jpegclub.org/exif_orientation.html
419 888888 888888 88 88 8888888888 88 88 8888888888
420 88 88 88 88 88 88 88 88 88 88 88 88
421 8888 8888 8888 8888 88 8888888888 8888888888 88
427 case EXIF_ORIENTATION_TOP_LEFT:
428 /* normal -- nothing to do */
431 case EXIF_ORIENTATION_TOP_RIGHT:
433 imd->delay_alter_type = ALTER_MIRROR;
435 case EXIF_ORIENTATION_BOTTOM_RIGHT:
437 imd->delay_alter_type = ALTER_ROTATE_180;
439 case EXIF_ORIENTATION_BOTTOM_LEFT:
441 imd->delay_alter_type = ALTER_FLIP;
443 case EXIF_ORIENTATION_LEFT_TOP:
444 /* not implemented -- too wacky to fix in one step */
447 case EXIF_ORIENTATION_RIGHT_TOP:
448 /* rotated -90 (270) */
449 imd->delay_alter_type = ALTER_ROTATE_90;
451 case EXIF_ORIENTATION_RIGHT_BOTTOM:
452 /* not implemented -- too wacky to fix in one step */
455 case EXIF_ORIENTATION_LEFT_BOTTOM:
457 imd->delay_alter_type = ALTER_ROTATE_90_CC;
460 /* The other values are out of range */
465 if (rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
469 if (imd->color_profile_enable)
471 if (!image_post_process_color(imd, 0, exif))
473 /* fixme: note error to user */
474 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
478 if (!imd->cm) image_post_process_alter(imd, clamp);
484 *-------------------------------------------------------------------
485 * read ahead (prebuffer)
486 *-------------------------------------------------------------------
489 static void image_read_ahead_cancel(ImageWindow *imd)
491 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
493 image_loader_free(imd->read_ahead_il);
494 imd->read_ahead_il = NULL;
496 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
497 imd->read_ahead_pixbuf = NULL;
499 g_free(imd->read_ahead_path);
500 imd->read_ahead_path = NULL;
503 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
505 ImageWindow *imd = data;
507 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
509 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
510 if (imd->read_ahead_pixbuf)
512 g_object_ref(imd->read_ahead_pixbuf);
516 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
518 image_loader_free(imd->read_ahead_il);
519 imd->read_ahead_il = NULL;
521 image_complete_util(imd, TRUE);
524 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
526 /* we even treat errors as success, maybe at least some of the file was ok */
527 image_read_ahead_done_cb(il, data);
530 static void image_read_ahead_start(ImageWindow *imd)
532 /* already started ? */
533 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
535 /* still loading ?, do later */
536 if (imd->il || imd->cm) return;
538 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
540 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
542 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
543 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
545 image_read_ahead_cancel(imd);
546 image_complete_util(imd, TRUE);
550 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
552 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
554 image_read_ahead_cancel(imd);
556 imd->read_ahead_path = g_strdup(path);
558 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
560 image_read_ahead_start(imd);
564 *-------------------------------------------------------------------
566 *-------------------------------------------------------------------
569 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
571 g_free(imd->prev_path);
572 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
576 imd->prev_path = g_strdup(path);
578 g_object_ref(pixbuf);
579 imd->prev_pixbuf = pixbuf;
580 imd->prev_color_row = color_row;
584 imd->prev_path = NULL;
585 imd->prev_pixbuf = NULL;
586 imd->prev_color_row = -1;
589 if (debug) printf("post buffer set: %s\n", path);
592 static gint image_post_buffer_get(ImageWindow *imd)
596 if (imd->prev_pixbuf &&
597 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
599 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
600 if (imd->prev_color_row >= 0)
602 ExifData *exif = NULL;
604 if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
605 image_post_process_color(imd, imd->prev_color_row, exif);
615 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
616 imd->prev_pixbuf = NULL;
618 g_free(imd->prev_path);
619 imd->prev_path = NULL;
625 *-------------------------------------------------------------------
627 *-------------------------------------------------------------------
630 static void image_load_pixbuf_ready(ImageWindow *imd)
632 if (image_get_pixbuf(imd) || !imd->il) return;
634 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
637 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
639 ImageWindow *imd = data;
642 pr = (PixbufRenderer *)imd->pr;
644 if (imd->delay_flip &&
645 pr->pixbuf != image_loader_get_pixbuf(il))
650 if (!pr->pixbuf) image_load_pixbuf_ready(imd);
652 pixbuf_renderer_area_changed(pr, x, y, w, h);
655 static void image_load_done_cb(ImageLoader *il, gpointer data)
657 ImageWindow *imd = data;
659 if (debug) printf ("image done\n");
661 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
662 image_state_unset(imd, IMAGE_STATE_LOADING);
664 if (imd->delay_flip &&
665 image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
667 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
668 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
671 image_loader_free(imd->il);
674 image_post_process(imd, TRUE);
676 image_read_ahead_start(imd);
679 static void image_load_error_cb(ImageLoader *il, gpointer data)
681 if (debug) printf ("image error\n");
683 /* even on error handle it like it was done,
684 * since we have a pixbuf with _something_ */
686 image_load_done_cb(il, data);
689 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
690 static void image_load_buffer_throttle(ImageLoader *il)
692 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
694 /* Larger image files usually have larger chunks of data per pixel...
695 * So increase the buffer read size so that the rendering chunks called
699 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
703 /* this read ahead is located here merely for the callbacks, above */
705 static gint image_read_ahead_check(ImageWindow *imd)
707 if (!imd->read_ahead_path) return FALSE;
708 if (imd->il) return FALSE;
710 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
712 image_read_ahead_cancel(imd);
716 if (imd->read_ahead_il)
718 imd->il = imd->read_ahead_il;
719 imd->read_ahead_il = NULL;
721 /* override the old signals */
722 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
723 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
724 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
726 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
727 image_load_buffer_throttle(imd->il);
730 /* do this one directly (probably should add a set func) */
731 imd->il->func_done = image_load_done_cb;
733 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
734 image_state_set(imd, IMAGE_STATE_LOADING);
736 if (!imd->delay_flip)
738 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
741 image_read_ahead_cancel(imd);
744 else if (imd->read_ahead_pixbuf)
746 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
747 g_object_unref(imd->read_ahead_pixbuf);
748 imd->read_ahead_pixbuf = NULL;
750 image_read_ahead_cancel(imd);
752 image_post_process(imd, FALSE);
756 image_read_ahead_cancel(imd);
760 static gint image_load_begin(ImageWindow *imd, const gchar *path)
762 if (debug) printf ("image begin \n");
764 if (imd->il) return FALSE;
766 imd->completed = FALSE;
767 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
769 if (image_post_buffer_get(imd))
771 if (debug) printf("from post buffer: %s\n", imd->image_path);
775 if (image_read_ahead_check(imd))
777 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
781 if (!imd->delay_flip && image_get_pixbuf(imd))
785 pr = PIXBUF_RENDERER(imd->pr);
786 if (pr->pixbuf) g_object_unref(pr->pixbuf);
790 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
792 imd->il = image_loader_new(path);
794 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
795 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
796 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
798 if (!image_loader_start(imd->il, image_load_done_cb, imd))
800 if (debug) printf("image start error\n");
802 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
804 image_loader_free(imd->il);
807 image_complete_util(imd, FALSE);
812 image_state_set(imd, IMAGE_STATE_LOADING);
814 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
815 image_load_buffer_throttle(imd->il);
818 if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
823 static void image_reset(ImageWindow *imd)
825 /* stops anything currently being done */
827 if (debug) printf("image reset\n");
829 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
831 image_loader_free(imd->il);
834 color_man_free((ColorMan *)imd->cm);
837 imd->delay_alter_type = ALTER_NONE;
839 image_state_set(imd, IMAGE_STATE_NONE);
843 *-------------------------------------------------------------------
845 *-------------------------------------------------------------------
848 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
852 if (imd->image_path && isfile(imd->image_path))
856 pr = PIXBUF_RENDERER(imd->pr);
857 pr->zoom = zoom; /* store the zoom, needed by the loader */
859 if (image_load_begin(imd, imd->image_path))
861 imd->unknown = FALSE;
867 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
868 image_change_pixbuf(imd, pixbuf, zoom);
869 g_object_unref(pixbuf);
873 imd->size = filesize(imd->image_path);
874 imd->mtime = filetime(imd->image_path);
882 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
883 image_change_pixbuf(imd, pixbuf, zoom);
884 g_object_unref(pixbuf);
885 imd->mtime = filetime(imd->image_path);
889 image_change_pixbuf(imd, NULL, zoom);
896 image_update_util(imd);
899 static void image_change_real(ImageWindow *imd, const gchar *path,
900 CollectionData *cd, CollectInfo *info, gdouble zoom)
903 GdkPixbuf *prev_pixbuf = NULL;
904 gchar *prev_path = NULL;
905 gint prev_clear = FALSE;
906 gint prev_color_row = -1;
908 imd->collection = cd;
909 imd->collection_info = info;
911 pixbuf = image_get_pixbuf(imd);
913 if (enable_read_ahead && imd->image_path && pixbuf)
917 /* current image is not finished */
922 prev_path = g_strdup(imd->image_path);
923 prev_pixbuf = pixbuf;
924 g_object_ref(prev_pixbuf);
930 cm = (ColorMan *)imd->cm;
931 prev_color_row = cm->row;
936 g_free(imd->image_path);
937 imd->image_path = g_strdup(path);
938 imd->image_name = filename_from_path(imd->image_path);
940 image_change_complete(imd, zoom, TRUE);
944 image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
946 g_object_unref(prev_pixbuf);
950 image_post_buffer_set(imd, NULL, NULL, -1);
953 image_update_title(imd);
954 image_state_set(imd, IMAGE_STATE_IMAGE);
958 *-------------------------------------------------------------------
960 *-------------------------------------------------------------------
963 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
967 widget = imd->widget;
968 if (!widget->window) return;
972 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
973 area, widget, "image_window",
974 widget->allocation.x, widget->allocation.y,
975 widget->allocation.width - 1, widget->allocation.height - 1);
979 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
980 area, widget, "image_window",
981 widget->allocation.x, widget->allocation.y,
982 widget->allocation.width - 1, widget->allocation.height - 1);
986 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
988 ImageWindow *imd = data;
990 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
994 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
996 ImageWindow *imd = data;
998 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
999 image_focus_paint(imd, TRUE, NULL);
1004 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1006 ImageWindow *imd = data;
1008 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1009 image_focus_paint(imd, FALSE, NULL);
1014 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
1015 gint relative, gint always)
1017 return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1020 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1022 pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1025 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1027 return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1030 void image_overlay_remove(ImageWindow *imd, gint id)
1032 pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1035 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1037 ImageWindow *imd = data;
1039 if (imd->func_scroll &&
1040 event && event->type == GDK_SCROLL)
1042 imd->func_scroll(imd, event->direction, event->time,
1043 event->x, event->y, event->state, imd->data_scroll);
1051 *-------------------------------------------------------------------
1053 *-------------------------------------------------------------------
1056 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1057 const gchar *title, const gchar *title_right, gint show_zoom)
1059 imd->top_window = window;
1061 imd->title = g_strdup(title);
1062 g_free(imd->title_right);
1063 imd->title_right = g_strdup(title_right);
1064 imd->title_show_zoom = show_zoom;
1066 if (!fit_window) window = NULL;
1068 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1070 image_update_title(imd);
1073 void image_set_update_func(ImageWindow *imd,
1074 void (*func)(ImageWindow *imd, gpointer data),
1077 imd->func_update = func;
1078 imd->data_update = data;
1081 void image_set_complete_func(ImageWindow *imd,
1082 void (*func)(ImageWindow *imd, gint preload, gpointer data),
1085 imd->func_complete = func;
1086 imd->data_complete = data;
1089 void image_set_state_func(ImageWindow *imd,
1090 void (*func)(ImageWindow *imd, ImageState state, gpointer data),
1093 imd->func_state = func;
1094 imd->data_state = data;
1098 void image_set_button_func(ImageWindow *imd,
1099 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1102 imd->func_button = func;
1103 imd->data_button = data;
1106 void image_set_scroll_func(ImageWindow *imd,
1107 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1110 imd->func_scroll = func;
1111 imd->data_scroll = data;
1114 void image_set_scroll_notify_func(ImageWindow *imd,
1115 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1118 imd->func_scroll_notify = func;
1119 imd->data_scroll_notify = data;
1124 const gchar *image_get_path(ImageWindow *imd)
1126 return imd->image_path;
1129 const gchar *image_get_name(ImageWindow *imd)
1131 return imd->image_name;
1134 /* merely changes path string, does not change the image! */
1135 void image_set_path(ImageWindow *imd, const gchar *newpath)
1137 g_free(imd->image_path);
1138 imd->image_path = g_strdup(newpath);
1139 imd->image_name = filename_from_path(imd->image_path);
1141 image_update_title(imd);
1142 image_state_set(imd, IMAGE_STATE_IMAGE);
1145 /* load a new image */
1147 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
1149 if (imd->image_path == path ||
1150 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
1152 image_change_real(imd, path, NULL, NULL, zoom);
1155 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1157 return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1160 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1162 pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1163 image_state_set(imd, IMAGE_STATE_IMAGE);
1166 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1168 if (!cd || !info || !g_list_find(cd->list, info)) return;
1170 image_change_real(imd, info->path, cd, info, zoom);
1173 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1175 if (collection_to_number(imd->collection) >= 0)
1177 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1179 if (info) *info = imd->collection_info;
1183 if (info) *info = NULL;
1185 return imd->collection;
1188 if (info) *info = NULL;
1192 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1194 /* change data for the callbacks directly */
1196 il->data_area_ready = data;
1197 il->data_error = data;
1198 il->data_done = data;
1199 il->data_percent = data;
1202 /* this is more like a move function
1203 * it moves most data from source to imd
1205 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1207 if (imd == source) return;
1209 imd->unknown = source->unknown;
1211 imd->collection = source->collection;
1212 imd->collection_info = source->collection_info;
1213 imd->size = source->size;
1214 imd->mtime = source->mtime;
1216 image_set_path(imd, image_get_path(source));
1218 image_loader_free(imd->il);
1223 imd->il = source->il;
1226 image_loader_sync_data(imd->il, imd);
1228 imd->delay_alter_type = source->delay_alter_type;
1229 source->delay_alter_type = ALTER_NONE;
1232 imd->color_profile_enable = source->color_profile_enable;
1233 imd->color_profile_input = source->color_profile_input;
1234 imd->color_profile_screen = source->color_profile_screen;
1235 imd->color_profile_use_image = source->color_profile_use_image;
1236 color_man_free((ColorMan *)imd->cm);
1242 imd->cm = source->cm;
1245 cm = (ColorMan *)imd->cm;
1247 cm->func_done_data = imd;
1250 image_loader_free(imd->read_ahead_il);
1251 imd->read_ahead_il = source->read_ahead_il;
1252 source->read_ahead_il = NULL;
1253 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1255 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1256 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1257 source->read_ahead_pixbuf = NULL;
1259 g_free(imd->read_ahead_path);
1260 imd->read_ahead_path = source->read_ahead_path;
1261 source->read_ahead_path = NULL;
1263 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1264 imd->prev_pixbuf = source->prev_pixbuf;
1265 source->prev_pixbuf = NULL;
1266 imd->prev_color_row = source->prev_color_row;
1267 source->prev_color_row = -1;
1269 g_free(imd->prev_path);
1270 imd->prev_path = source->prev_path;
1271 source->prev_path = NULL;
1273 imd->completed = source->completed;
1274 imd->state = source->state;
1275 source->state = IMAGE_STATE_NONE;
1277 pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1282 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1284 pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1287 void image_reload(ImageWindow *imd)
1289 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1291 image_change_complete(imd, image_zoom_get(imd), FALSE);
1294 void image_scroll(ImageWindow *imd, gint x, gint y)
1296 pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1299 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1300 gdouble x_align, gdouble y_align)
1302 pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1305 void image_alter(ImageWindow *imd, AlterType type)
1307 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1309 if (imd->il || imd->cm)
1311 /* still loading, wait till done */
1312 imd->delay_alter_type = type;
1313 image_state_set(imd, IMAGE_STATE_ROTATE_USER);
1315 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1317 image_state_unset(imd, IMAGE_STATE_ROTATE_AUTO);
1322 image_alter_real(imd, type, TRUE);
1325 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1327 pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1330 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1332 pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1335 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1337 pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1340 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1342 pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1345 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1351 pr = (PixbufRenderer *)imd->pr;
1353 if (!pixbuf_renderer_get_pixbuf(pr) ||
1354 !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1358 zoom = (gdouble)pr->window_height / height;
1362 zoom = (gdouble)pr->window_width / width;
1367 zoom = 0.0 - 1.0 / zoom;
1370 pixbuf_renderer_zoom_set(pr, zoom);
1373 gdouble image_zoom_get(ImageWindow *imd)
1375 return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1378 gdouble image_zoom_get_real(ImageWindow *imd)
1380 return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1383 gchar *image_zoom_get_as_text(ImageWindow *imd)
1391 gchar *approx = " ";
1393 zoom = image_zoom_get(imd);
1394 scale = image_zoom_get_real(imd);
1400 else if (zoom < 0.0)
1404 else if (zoom == 0.0 && scale != 0.0)
1417 if (rint(l) != l) pl = 1;
1418 if (rint(r) != r) pr = 1;
1420 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1423 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1427 if (mode == ZOOM_RESET_ORIGINAL)
1431 else if (mode == ZOOM_RESET_FIT_WINDOW)
1439 zoom = image_zoom_get(imd);
1452 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1454 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1458 image_read_ahead_set(imd, path);
1462 image_read_ahead_cancel(imd);
1466 static gint image_auto_refresh_cb(gpointer data)
1468 ImageWindow *imd = data;
1471 if (!imd || !image_get_pixbuf(imd) ||
1472 imd->il || !imd->image_path ||
1473 !update_on_time_change) return TRUE;
1475 newtime = filetime(imd->image_path);
1476 if (newtime > 0 && newtime != imd->mtime)
1478 imd->mtime = newtime;
1485 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1487 void image_auto_refresh(ImageWindow *imd, gint interval)
1490 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1492 if (imd->auto_refresh_id > -1)
1494 g_source_remove(imd->auto_refresh_id);
1495 imd->auto_refresh_id = -1;
1496 imd->auto_refresh_interval = -1;
1499 if (interval < 0) return;
1501 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1503 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1504 imd->auto_refresh_interval = interval;
1507 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1509 imd->top_window_sync = allow_sync;
1511 g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1514 void image_background_set_black(ImageWindow *imd, gint black)
1516 pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1519 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1521 pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1524 void image_color_profile_set(ImageWindow *imd,
1525 gint input_type, gint screen_type,
1530 if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1531 screen_type < 0 || screen_type > 1)
1536 imd->color_profile_input = input_type;
1537 imd->color_profile_screen = screen_type;
1538 imd->color_profile_use_image = use_image;
1541 gint image_color_profile_get(ImageWindow *imd,
1542 gint *input_type, gint *screen_type,
1545 if (!imd) return FALSE;
1547 if (input_type) *input_type = imd->color_profile_input;
1548 if (screen_type) *screen_type = imd->color_profile_screen;
1549 if (use_image) *use_image = imd->color_profile_use_image;
1554 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1558 if (imd->color_profile_enable == enable) return;
1560 imd->color_profile_enable = enable;
1563 gint image_color_profile_get_use(ImageWindow *imd)
1565 if (!imd) return FALSE;
1567 return imd->color_profile_enable;
1570 void image_set_delay_flip(ImageWindow *imd, gint delay)
1573 imd->delay_flip == delay) return;
1575 imd->delay_flip = delay;
1577 g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1579 if (!imd->delay_flip && imd->il)
1583 pr = PIXBUF_RENDERER(imd->pr);
1584 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1587 image_load_pixbuf_ready(imd);
1591 void image_to_root_window(ImageWindow *imd, gint scaled)
1594 GdkWindow *rootwindow;
1602 pixbuf = image_get_pixbuf(imd);
1603 if (!pixbuf) return;
1605 screen = gtk_widget_get_screen(imd->widget);
1606 rootwindow = gdk_screen_get_root_window(screen);
1607 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1611 width = gdk_screen_width();
1612 height = gdk_screen_height();
1616 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1619 pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1621 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1622 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1623 gdk_window_clear(rootwindow);
1625 g_object_unref(pixmap);
1631 *-------------------------------------------------------------------
1633 *-------------------------------------------------------------------
1636 static void image_options_set(ImageWindow *imd)
1638 g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1639 "zoom_2pass", two_pass_zoom,
1640 "zoom_expand", zoom_to_fit_expands,
1641 "dither_quality", dither_quality,
1642 "scroll_reset", scroll_reset_method,
1643 "cache_display", tile_cache_max,
1644 "window_fit", (imd->top_window_sync && fit_window),
1645 "window_limit", limit_window_size,
1646 "window_limit_value", max_window_size,
1649 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1652 void image_options_sync(void)
1664 image_options_set(imd);
1669 *-------------------------------------------------------------------
1671 *-------------------------------------------------------------------
1674 static void image_free(ImageWindow *imd)
1676 image_list = g_list_remove(image_list, imd);
1680 image_read_ahead_cancel(imd);
1681 image_post_buffer_set(imd, NULL, NULL, -1);
1682 image_auto_refresh(imd, -1);
1684 g_free(imd->image_path);
1686 g_free(imd->title_right);
1691 static void image_destroy_cb(GtkObject *widget, gpointer data)
1693 ImageWindow *imd = data;
1697 ImageWindow *image_new(gint frame)
1701 imd = g_new0(ImageWindow, 1);
1703 imd->top_window = NULL;
1705 imd->title_right = NULL;
1706 imd->title_show_zoom = FALSE;
1708 imd->unknown = TRUE;
1710 imd->has_frame = frame;
1711 imd->top_window_sync = FALSE;
1713 imd->delay_alter_type = ALTER_NONE;
1715 imd->read_ahead_il = NULL;
1716 imd->read_ahead_pixbuf = NULL;
1717 imd->read_ahead_path = NULL;
1719 imd->completed = FALSE;
1720 imd->state = IMAGE_STATE_NONE;
1722 imd->color_profile_enable = FALSE;
1723 imd->color_profile_input = 0;
1724 imd->color_profile_screen = 0;
1725 imd->color_profile_use_image = FALSE;
1727 imd->auto_refresh_id = -1;
1728 imd->auto_refresh_interval = -1;
1730 imd->delay_flip = FALSE;
1732 imd->func_update = NULL;
1733 imd->func_complete = NULL;
1734 imd->func_tile_request = NULL;
1735 imd->func_tile_dispose = NULL;
1737 imd->func_button = NULL;
1738 imd->func_scroll = NULL;
1740 imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1742 image_options_set(imd);
1746 imd->widget = gtk_frame_new(NULL);
1747 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1748 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1749 gtk_widget_show(imd->pr);
1751 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1752 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1753 G_CALLBACK(image_focus_in_cb), imd);
1754 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1755 G_CALLBACK(image_focus_out_cb), imd);
1757 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1758 G_CALLBACK(image_focus_expose), imd);
1762 imd->widget = imd->pr;
1765 g_signal_connect(G_OBJECT(imd->pr), "clicked",
1766 G_CALLBACK(image_click_cb), imd);
1767 g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1768 G_CALLBACK(image_scroll_notify_cb), imd);
1770 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1771 G_CALLBACK(image_scroll_cb), imd);
1773 g_signal_connect(G_OBJECT(imd->pr), "destroy",
1774 G_CALLBACK(image_destroy_cb), imd);
1776 g_signal_connect(G_OBJECT(imd->pr), "zoom",
1777 G_CALLBACK(image_zoom_cb), imd);
1778 g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1779 G_CALLBACK(image_render_complete_cb), imd);
1781 image_list = g_list_append(image_list, imd);