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"
30 /* size of the image loader buffer (512 bytes x defined number) */
31 #define IMAGE_LOAD_BUFFER_COUNT 8
33 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
34 #define IMAGE_THROTTLE_LARGER_IMAGES 1
36 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
37 #define IMAGE_THROTTLE_FACTOR 4
39 /* the file size at which throttling take place */
40 #define IMAGE_THROTTLE_THRESHOLD 1048576
42 #define IMAGE_AUTO_REFRESH_TIME 3000
45 static GList *image_list = NULL;
48 static void image_update_title(ImageWindow *imd);
49 static void image_post_process(ImageWindow *imd, gint clamp);
50 static void image_read_ahead_start(ImageWindow *imd);
53 *-------------------------------------------------------------------
55 *-------------------------------------------------------------------
58 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
60 ImageWindow *imd = data;
64 imd->func_button(imd, event->button, event->time,
65 event->x, event->y, event->state, imd->data_button);
69 static void image_drag_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
71 ImageWindow *imd = data;
74 pixbuf_renderer_get_scaled_size(pr, &width, &height);
78 imd->func_drag(imd, event->button, event->time,
79 event->x, event->y, event->state,
80 (gfloat)(pr->drag_last_x - event->x) / width, (gfloat)(pr->drag_last_y - event->y) / height,
85 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
87 ImageWindow *imd = data;
89 if (imd->func_scroll_notify && pr->scale)
91 imd->func_scroll_notify(imd,
92 (gint)((gdouble)pr->x_scroll / pr->scale),
93 (gint)((gdouble)pr->y_scroll / pr->scale),
94 (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale),
95 (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale),
96 imd->data_scroll_notify);
100 static void image_update_util(ImageWindow *imd)
102 if (imd->func_update) imd->func_update(imd, imd->data_update);
105 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
107 ImageWindow *imd = data;
109 if (imd->title_show_zoom) image_update_title(imd);
110 image_update_util(imd);
113 static void image_complete_util(ImageWindow *imd, gint preload)
115 if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
117 if (debug) printf("image load completed \"%s\" (%s)\n",
118 (preload) ? imd->read_ahead_fd->path : imd->image_fd->path,
119 (preload) ? "preload" : "current");
121 if (!preload) imd->completed = TRUE;
122 if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
125 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
127 ImageWindow *imd = data;
129 image_complete_util(imd, FALSE);
132 static void image_state_set(ImageWindow *imd, ImageState state)
134 if (state == IMAGE_STATE_NONE)
142 if (imd->func_state) imd->func_state(imd, state, imd->data_state);
145 static void image_state_unset(ImageWindow *imd, ImageState state)
147 imd->state &= ~state;
148 if (imd->func_state) imd->func_state(imd, state, imd->data_state);
152 *-------------------------------------------------------------------
154 *-------------------------------------------------------------------
157 static void image_update_title(ImageWindow *imd)
161 gchar *collection = NULL;
163 if (!imd->top_window) return;
165 if (imd->collection && collection_to_number(imd->collection) >= 0)
168 name = imd->collection->name;
169 if (!name) name = _("Untitled");
170 collection = g_strdup_printf(" (Collection %s)", name);
173 if (imd->title_show_zoom)
175 gchar *buf = image_zoom_get_as_text(imd);
176 zoom = g_strconcat(" [", buf, "]", NULL);
180 title = g_strdup_printf("%s%s%s%s%s%s",
181 imd->title ? imd->title : "",
182 imd->image_fd ? imd->image_fd->name : "",
184 collection ? collection : "",
185 imd->image_fd ? " - " : "",
186 imd->title_right ? imd->title_right : "");
188 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
196 *-------------------------------------------------------------------
197 * rotation, flip, etc.
198 *-------------------------------------------------------------------
201 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
204 GdkPixbuf *new = NULL;
209 pr = (PixbufRenderer *)imd->pr;
211 exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
212 imd->delay_alter_type = ALTER_NONE;
214 if (!pr->pixbuf) return;
216 x = pr->x_scroll + (pr->vis_width / 2);
217 y = pr->y_scroll + (pr->vis_height / 2);
221 case ALTER_ROTATE_90:
222 new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
227 case ALTER_ROTATE_90_CC:
228 new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
233 case ALTER_ROTATE_180:
234 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
239 new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
243 new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
246 case ALTER_DESATURATE:
247 pixbuf_desaturate_rect(pr->pixbuf,
248 0, 0, pr->image_width, pr->image_height);
249 image_area_changed(imd, 0, 0, pr->image_width, pr->image_height);
259 pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
262 if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
266 switch (pr->scroll_reset)
268 case PR_SCROLL_RESET_NOCHANGE:
270 case PR_SCROLL_RESET_CENTER:
271 x = (gint)((gdouble)pr->image_width / 2.0 * pr->scale);
272 y = (gint)((gdouble)pr->image_height / 2.0 * pr->scale);
274 case PR_SCROLL_RESET_TOPLEFT:
281 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)x / pr->scale),
282 (gint)((gdouble)y / pr->scale),
286 if (exif_rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
289 static void image_post_process_alter(ImageWindow *imd, gint clamp)
291 if (imd->delay_alter_type != ALTER_NONE)
293 image_alter_real(imd, imd->delay_alter_type, clamp);
297 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
299 ImageWindow *imd = data;
302 if (type == COLOR_RETURN_IMAGE_CHANGED)
304 if (cm == imd->cm) imd->cm = NULL;
309 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
311 image_post_process_alter(imd, FALSE);
313 image_read_ahead_start(imd);
316 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
319 ColorManProfileType input_type;
320 ColorManProfileType screen_type;
321 const gchar *input_file;
322 const gchar *screen_file;
323 ExifItem *item = NULL;
325 if (imd->cm) return FALSE;
327 if (imd->color_profile_input >= 1 &&
328 imd->color_profile_input <= COLOR_PROFILE_INPUTS)
332 n = imd->color_profile_input - 1;
333 if (!color_profile_input_file[n]) return FALSE;
335 input_type = COLOR_PROFILE_FILE;
336 input_file = color_profile_input_file[n];
338 else if (imd->color_profile_input == 0)
340 input_type = COLOR_PROFILE_SRGB;
348 if (imd->color_profile_screen == 1 &&
349 color_profile_screen_file)
351 screen_type = COLOR_PROFILE_FILE;
352 screen_file = color_profile_screen_file;
354 else if (imd->color_profile_screen == 0)
356 screen_type = COLOR_PROFILE_SRGB;
364 if (imd->color_profile_use_image && exif)
366 item = exif_get_item(exif, "ColorProfile");
371 /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
372 if (exif_get_integer(exif, "Exif.Photo.ColorSpace", &cs) &&
375 input_type = COLOR_PROFILE_SRGB;
378 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
383 if (item && exif_item_get_format_id(item) == EXIF_FORMAT_UNDEFINED)
387 if (debug) printf("Found embedded color profile\n");
389 data = exif_item_get_data(item, &data_len);
391 cm = color_man_new_embedded(imd, NULL,
393 screen_type, screen_file,
394 image_post_process_color_cb, imd);
398 cm = color_man_new(imd, NULL,
399 input_type, input_file,
400 screen_type, screen_file,
401 image_post_process_color_cb, imd);
409 cm->incremental_sync = TRUE;
412 imd->cm = (gpointer)cm;
419 static void image_post_process(ImageWindow *imd, gint clamp)
421 ExifData *exif = NULL;
423 if (!image_get_pixbuf(imd)) return;
425 if (exif_rotate_enable ||
426 (imd->color_profile_enable && imd->color_profile_use_image) )
428 exif = exif_read(imd->image_fd->path, (imd->color_profile_enable && imd->color_profile_use_image));
431 if (exif_rotate_enable && exif)
435 if (exif_get_integer(exif, "Exif.Image.Orientation", &orientation))
439 /* see http://jpegclub.org/exif_orientation.html
442 888888 888888 88 88 8888888888 88 88 8888888888
443 88 88 88 88 88 88 88 88 88 88 88 88
444 8888 8888 8888 8888 88 8888888888 8888888888 88
450 case EXIF_ORIENTATION_TOP_LEFT:
451 /* normal -- nothing to do */
454 case EXIF_ORIENTATION_TOP_RIGHT:
456 imd->delay_alter_type = ALTER_MIRROR;
458 case EXIF_ORIENTATION_BOTTOM_RIGHT:
460 imd->delay_alter_type = ALTER_ROTATE_180;
462 case EXIF_ORIENTATION_BOTTOM_LEFT:
464 imd->delay_alter_type = ALTER_FLIP;
466 case EXIF_ORIENTATION_LEFT_TOP:
467 /* not implemented -- too wacky to fix in one step */
470 case EXIF_ORIENTATION_RIGHT_TOP:
471 /* rotated -90 (270) */
472 imd->delay_alter_type = ALTER_ROTATE_90;
474 case EXIF_ORIENTATION_RIGHT_BOTTOM:
475 /* not implemented -- too wacky to fix in one step */
478 case EXIF_ORIENTATION_LEFT_BOTTOM:
480 imd->delay_alter_type = ALTER_ROTATE_90_CC;
483 /* The other values are out of range */
488 if (rotate) image_state_set(imd, IMAGE_STATE_ROTATE_AUTO);
492 if (imd->color_profile_enable)
494 if (!image_post_process_color(imd, 0, exif))
496 /* fixme: note error to user */
497 image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
501 if (!imd->cm) image_post_process_alter(imd, clamp);
507 *-------------------------------------------------------------------
508 * read ahead (prebuffer)
509 *-------------------------------------------------------------------
512 static void image_read_ahead_cancel(ImageWindow *imd)
514 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_fd->path);
516 image_loader_free(imd->read_ahead_il);
517 imd->read_ahead_il = NULL;
519 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
520 imd->read_ahead_pixbuf = NULL;
522 file_data_unref(imd->read_ahead_fd);
523 imd->read_ahead_fd = NULL;
526 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
528 ImageWindow *imd = data;
530 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_fd->path);
532 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
533 if (imd->read_ahead_pixbuf)
535 g_object_ref(imd->read_ahead_pixbuf);
539 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
541 image_loader_free(imd->read_ahead_il);
542 imd->read_ahead_il = NULL;
544 image_complete_util(imd, TRUE);
547 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
549 /* we even treat errors as success, maybe at least some of the file was ok */
550 image_read_ahead_done_cb(il, data);
553 static void image_read_ahead_start(ImageWindow *imd)
555 /* already started ? */
556 if (!imd->read_ahead_fd || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
558 /* still loading ?, do later */
559 if (imd->il || imd->cm) return;
561 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_fd->path);
563 imd->read_ahead_il = image_loader_new(imd->read_ahead_fd);
565 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
566 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
568 image_read_ahead_cancel(imd);
569 image_complete_util(imd, TRUE);
573 static void image_read_ahead_set(ImageWindow *imd, FileData *fd)
575 if (imd->read_ahead_fd && fd && imd->read_ahead_fd == fd) return;
577 image_read_ahead_cancel(imd);
579 imd->read_ahead_fd = file_data_ref(fd);
581 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_fd->path);
583 image_read_ahead_start(imd);
587 *-------------------------------------------------------------------
589 *-------------------------------------------------------------------
592 static void image_post_buffer_set(ImageWindow *imd, FileData *fd, GdkPixbuf *pixbuf, gint color_row)
594 file_data_unref(imd->prev_fd);
595 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
599 imd->prev_fd = file_data_ref(fd);
601 g_object_ref(pixbuf);
602 imd->prev_pixbuf = pixbuf;
603 imd->prev_color_row = color_row;
608 imd->prev_pixbuf = NULL;
609 imd->prev_color_row = -1;
612 if (debug) printf("post buffer set: %s\n", fd->path);
615 static gint image_post_buffer_get(ImageWindow *imd)
619 if (imd->prev_pixbuf &&
620 imd->image_fd && imd->prev_fd && imd->image_fd == imd->prev_fd)
622 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
623 if (imd->prev_color_row >= 0)
625 ExifData *exif = NULL;
627 if (imd->color_profile_use_image) exif = exif_read(imd->image_fd->path, TRUE);
628 image_post_process_color(imd, imd->prev_color_row, exif);
638 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
639 imd->prev_pixbuf = NULL;
641 file_data_unref(imd->prev_fd);
648 *-------------------------------------------------------------------
650 *-------------------------------------------------------------------
653 static void image_load_pixbuf_ready(ImageWindow *imd)
655 if (image_get_pixbuf(imd) || !imd->il) return;
657 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
660 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
662 ImageWindow *imd = data;
665 pr = (PixbufRenderer *)imd->pr;
667 if (imd->delay_flip &&
668 pr->pixbuf != image_loader_get_pixbuf(il))
673 if (!pr->pixbuf) image_load_pixbuf_ready(imd);
675 pixbuf_renderer_area_changed(pr, x, y, w, h);
678 static void image_load_done_cb(ImageLoader *il, gpointer data)
680 ImageWindow *imd = data;
682 if (debug) printf ("image done\n");
684 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
685 image_state_unset(imd, IMAGE_STATE_LOADING);
687 if (imd->delay_flip &&
688 image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
690 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
691 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
694 image_loader_free(imd->il);
697 image_post_process(imd, TRUE);
699 image_read_ahead_start(imd);
702 static void image_load_error_cb(ImageLoader *il, gpointer data)
704 if (debug) printf ("image error\n");
706 /* even on error handle it like it was done,
707 * since we have a pixbuf with _something_ */
709 image_load_done_cb(il, data);
712 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
713 static void image_load_buffer_throttle(ImageLoader *il)
715 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
717 /* Larger image files usually have larger chunks of data per pixel...
718 * So increase the buffer read size so that the rendering chunks called
722 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
726 /* this read ahead is located here merely for the callbacks, above */
728 static gint image_read_ahead_check(ImageWindow *imd)
730 if (!imd->read_ahead_fd) return FALSE;
731 if (imd->il) return FALSE;
733 if (!imd->image_fd || imd->read_ahead_fd != imd->image_fd)
735 image_read_ahead_cancel(imd);
739 if (imd->read_ahead_il)
741 imd->il = imd->read_ahead_il;
742 imd->read_ahead_il = NULL;
744 /* override the old signals */
745 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
746 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
747 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
749 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
750 image_load_buffer_throttle(imd->il);
753 /* do this one directly (probably should add a set func) */
754 imd->il->func_done = image_load_done_cb;
756 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
757 image_state_set(imd, IMAGE_STATE_LOADING);
759 if (!imd->delay_flip)
761 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
764 image_read_ahead_cancel(imd);
767 else if (imd->read_ahead_pixbuf)
769 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
770 g_object_unref(imd->read_ahead_pixbuf);
771 imd->read_ahead_pixbuf = NULL;
773 image_read_ahead_cancel(imd);
775 image_post_process(imd, FALSE);
779 image_read_ahead_cancel(imd);
783 static gint image_load_begin(ImageWindow *imd, FileData *fd)
785 if (debug) printf ("image begin \n");
787 if (imd->il) return FALSE;
789 imd->completed = FALSE;
790 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
792 if (image_post_buffer_get(imd))
794 if (debug) printf("from post buffer: %s\n", imd->image_fd->path);
798 if (image_read_ahead_check(imd))
800 if (debug) printf("from read ahead buffer: %s\n", imd->image_fd->path);
804 if (!imd->delay_flip && image_get_pixbuf(imd))
808 pr = PIXBUF_RENDERER(imd->pr);
809 if (pr->pixbuf) g_object_unref(pr->pixbuf);
813 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
815 imd->il = image_loader_new(fd);
817 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
818 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
819 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
821 if (!image_loader_start(imd->il, image_load_done_cb, imd))
823 if (debug) printf("image start error\n");
825 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
827 image_loader_free(imd->il);
830 image_complete_util(imd, FALSE);
835 image_state_set(imd, IMAGE_STATE_LOADING);
837 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
838 image_load_buffer_throttle(imd->il);
841 if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
846 static void image_reset(ImageWindow *imd)
848 /* stops anything currently being done */
850 if (debug) printf("image reset\n");
852 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
854 image_loader_free(imd->il);
857 color_man_free((ColorMan *)imd->cm);
860 imd->delay_alter_type = ALTER_NONE;
862 image_state_set(imd, IMAGE_STATE_NONE);
866 *-------------------------------------------------------------------
868 *-------------------------------------------------------------------
871 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
875 if (imd->image_fd && isfile(imd->image_fd->path))
879 pr = PIXBUF_RENDERER(imd->pr);
880 pr->zoom = zoom; /* store the zoom, needed by the loader */
882 if (image_load_begin(imd, imd->image_fd))
884 imd->unknown = FALSE;
890 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
891 image_change_pixbuf(imd, pixbuf, zoom);
892 g_object_unref(pixbuf);
896 imd->size = filesize(imd->image_fd->path);
897 imd->mtime = filetime(imd->image_fd->path);
905 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
906 image_change_pixbuf(imd, pixbuf, zoom);
907 g_object_unref(pixbuf);
908 imd->mtime = filetime(imd->image_fd->path);
912 image_change_pixbuf(imd, NULL, zoom);
919 image_update_util(imd);
922 static void image_change_real(ImageWindow *imd, FileData *fd,
923 CollectionData *cd, CollectInfo *info, gdouble zoom)
926 GdkPixbuf *prev_pixbuf = NULL;
927 FileData *prev_fd = NULL;
928 gint prev_clear = FALSE;
929 gint prev_color_row = -1;
931 imd->collection = cd;
932 imd->collection_info = info;
934 pixbuf = image_get_pixbuf(imd);
936 if (enable_read_ahead && imd->image_fd && pixbuf)
940 /* current image is not finished */
945 prev_fd = file_data_ref(imd->image_fd);
946 prev_pixbuf = pixbuf;
947 g_object_ref(prev_pixbuf);
953 cm = (ColorMan *)imd->cm;
954 prev_color_row = cm->row;
959 file_data_unref(imd->image_fd);
960 imd->image_fd = file_data_ref(fd);
962 image_change_complete(imd, zoom, TRUE);
966 image_post_buffer_set(imd, prev_fd, prev_pixbuf, prev_color_row);
967 file_data_unref(prev_fd);
968 g_object_unref(prev_pixbuf);
972 image_post_buffer_set(imd, NULL, NULL, -1);
975 image_update_title(imd);
976 image_state_set(imd, IMAGE_STATE_IMAGE);
980 *-------------------------------------------------------------------
982 *-------------------------------------------------------------------
985 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
989 widget = imd->widget;
990 if (!widget->window) return;
994 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
995 area, widget, "image_window",
996 widget->allocation.x, widget->allocation.y,
997 widget->allocation.width - 1, widget->allocation.height - 1);
1001 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
1002 area, widget, "image_window",
1003 widget->allocation.x, widget->allocation.y,
1004 widget->allocation.width - 1, widget->allocation.height - 1);
1008 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1010 ImageWindow *imd = data;
1012 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
1016 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1018 ImageWindow *imd = data;
1020 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1021 image_focus_paint(imd, TRUE, NULL);
1026 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1028 ImageWindow *imd = data;
1030 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1031 image_focus_paint(imd, FALSE, NULL);
1036 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
1037 gint relative, gint always)
1039 return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1042 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1044 pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1047 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1049 return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1052 void image_overlay_remove(ImageWindow *imd, gint id)
1054 pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1057 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1059 ImageWindow *imd = data;
1061 if (imd->func_scroll &&
1062 event && event->type == GDK_SCROLL)
1064 imd->func_scroll(imd, event->direction, event->time,
1065 event->x, event->y, event->state, imd->data_scroll);
1073 *-------------------------------------------------------------------
1075 *-------------------------------------------------------------------
1078 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1079 const gchar *title, const gchar *title_right, gint show_zoom)
1081 imd->top_window = window;
1083 imd->title = g_strdup(title);
1084 g_free(imd->title_right);
1085 imd->title_right = g_strdup(title_right);
1086 imd->title_show_zoom = show_zoom;
1088 if (!fit_window) window = NULL;
1090 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1092 image_update_title(imd);
1095 void image_set_update_func(ImageWindow *imd,
1096 void (*func)(ImageWindow *imd, gpointer data),
1099 imd->func_update = func;
1100 imd->data_update = data;
1103 void image_set_complete_func(ImageWindow *imd,
1104 void (*func)(ImageWindow *imd, gint preload, gpointer data),
1107 imd->func_complete = func;
1108 imd->data_complete = data;
1111 void image_set_state_func(ImageWindow *imd,
1112 void (*func)(ImageWindow *imd, ImageState state, gpointer data),
1115 imd->func_state = func;
1116 imd->data_state = data;
1120 void image_set_button_func(ImageWindow *imd,
1121 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1124 imd->func_button = func;
1125 imd->data_button = data;
1128 void image_set_drag_func(ImageWindow *imd,
1129 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gdouble dx, gdouble dy, gpointer),
1132 imd->func_drag = func;
1133 imd->data_drag = data;
1136 void image_set_scroll_func(ImageWindow *imd,
1137 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1140 imd->func_scroll = func;
1141 imd->data_scroll = data;
1144 void image_set_scroll_notify_func(ImageWindow *imd,
1145 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1148 imd->func_scroll_notify = func;
1149 imd->data_scroll_notify = data;
1154 const gchar *image_get_path(ImageWindow *imd)
1156 if (imd->image_fd == NULL) return NULL;
1157 return imd->image_fd->path;
1160 const gchar *image_get_name(ImageWindow *imd)
1162 if (imd->image_fd == NULL) return NULL;
1163 return imd->image_fd->name;
1166 FileData *image_get_fd(ImageWindow *imd)
1168 return imd->image_fd;
1171 /* merely changes path string, does not change the image! */
1172 void image_set_fd(ImageWindow *imd, FileData *fd)
1174 file_data_unref(imd->image_fd);
1175 imd->image_fd = file_data_ref(fd);
1177 image_update_title(imd);
1178 image_state_set(imd, IMAGE_STATE_IMAGE);
1181 /* load a new image */
1183 void image_change_fd(ImageWindow *imd, FileData *fd, gdouble zoom)
1185 if (imd->image_fd == fd) return;
1187 image_change_real(imd, fd, NULL, NULL, zoom);
1190 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1192 return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1195 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1197 pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1198 image_state_set(imd, IMAGE_STATE_IMAGE);
1201 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1203 if (!cd || !info || !g_list_find(cd->list, info)) return;
1205 image_change_real(imd, info->fd, cd, info, zoom);
1208 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1210 if (collection_to_number(imd->collection) >= 0)
1212 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1214 if (info) *info = imd->collection_info;
1218 if (info) *info = NULL;
1220 return imd->collection;
1223 if (info) *info = NULL;
1227 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1229 /* change data for the callbacks directly */
1231 il->data_area_ready = data;
1232 il->data_error = data;
1233 il->data_done = data;
1234 il->data_percent = data;
1237 /* this is more like a move function
1238 * it moves most data from source to imd
1240 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1242 if (imd == source) return;
1244 imd->unknown = source->unknown;
1246 imd->collection = source->collection;
1247 imd->collection_info = source->collection_info;
1248 imd->size = source->size;
1249 imd->mtime = source->mtime;
1251 image_set_fd(imd, image_get_fd(source));
1253 image_loader_free(imd->il);
1258 imd->il = source->il;
1261 image_loader_sync_data(imd->il, imd);
1263 imd->delay_alter_type = source->delay_alter_type;
1264 source->delay_alter_type = ALTER_NONE;
1267 imd->color_profile_enable = source->color_profile_enable;
1268 imd->color_profile_input = source->color_profile_input;
1269 imd->color_profile_screen = source->color_profile_screen;
1270 imd->color_profile_use_image = source->color_profile_use_image;
1271 color_man_free((ColorMan *)imd->cm);
1277 imd->cm = source->cm;
1280 cm = (ColorMan *)imd->cm;
1282 cm->func_done_data = imd;
1285 image_loader_free(imd->read_ahead_il);
1286 imd->read_ahead_il = source->read_ahead_il;
1287 source->read_ahead_il = NULL;
1288 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1290 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1291 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1292 source->read_ahead_pixbuf = NULL;
1294 file_data_unref(imd->read_ahead_fd);
1295 imd->read_ahead_fd = source->read_ahead_fd;
1296 source->read_ahead_fd = NULL;
1298 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1299 imd->prev_pixbuf = source->prev_pixbuf;
1300 source->prev_pixbuf = NULL;
1301 imd->prev_color_row = source->prev_color_row;
1302 source->prev_color_row = -1;
1304 file_data_unref(imd->prev_fd);
1305 imd->prev_fd = source->prev_fd;
1306 source->prev_fd = NULL;
1308 imd->completed = source->completed;
1309 imd->state = source->state;
1310 source->state = IMAGE_STATE_NONE;
1312 pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1317 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1319 pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1322 void image_reload(ImageWindow *imd)
1324 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1326 image_change_complete(imd, image_zoom_get(imd), FALSE);
1329 void image_scroll(ImageWindow *imd, gint x, gint y)
1331 pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1334 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1335 gdouble x_align, gdouble y_align)
1337 pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1340 void image_get_scroll_center(ImageWindow *imd, gdouble *x, gdouble *y)
1342 pixbuf_renderer_get_scroll_center(PIXBUF_RENDERER(imd->pr), x, y);
1345 void image_set_scroll_center(ImageWindow *imd, gdouble x, gdouble y)
1347 pixbuf_renderer_set_scroll_center(PIXBUF_RENDERER(imd->pr), x, y);
1352 void image_alter(ImageWindow *imd, AlterType type)
1354 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1356 if (imd->il || imd->cm)
1358 /* still loading, wait till done */
1359 imd->delay_alter_type = type;
1360 image_state_set(imd, IMAGE_STATE_ROTATE_USER);
1362 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1364 image_state_unset(imd, IMAGE_STATE_ROTATE_AUTO);
1369 image_alter_real(imd, type, TRUE);
1372 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1374 pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1377 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1379 pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1382 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1384 pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1387 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1389 pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1392 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1398 pr = (PixbufRenderer *)imd->pr;
1400 if (!pixbuf_renderer_get_pixbuf(pr) ||
1401 !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1405 zoom = (gdouble)pr->window_height / height;
1409 zoom = (gdouble)pr->window_width / width;
1414 zoom = 0.0 - 1.0 / zoom;
1417 pixbuf_renderer_zoom_set(pr, zoom);
1420 gdouble image_zoom_get(ImageWindow *imd)
1422 return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1425 gdouble image_zoom_get_real(ImageWindow *imd)
1427 return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1430 gchar *image_zoom_get_as_text(ImageWindow *imd)
1438 gchar *approx = " ";
1440 zoom = image_zoom_get(imd);
1441 scale = image_zoom_get_real(imd);
1447 else if (zoom < 0.0)
1451 else if (zoom == 0.0 && scale != 0.0)
1464 if (rint(l) != l) pl = 1;
1465 if (rint(r) != r) pr = 1;
1467 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1470 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1474 if (mode == ZOOM_RESET_ORIGINAL)
1478 else if (mode == ZOOM_RESET_FIT_WINDOW)
1486 zoom = image_zoom_get(imd);
1499 void image_prebuffer_set(ImageWindow *imd, FileData *fd)
1501 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1505 image_read_ahead_set(imd, fd);
1509 image_read_ahead_cancel(imd);
1513 static gint image_auto_refresh_cb(gpointer data)
1515 ImageWindow *imd = data;
1518 if (!imd || !image_get_pixbuf(imd) ||
1519 imd->il || !imd->image_fd ||
1520 !update_on_time_change) return TRUE;
1522 newtime = filetime(imd->image_fd->path);
1523 if (newtime > 0 && newtime != imd->mtime)
1525 imd->mtime = newtime;
1532 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1534 void image_auto_refresh(ImageWindow *imd, gint interval)
1537 if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1539 if (imd->auto_refresh_id > -1)
1541 g_source_remove(imd->auto_refresh_id);
1542 imd->auto_refresh_id = -1;
1543 imd->auto_refresh_interval = -1;
1546 if (interval < 0) return;
1548 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1550 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1551 imd->auto_refresh_interval = interval;
1554 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1556 imd->top_window_sync = allow_sync;
1558 g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1561 void image_background_set_black(ImageWindow *imd, gint black)
1563 pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1566 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1568 pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1571 void image_color_profile_set(ImageWindow *imd,
1572 gint input_type, gint screen_type,
1577 if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1578 screen_type < 0 || screen_type > 1)
1583 imd->color_profile_input = input_type;
1584 imd->color_profile_screen = screen_type;
1585 imd->color_profile_use_image = use_image;
1588 gint image_color_profile_get(ImageWindow *imd,
1589 gint *input_type, gint *screen_type,
1592 if (!imd) return FALSE;
1594 if (input_type) *input_type = imd->color_profile_input;
1595 if (screen_type) *screen_type = imd->color_profile_screen;
1596 if (use_image) *use_image = imd->color_profile_use_image;
1601 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1605 if (imd->color_profile_enable == enable) return;
1607 imd->color_profile_enable = enable;
1610 gint image_color_profile_get_use(ImageWindow *imd)
1612 if (!imd) return FALSE;
1614 return imd->color_profile_enable;
1617 void image_set_delay_flip(ImageWindow *imd, gint delay)
1620 imd->delay_flip == delay) return;
1622 imd->delay_flip = delay;
1624 g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1626 if (!imd->delay_flip && imd->il)
1630 pr = PIXBUF_RENDERER(imd->pr);
1631 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1634 image_load_pixbuf_ready(imd);
1638 void image_to_root_window(ImageWindow *imd, gint scaled)
1641 GdkWindow *rootwindow;
1649 pixbuf = image_get_pixbuf(imd);
1650 if (!pixbuf) return;
1652 screen = gtk_widget_get_screen(imd->widget);
1653 rootwindow = gdk_screen_get_root_window(screen);
1654 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1658 width = gdk_screen_width();
1659 height = gdk_screen_height();
1663 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1666 pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1668 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1669 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1670 gdk_window_clear(rootwindow);
1672 g_object_unref(pixmap);
1677 void image_select(ImageWindow *imd, gboolean select)
1683 gtk_widget_set_state(imd->widget, GTK_STATE_SELECTED);
1684 gtk_widget_set_state(imd->pr, GTK_STATE_NORMAL); /* do not propagate */
1687 gtk_widget_set_state(imd->widget, GTK_STATE_NORMAL);
1693 void image_set_selectable(ImageWindow *imd, gboolean selectable)
1699 gtk_frame_set_shadow_type(GTK_FRAME(imd->frame), GTK_SHADOW_NONE);
1700 gtk_container_set_border_width (GTK_CONTAINER (imd->frame), 4);
1704 gtk_frame_set_shadow_type(GTK_FRAME(imd->frame), GTK_SHADOW_NONE);
1705 gtk_container_set_border_width (GTK_CONTAINER (imd->frame), 0);
1711 *-------------------------------------------------------------------
1713 *-------------------------------------------------------------------
1716 static void image_options_set(ImageWindow *imd)
1718 g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1719 "zoom_2pass", two_pass_zoom,
1720 "zoom_expand", zoom_to_fit_expands,
1721 "dither_quality", dither_quality,
1722 "scroll_reset", scroll_reset_method,
1723 "cache_display", tile_cache_max,
1724 "window_fit", (imd->top_window_sync && fit_window),
1725 "window_limit", limit_window_size,
1726 "window_limit_value", max_window_size,
1729 pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1732 void image_options_sync(void)
1744 image_options_set(imd);
1749 *-------------------------------------------------------------------
1751 *-------------------------------------------------------------------
1754 static void image_free(ImageWindow *imd)
1756 image_list = g_list_remove(image_list, imd);
1760 image_read_ahead_cancel(imd);
1761 image_post_buffer_set(imd, NULL, NULL, -1);
1762 image_auto_refresh(imd, -1);
1764 file_data_unref(imd->image_fd);
1766 g_free(imd->title_right);
1771 static void image_destroy_cb(GtkObject *widget, gpointer data)
1773 ImageWindow *imd = data;
1777 gboolean selectable_frame_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
1779 gtk_paint_flat_box(widget->style,
1782 GTK_FRAME (widget)->shadow_type,
1786 widget->allocation.x + 3, widget->allocation.y + 3,
1787 widget->allocation.width - 6, widget->allocation.height - 6);
1794 void image_set_frame(ImageWindow *imd, gboolean frame)
1798 if (frame == imd->has_frame) return;
1800 gtk_widget_hide(imd->pr);
1804 imd->frame = gtk_frame_new(NULL);
1805 gtk_widget_ref(imd->pr);
1806 if (imd->has_frame != -1) gtk_container_remove(GTK_CONTAINER(imd->widget), imd->pr);
1807 gtk_container_add(GTK_CONTAINER(imd->frame), imd->pr);
1808 gtk_widget_unref(imd->pr);
1809 g_signal_connect (G_OBJECT (imd->frame), "expose_event",
1810 G_CALLBACK (selectable_frame_expose_cb), NULL);
1812 GTK_WIDGET_SET_FLAGS(imd->frame, GTK_CAN_FOCUS);
1813 g_signal_connect(G_OBJECT(imd->frame), "focus_in_event",
1814 G_CALLBACK(image_focus_in_cb), imd);
1815 g_signal_connect(G_OBJECT(imd->frame), "focus_out_event",
1816 G_CALLBACK(image_focus_out_cb), imd);
1818 g_signal_connect_after(G_OBJECT(imd->frame), "expose_event",
1819 G_CALLBACK(image_focus_expose), imd);
1822 gtk_box_pack_start_defaults(GTK_BOX(imd->widget), imd->frame);
1823 gtk_widget_show(imd->frame);
1827 gtk_widget_ref(imd->pr);
1830 gtk_container_remove(GTK_CONTAINER(imd->frame), imd->pr);
1831 gtk_widget_destroy(imd->frame);
1834 gtk_box_pack_start_defaults(GTK_BOX(imd->widget), imd->pr);
1835 gtk_widget_unref(imd->pr);
1838 gtk_widget_show(imd->pr);
1840 imd->has_frame = frame;
1843 ImageWindow *image_new(gint frame)
1847 imd = g_new0(ImageWindow, 1);
1849 imd->top_window = NULL;
1851 imd->title_right = NULL;
1852 imd->title_show_zoom = FALSE;
1854 imd->unknown = TRUE;
1856 imd->has_frame = -1; /* not initialized; for image_set_frame */
1857 imd->top_window_sync = FALSE;
1859 imd->delay_alter_type = ALTER_NONE;
1861 imd->read_ahead_il = NULL;
1862 imd->read_ahead_pixbuf = NULL;
1863 imd->read_ahead_fd = NULL;
1865 imd->completed = FALSE;
1866 imd->state = IMAGE_STATE_NONE;
1868 imd->color_profile_enable = FALSE;
1869 imd->color_profile_input = 0;
1870 imd->color_profile_screen = 0;
1871 imd->color_profile_use_image = FALSE;
1873 imd->auto_refresh_id = -1;
1874 imd->auto_refresh_interval = -1;
1876 imd->delay_flip = FALSE;
1878 imd->func_update = NULL;
1879 imd->func_complete = NULL;
1880 imd->func_tile_request = NULL;
1881 imd->func_tile_dispose = NULL;
1883 imd->func_button = NULL;
1884 imd->func_scroll = NULL;
1886 imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1888 image_options_set(imd);
1891 imd->widget = gtk_vbox_new(0, 0);
1893 image_set_frame(imd, frame);
1895 image_set_selectable(imd, 0);
1897 g_signal_connect(G_OBJECT(imd->pr), "clicked",
1898 G_CALLBACK(image_click_cb), imd);
1899 g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1900 G_CALLBACK(image_scroll_notify_cb), imd);
1902 g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1903 G_CALLBACK(image_scroll_cb), imd);
1905 g_signal_connect(G_OBJECT(imd->pr), "destroy",
1906 G_CALLBACK(image_destroy_cb), imd);
1908 g_signal_connect(G_OBJECT(imd->pr), "zoom",
1909 G_CALLBACK(image_zoom_cb), imd);
1910 g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1911 G_CALLBACK(image_render_complete_cb), imd);
1912 g_signal_connect(G_OBJECT(imd->pr), "drag",
1913 G_CALLBACK(image_drag_cb), imd);
1915 image_list = g_list_append(image_list, imd);