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);
367 if (start_row > 0) cm->row = start_row;
369 imd->cm = (gpointer)cm;
376 static void image_post_process(ImageWindow *imd, gint clamp)
378 ExifData *exif = NULL;
380 if (!image_get_pixbuf(imd)) return;
382 if (exif_rotate_enable ||
383 (imd->color_profile_enable && imd->color_profile_use_image) )
385 exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
388 if (exif_rotate_enable && exif)
392 if (exif_get_integer(exif, "Orientation", &orientation))
396 /* see http://jpegclub.org/exif_orientation.html
399 888888 888888 88 88 8888888888 88 88 8888888888
400 88 88 88 88 88 88 88 88 88 88 88 88
401 8888 8888 8888 8888 88 8888888888 8888888888 88
407 case EXIF_ORIENTATION_TOP_LEFT:
408 /* normal -- nothing to do */
411 case EXIF_ORIENTATION_TOP_RIGHT:
413 imd->delay_alter_type = ALTER_MIRROR;
415 case EXIF_ORIENTATION_BOTTOM_RIGHT:
417 imd->delay_alter_type = ALTER_ROTATE_180;
419 case EXIF_ORIENTATION_BOTTOM_LEFT:
421 imd->delay_alter_type = ALTER_FLIP;
423 case EXIF_ORIENTATION_LEFT_TOP:
424 /* not implemented -- too wacky to fix in one step */
427 case EXIF_ORIENTATION_RIGHT_TOP:
428 /* rotated -90 (270) */
429 imd->delay_alter_type = ALTER_ROTATE_90;
431 case EXIF_ORIENTATION_RIGHT_BOTTOM:
432 /* not implemented -- too wacky to fix in one step */
435 case EXIF_ORIENTATION_LEFT_BOTTOM:
437 imd->delay_alter_type = ALTER_ROTATE_90_CC;
440 /* The other values are out of range */
445 if (rotate) imd->state |= IMAGE_STATE_COLOR_ADJ;
449 if (imd->color_profile_enable)
451 if (!image_post_process_color(imd, 0, exif))
453 /* fixme: note error to user */
454 imd->state |= IMAGE_STATE_COLOR_ADJ;
458 if (!imd->cm) image_post_process_alter(imd, clamp);
464 *-------------------------------------------------------------------
465 * read ahead (prebuffer)
466 *-------------------------------------------------------------------
469 static void image_read_ahead_cancel(ImageWindow *imd)
471 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
473 image_loader_free(imd->read_ahead_il);
474 imd->read_ahead_il = NULL;
476 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
477 imd->read_ahead_pixbuf = NULL;
479 g_free(imd->read_ahead_path);
480 imd->read_ahead_path = NULL;
483 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
485 ImageWindow *imd = data;
487 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
489 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
490 if (imd->read_ahead_pixbuf)
492 g_object_ref(imd->read_ahead_pixbuf);
496 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
498 image_loader_free(imd->read_ahead_il);
499 imd->read_ahead_il = NULL;
501 image_complete_util(imd, TRUE);
504 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
506 /* we even treat errors as success, maybe at least some of the file was ok */
507 image_read_ahead_done_cb(il, data);
510 static void image_read_ahead_start(ImageWindow *imd)
512 /* already started ? */
513 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
515 /* still loading ?, do later */
516 if (imd->il || imd->cm) return;
518 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
520 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
522 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
523 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
525 image_read_ahead_cancel(imd);
526 image_complete_util(imd, TRUE);
530 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
532 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
534 image_read_ahead_cancel(imd);
536 imd->read_ahead_path = g_strdup(path);
538 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
540 image_read_ahead_start(imd);
544 *-------------------------------------------------------------------
546 *-------------------------------------------------------------------
549 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
551 g_free(imd->prev_path);
552 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
556 imd->prev_path = g_strdup(path);
558 g_object_ref(pixbuf);
559 imd->prev_pixbuf = pixbuf;
560 imd->prev_color_row = color_row;
564 imd->prev_path = NULL;
565 imd->prev_pixbuf = NULL;
566 imd->prev_color_row = -1;
569 if (debug) printf("post buffer set: %s\n", path);
572 static gint image_post_buffer_get(ImageWindow *imd)
576 if (imd->prev_pixbuf &&
577 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
579 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
580 if (imd->prev_color_row >= 0)
582 ExifData *exif = NULL;
584 if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
585 image_post_process_color(imd, imd->prev_color_row, exif);
595 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
596 imd->prev_pixbuf = NULL;
598 g_free(imd->prev_path);
599 imd->prev_path = NULL;
605 *-------------------------------------------------------------------
607 *-------------------------------------------------------------------
610 static void image_load_pixbuf_ready(ImageWindow *imd)
612 if (image_get_pixbuf(imd) || !imd->il) return;
614 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
617 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
619 ImageWindow *imd = data;
622 pr = (PixbufRenderer *)imd->pr;
624 if (imd->delay_flip &&
625 pr->pixbuf != image_loader_get_pixbuf(il))
630 if (!pr->pixbuf) image_load_pixbuf_ready(imd);
632 pixbuf_renderer_area_changed(pr, x, y, w, h);
635 static void image_load_done_cb(ImageLoader *il, gpointer data)
637 ImageWindow *imd = data;
639 if (debug) printf ("image done\n");
641 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
643 if (imd->delay_flip &&
644 image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
646 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
647 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
650 image_loader_free(imd->il);
653 image_post_process(imd, TRUE);
655 image_read_ahead_start(imd);
658 static void image_load_error_cb(ImageLoader *il, gpointer data)
660 if (debug) printf ("image error\n");
662 /* even on error handle it like it was done,
663 * since we have a pixbuf with _something_ */
665 image_load_done_cb(il, data);
668 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
669 static void image_load_buffer_throttle(ImageLoader *il)
671 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
673 /* Larger image files usually have larger chunks of data per pixel...
674 * So increase the buffer read size so that the rendering chunks called
678 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
682 /* this read ahead is located here merely for the callbacks, above */
684 static gint image_read_ahead_check(ImageWindow *imd)
686 if (!imd->read_ahead_path) return FALSE;
687 if (imd->il) return FALSE;
689 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
691 image_read_ahead_cancel(imd);
695 if (imd->read_ahead_il)
697 imd->il = imd->read_ahead_il;
698 imd->read_ahead_il = NULL;
700 /* override the old signals */
701 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
702 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
703 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
705 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
706 image_load_buffer_throttle(imd->il);
709 /* do this one directly (probably should add a set func) */
710 imd->il->func_done = image_load_done_cb;
712 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
714 if (!imd->delay_flip)
716 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
719 image_read_ahead_cancel(imd);
722 else if (imd->read_ahead_pixbuf)
724 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
725 g_object_unref(imd->read_ahead_pixbuf);
726 imd->read_ahead_pixbuf = NULL;
728 image_read_ahead_cancel(imd);
730 image_post_process(imd, FALSE);
734 image_read_ahead_cancel(imd);
738 static gint image_load_begin(ImageWindow *imd, const gchar *path)
740 if (debug) printf ("image begin \n");
742 if (imd->il) return FALSE;
744 imd->completed = FALSE;
745 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
747 if (image_post_buffer_get(imd))
749 if (debug) printf("from post buffer: %s\n", imd->image_path);
753 if (image_read_ahead_check(imd))
755 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
759 if (!imd->delay_flip && image_get_pixbuf(imd))
763 pr = PIXBUF_RENDERER(imd->pr);
764 if (pr->pixbuf) g_object_unref(pr->pixbuf);
768 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
770 imd->il = image_loader_new(path);
772 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
773 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
774 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
776 if (!image_loader_start(imd->il, image_load_done_cb, imd))
778 if (debug) printf("image start error\n");
780 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
782 image_loader_free(imd->il);
785 image_complete_util(imd, FALSE);
790 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
791 image_load_buffer_throttle(imd->il);
794 if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
799 static void image_reset(ImageWindow *imd)
801 /* stops anything currently being done */
803 if (debug) printf("image reset\n");
805 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
807 image_loader_free(imd->il);
810 color_man_free((ColorMan *)imd->cm);
813 imd->delay_alter_type = ALTER_NONE;
815 imd->state = IMAGE_STATE_NONE;
819 *-------------------------------------------------------------------
821 *-------------------------------------------------------------------
824 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
828 if (imd->image_path && isfile(imd->image_path))
832 pr = PIXBUF_RENDERER(imd->pr);
833 pr->zoom = zoom; /* store the zoom, needed by the loader */
835 if (image_load_begin(imd, imd->image_path))
837 imd->unknown = FALSE;
843 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
844 image_change_pixbuf(imd, pixbuf, zoom);
845 g_object_unref(pixbuf);
849 imd->size = filesize(imd->image_path);
850 imd->mtime = filetime(imd->image_path);
858 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
859 image_change_pixbuf(imd, pixbuf, zoom);
860 g_object_unref(pixbuf);
861 imd->mtime = filetime(imd->image_path);
865 image_change_pixbuf(imd, NULL, zoom);
872 image_update_util(imd);
875 static void image_change_real(ImageWindow *imd, const gchar *path,
876 CollectionData *cd, CollectInfo *info, gdouble zoom)
879 GdkPixbuf *prev_pixbuf = NULL;
880 gchar *prev_path = NULL;
881 gint prev_clear = FALSE;
882 gint prev_color_row = -1;
884 imd->collection = cd;
885 imd->collection_info = info;
887 pixbuf = image_get_pixbuf(imd);
889 if (enable_read_ahead && imd->image_path && pixbuf)
893 /* current image is not finished */
898 prev_path = g_strdup(imd->image_path);
899 prev_pixbuf = pixbuf;
900 g_object_ref(prev_pixbuf);
906 cm = (ColorMan *)imd->cm;
907 prev_color_row = cm->row;
912 g_free(imd->image_path);
913 imd->image_path = g_strdup(path);
914 imd->image_name = filename_from_path(imd->image_path);
916 image_change_complete(imd, zoom, TRUE);
920 image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
922 g_object_unref(prev_pixbuf);
926 image_post_buffer_set(imd, NULL, NULL, -1);
929 image_update_title(imd);
934 *-------------------------------------------------------------------
936 *-------------------------------------------------------------------
939 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
943 widget = imd->widget;
944 if (!widget->window) return;
948 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
949 area, widget, "image_window",
950 widget->allocation.x, widget->allocation.y,
951 widget->allocation.width - 1, widget->allocation.height - 1);
955 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
956 area, widget, "image_window",
957 widget->allocation.x, widget->allocation.y,
958 widget->allocation.width - 1, widget->allocation.height - 1);
962 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
964 ImageWindow *imd = data;
966 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
970 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
972 ImageWindow *imd = data;
974 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
975 image_focus_paint(imd, TRUE, NULL);
980 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
982 ImageWindow *imd = data;
984 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
985 image_focus_paint(imd, FALSE, NULL);
990 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
991 gint relative, gint always)
993 return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
996 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
998 pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1001 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1003 return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1006 void image_overlay_remove(ImageWindow *imd, gint id)
1008 pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1011 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1013 ImageWindow *imd = data;
1015 if (imd->func_scroll &&
1016 event && event->type == GDK_SCROLL)
1018 imd->func_scroll(imd, event->direction, event->time,
1019 event->x, event->y, event->state, imd->data_scroll);
1027 *-------------------------------------------------------------------
1029 *-------------------------------------------------------------------
1032 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1033 const gchar *title, const gchar *title_right, gint show_zoom)
1035 imd->top_window = window;
1037 imd->title = g_strdup(title);
1038 g_free(imd->title_right);
1039 imd->title_right = g_strdup(title_right);
1040 imd->title_show_zoom = show_zoom;
1042 if (!fit_window) window = NULL;
1044 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1046 image_update_title(imd);
1049 void image_set_update_func(ImageWindow *imd,
1050 void (*func)(ImageWindow *imd, gpointer data),
1053 imd->func_update = func;
1054 imd->data_update = data;
1057 void image_set_complete_func(ImageWindow *imd,
1058 void (*func)(ImageWindow *, gint preload, gpointer),
1061 imd->func_complete = func;
1062 imd->data_complete = data;
1065 void image_set_new_func(ImageWindow *imd,
1066 void (*func)(ImageWindow *, gpointer),
1069 imd->func_new = func;
1070 imd->data_new = data;
1074 void image_set_button_func(ImageWindow *imd,
1075 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1078 imd->func_button = func;
1079 imd->data_button = data;
1082 void image_set_scroll_func(ImageWindow *imd,
1083 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1086 imd->func_scroll = func;
1087 imd->data_scroll = data;
1090 void image_set_scroll_notify_func(ImageWindow *imd,
1091 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1094 imd->func_scroll_notify = func;
1095 imd->data_scroll_notify = data;
1100 const gchar *image_get_path(ImageWindow *imd)
1102 return imd->image_path;
1105 const gchar *image_get_name(ImageWindow *imd)
1107 return imd->image_name;
1110 /* merely changes path string, does not change the image! */
1111 void image_set_path(ImageWindow *imd, const gchar *newpath)
1113 g_free(imd->image_path);
1114 imd->image_path = g_strdup(newpath);
1115 imd->image_name = filename_from_path(imd->image_path);
1117 image_update_title(imd);
1118 image_new_util(imd);
1121 /* load a new image */
1123 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
1125 if (imd->image_path == path ||
1126 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
1128 image_change_real(imd, path, NULL, NULL, zoom);
1131 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1133 return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1136 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1138 pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1139 image_new_util(imd);
1142 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1144 if (!cd || !info || !g_list_find(cd->list, info)) return;
1146 image_change_real(imd, info->path, cd, info, zoom);
1149 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1151 if (collection_to_number(imd->collection) >= 0)
1153 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1155 if (info) *info = imd->collection_info;
1159 if (info) *info = NULL;
1161 return imd->collection;
1164 if (info) *info = NULL;
1168 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1170 /* change data for the callbacks directly */
1172 il->data_area_ready = data;
1173 il->data_error = data;
1174 il->data_done = data;
1175 il->data_percent = data;
1178 /* this is more like a move function
1179 * it moves most data from source to imd
1181 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1183 if (imd == source) return;
1185 imd->unknown = source->unknown;
1187 imd->collection = source->collection;
1188 imd->collection_info = source->collection_info;
1189 imd->size = source->size;
1190 imd->mtime = source->mtime;
1192 image_set_path(imd, image_get_path(source));
1194 image_loader_free(imd->il);
1199 imd->il = source->il;
1202 image_loader_sync_data(imd->il, imd);
1204 imd->delay_alter_type = source->delay_alter_type;
1205 source->delay_alter_type = ALTER_NONE;
1208 imd->color_profile_enable = source->color_profile_enable;
1209 imd->color_profile_input = source->color_profile_input;
1210 imd->color_profile_screen = source->color_profile_screen;
1211 imd->color_profile_use_image = source->color_profile_use_image;
1212 color_man_free((ColorMan *)imd->cm);
1218 imd->cm = source->cm;
1221 cm = (ColorMan *)imd->cm;
1223 cm->func_done_data = imd;
1226 image_loader_free(imd->read_ahead_il);
1227 imd->read_ahead_il = source->read_ahead_il;
1228 source->read_ahead_il = NULL;
1229 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1231 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1232 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1233 source->read_ahead_pixbuf = NULL;
1235 g_free(imd->read_ahead_path);
1236 imd->read_ahead_path = source->read_ahead_path;
1237 source->read_ahead_path = NULL;
1239 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1240 imd->prev_pixbuf = source->prev_pixbuf;
1241 source->prev_pixbuf = NULL;
1242 imd->prev_color_row = source->prev_color_row;
1243 source->prev_color_row = -1;
1245 g_free(imd->prev_path);
1246 imd->prev_path = source->prev_path;
1247 source->prev_path = NULL;
1249 imd->completed = source->completed;
1250 imd->state = source->state;
1251 source->state = IMAGE_STATE_NONE;
1253 pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1258 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1260 pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1263 void image_reload(ImageWindow *imd)
1265 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1267 image_change_complete(imd, image_zoom_get(imd), FALSE);
1270 void image_scroll(ImageWindow *imd, gint x, gint y)
1272 pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1275 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1276 gdouble x_align, gdouble y_align)
1278 pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1281 void image_alter(ImageWindow *imd, AlterType type)
1283 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1285 if (imd->il || imd->cm)
1287 /* still loading, wait till done */
1288 imd->delay_alter_type = type;
1289 imd->state |= IMAGE_STATE_ROTATE_USER;
1291 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1293 imd->state &= ~IMAGE_STATE_ROTATE_AUTO;
1298 image_alter_real(imd, type, TRUE);
1301 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1303 pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1306 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1308 pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1311 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1313 pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1316 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1318 pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1321 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1327 pr = (PixbufRenderer *)imd->pr;
1329 if (!pixbuf_renderer_get_pixbuf(pr) ||
1330 !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1334 zoom = (gdouble)pr->window_height / height;
1338 zoom = (gdouble)pr->window_width / width;
1343 zoom = 0.0 - 1.0 / zoom;
1346 pixbuf_renderer_zoom_set(pr, zoom);
1349 gdouble image_zoom_get(ImageWindow *imd)
1351 return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1354 gdouble image_zoom_get_real(ImageWindow *imd)
1356 return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1359 gchar *image_zoom_get_as_text(ImageWindow *imd)
1367 gchar *approx = " ";
1369 zoom = image_zoom_get(imd);
1370 scale = image_zoom_get_real(imd);
1376 else if (zoom < 0.0)
1380 else if (zoom == 0.0 && scale != 0.0)
1393 if (rint(l) != l) pl = 1;
1394 if (rint(r) != r) pr = 1;
1396 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1399 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1403 if (mode == ZOOM_RESET_ORIGINAL)
1407 else if (mode == ZOOM_RESET_FIT_WINDOW)
1415 zoom = image_zoom_get(imd);
1428 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1430 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1434 image_read_ahead_set(imd, path);
1438 image_read_ahead_cancel(imd);
1442 static gint image_auto_refresh_cb(gpointer data)
1444 ImageWindow *imd = data;
1447 if (!imd || !image_get_pixbuf(imd) ||
1448 imd->il || !imd->image_path ||
1449 !update_on_time_change) return TRUE;
1451 newtime = filetime(imd->image_path);
1452 if (newtime > 0 && newtime != imd->mtime)
1454 imd->mtime = newtime;
1461 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1463 void image_auto_refresh(ImageWindow *imd, gint interval)
1466 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1468 if (imd->auto_refresh_id > -1)
1470 g_source_remove(imd->auto_refresh_id);
1471 imd->auto_refresh_id = -1;
1472 imd->auto_refresh_interval = -1;
1475 if (interval < 0) return;
1477 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1479 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1480 imd->auto_refresh_interval = interval;
1483 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1485 imd->top_window_sync = allow_sync;
1487 g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1490 void image_background_set_black(ImageWindow *imd, gint black)
1492 pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1495 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1497 pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1500 void image_color_profile_set(ImageWindow *imd,
1501 gint input_type, gint screen_type,
1506 if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1507 screen_type < 0 || screen_type > 1)
1512 imd->color_profile_input = input_type;
1513 imd->color_profile_screen = screen_type;
1514 imd->color_profile_use_image = use_image;
1517 gint image_color_profile_get(ImageWindow *imd,
1518 gint *input_type, gint *screen_type,
1521 if (!imd) return FALSE;
1523 if (input_type) *input_type = imd->color_profile_input;
1524 if (screen_type) *screen_type = imd->color_profile_screen;
1525 if (use_image) *use_image = imd->color_profile_use_image;
1530 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1534 if (imd->color_profile_enable == enable) return;
1536 imd->color_profile_enable = enable;
1539 gint image_color_profile_get_use(ImageWindow *imd)
1541 if (!imd) return FALSE;
1543 return imd->color_profile_enable;
1546 void image_set_delay_flip(ImageWindow *imd, gint delay)
1549 imd->delay_flip == delay) return;
1551 imd->delay_flip = delay;
1553 g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1555 if (!imd->delay_flip && imd->il)
1559 pr = PIXBUF_RENDERER(imd->pr);
1560 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1563 image_load_pixbuf_ready(imd);
1567 void image_to_root_window(ImageWindow *imd, gint scaled)
1570 GdkWindow *rootwindow;
1578 pixbuf = image_get_pixbuf(imd);
1579 if (!pixbuf) return;
1581 screen = gtk_widget_get_screen(imd->widget);
1582 rootwindow = gdk_screen_get_root_window(screen);
1583 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1587 width = gdk_screen_width();
1588 height = gdk_screen_height();
1592 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1595 pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1597 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1598 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1599 gdk_window_clear(rootwindow);
1601 g_object_unref(pixmap);
1607 *-------------------------------------------------------------------
1609 *-------------------------------------------------------------------
1612 static void image_options_set(ImageWindow *imd)
1614 g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1615 "zoom_2pass", two_pass_zoom,
1616 "zoom_expand", zoom_to_fit_expands,
1617 "dither_quality", dither_quality,
1618 "scroll_reset", scroll_reset_method,
1619 "cache_display", tile_cache_max,
1620 "window_fit", (imd->top_window_sync && fit_window),
1621 "window_limit", limit_window_size,
1622 "window_limit_value", max_window_size,
1625 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1628 void image_options_sync(void)
1640 image_options_set(imd);
1645 *-------------------------------------------------------------------
1647 *-------------------------------------------------------------------
1650 static void image_free(ImageWindow *imd)
1652 image_list = g_list_remove(image_list, imd);
1656 image_read_ahead_cancel(imd);
1657 image_post_buffer_set(imd, NULL, NULL, -1);
1658 image_auto_refresh(imd, -1);
1660 g_free(imd->image_path);
1662 g_free(imd->title_right);
1667 static void image_destroy_cb(GtkObject *widget, gpointer data)
1669 ImageWindow *imd = data;
1673 ImageWindow *image_new(gint frame)
1677 imd = g_new0(ImageWindow, 1);
1679 imd->top_window = NULL;
1681 imd->title_right = NULL;
1682 imd->title_show_zoom = FALSE;
1684 imd->unknown = TRUE;
1686 imd->has_frame = frame;
1687 imd->top_window_sync = FALSE;
1689 imd->delay_alter_type = ALTER_NONE;
1691 imd->read_ahead_il = NULL;
1692 imd->read_ahead_pixbuf = NULL;
1693 imd->read_ahead_path = NULL;
1695 imd->completed = FALSE;
1696 imd->state = IMAGE_STATE_NONE;
1698 imd->color_profile_enable = FALSE;
1699 imd->color_profile_input = 0;
1700 imd->color_profile_screen = 0;
1701 imd->color_profile_use_image = FALSE;
1703 imd->auto_refresh_id = -1;
1704 imd->auto_refresh_interval = -1;
1706 imd->delay_flip = FALSE;
1708 imd->func_update = NULL;
1709 imd->func_complete = NULL;
1710 imd->func_tile_request = NULL;
1711 imd->func_tile_dispose = NULL;
1713 imd->func_button = NULL;
1714 imd->func_scroll = NULL;
1716 imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1718 image_options_set(imd);
1722 imd->widget = gtk_frame_new(NULL);
1723 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1724 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1725 gtk_widget_show(imd->pr);
1727 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1728 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1729 G_CALLBACK(image_focus_in_cb), imd);
1730 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1731 G_CALLBACK(image_focus_out_cb), imd);
1733 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1734 G_CALLBACK(image_focus_expose), imd);
1738 imd->widget = imd->pr;
1741 g_signal_connect(G_OBJECT(imd->pr), "clicked",
1742 G_CALLBACK(image_click_cb), imd);
1743 g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1744 G_CALLBACK(image_scroll_notify_cb), imd);
1746 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1747 G_CALLBACK(image_scroll_cb), imd);
1749 g_signal_connect(G_OBJECT(imd->pr), "destroy",
1750 G_CALLBACK(image_destroy_cb), imd);
1752 g_signal_connect(G_OBJECT(imd->pr), "zoom",
1753 G_CALLBACK(image_zoom_cb), imd);
1754 g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1755 G_CALLBACK(image_render_complete_cb), imd);
1757 image_list = g_list_append(image_list, imd);