8e3c6d34c5bd70615257a717a2d670768b76bff4
[geeqie.git] / src / image.c
1 /*
2  * GQview
3  * (C) 2006 John Ellis
4  *
5  * Author: John Ellis
6  *
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!
10  */
11
12
13 #include "gqview.h"
14 #include "image.h"
15
16
17 #include "image-load.h"
18 #include "collect.h"
19 #include "color-man.h"
20 #include "exif.h"
21 #include "pixbuf-renderer.h"
22 #include "pixbuf_util.h"
23 #include "ui_fileops.h"
24
25 #include <math.h>
26
27
28 /* size of the image loader buffer (512 bytes x defined number) */
29 #define IMAGE_LOAD_BUFFER_COUNT 8
30
31 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
32 #define IMAGE_THROTTLE_LARGER_IMAGES 1
33
34 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
35 #define IMAGE_THROTTLE_FACTOR 4
36
37 /* the file size at which throttling take place */
38 #define IMAGE_THROTTLE_THRESHOLD 1048576
39
40 #define IMAGE_AUTO_REFRESH_TIME 3000
41
42
43 static GList *image_list = NULL;
44
45
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);
49
50 /*
51  *-------------------------------------------------------------------
52  * 'signals'
53  *-------------------------------------------------------------------
54  */
55
56 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
57 {
58         ImageWindow *imd = data;
59
60         if (imd->func_button)
61                 {
62                 imd->func_button(imd, event->button, event->time,
63                                  event->x, event->y, event->state, imd->data_button);
64                 }
65 }
66
67 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
68 {
69         ImageWindow *imd = data;
70
71         if (imd->func_scroll_notify && pr->scale)
72                 {
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);
79                 }
80 }
81
82 static void image_update_util(ImageWindow *imd)
83 {
84         if (imd->func_update) imd->func_update(imd, imd->data_update);
85 }
86
87 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
88 {
89         ImageWindow *imd = data;
90
91         if (imd->title_show_zoom) image_update_title(imd);
92         image_update_util(imd);
93 }
94
95 static void image_complete_util(ImageWindow *imd, gint preload)
96 {
97         if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
98
99         if (debug) printf("image load completed \"%s\" (%s)\n",
100                           (preload) ? imd->read_ahead_path : imd->image_path,
101                           (preload) ? "preload" : "current");
102
103         if (!preload) imd->completed = TRUE;
104         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
105 }
106
107 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
108 {
109         ImageWindow *imd = data;
110
111         image_complete_util(imd, FALSE);
112 }
113
114 static void image_state_set(ImageWindow *imd, ImageState state)
115 {
116         if (state == IMAGE_STATE_NONE)
117                 {
118                 imd->state = state;
119                 }
120         else
121                 {
122                 imd->state |= state;
123                 }
124         if (imd->func_state) imd->func_state(imd, state, imd->data_state);
125 }
126
127 static void image_state_unset(ImageWindow *imd, ImageState state)
128 {
129         imd->state &= ~state;
130         if (imd->func_state) imd->func_state(imd, state, imd->data_state);
131 }
132
133 /*
134  *-------------------------------------------------------------------
135  * misc
136  *-------------------------------------------------------------------
137  */
138
139 static void image_update_title(ImageWindow *imd)
140 {
141         gchar *title = NULL;
142         gchar *zoom = NULL;
143         gchar *collection = NULL;
144
145         if (!imd->top_window) return;
146
147         if (imd->collection && collection_to_number(imd->collection) >= 0)
148                 {
149                 const gchar *name;
150                 name = imd->collection->name;
151                 if (!name) name = _("Untitled");
152                 collection = g_strdup_printf(" (Collection %s)", name);
153                 }
154
155         if (imd->title_show_zoom)
156                 {
157                 gchar *buf = image_zoom_get_as_text(imd);
158                 zoom = g_strconcat(" [", buf, "]", NULL);
159                 g_free(buf);
160                 }
161
162         title = g_strdup_printf("%s%s%s%s%s%s",
163                 imd->title ? imd->title : "",
164                 imd->image_name ? imd->image_name : "",
165                 zoom ? zoom : "",
166                 collection ? collection : "",
167                 imd->image_name ? " - " : "",
168                 imd->title_right ? imd->title_right : "");
169
170         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
171
172         g_free(title);
173         g_free(zoom);
174         g_free(collection);
175 }
176
177 /*
178  *-------------------------------------------------------------------
179  * rotation, flip, etc.
180  *-------------------------------------------------------------------
181  */
182
183 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
184 {
185         PixbufRenderer *pr;
186         GdkPixbuf *new = NULL;
187         gint exif_rotate;
188         gint x, y;
189         gint t;
190
191         pr = (PixbufRenderer *)imd->pr;
192
193         exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
194         imd->delay_alter_type = ALTER_NONE;
195
196         if (!pr->pixbuf) return;
197
198         x = pr->x_scroll + (pr->vis_width / 2);
199         y = pr->y_scroll + (pr->vis_height / 2);
200
201         switch (type)
202                 {
203                 case ALTER_ROTATE_90:
204                         new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
205                         t = x;
206                         x = pr->height - y;
207                         y = t;
208                         break;
209                 case ALTER_ROTATE_90_CC:
210                         new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
211                         t = x;
212                         x = y;
213                         y = pr->width - t;
214                         break;
215                 case ALTER_ROTATE_180:
216                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
217                         x = pr->width - x;
218                         y = pr->height - y;
219                         break;
220                 case ALTER_MIRROR:
221                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
222                         x = pr->width - x;
223                         break;
224                 case ALTER_FLIP:
225                         new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
226                         y = pr->height - y;
227                         break;
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);
232                         break;
233                 case ALTER_NONE:
234                 default:
235                         return;
236                         break;
237                 }
238
239         if (!new) return;
240
241         pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
242         g_object_unref(new);
243
244         if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
245                 {
246                 if (exif_rotate)
247                         {
248                         switch (pr->scroll_reset)
249                                 {
250                                 case PR_SCROLL_RESET_NOCHANGE:
251                                         break;
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);
255                                         break;
256                                 case PR_SCROLL_RESET_TOPLEFT:
257                                 default:
258                                         x = 0;
259                                         y = 0;
260                                         break;
261                                 }
262                         }
263                 pixbuf_renderer_scroll_to_point(pr, (gint)((gdouble)x / pr->scale),
264                                                     (gint)((gdouble)y / pr->scale),
265                                                     0.50, 0.50);
266                 }
267 }
268
269 static void image_post_process_alter(ImageWindow *imd, gint clamp)
270 {
271         if (imd->delay_alter_type != ALTER_NONE)
272                 {
273                 image_alter_real(imd, imd->delay_alter_type, clamp);
274                 }
275 }
276
277 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
278 {
279         ImageWindow *imd = data;
280
281         color_man_free(cm);
282         if (type == COLOR_RETURN_IMAGE_CHANGED)
283                 {
284                 if (cm == imd->cm) imd->cm = NULL;
285                 return;
286                 }
287
288         imd->cm = NULL;
289         image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
290
291         image_post_process_alter(imd, FALSE);
292
293         image_read_ahead_start(imd);
294 }
295
296 static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
297 {
298         ColorMan *cm;
299         ColorManProfileType input_type;
300         ColorManProfileType screen_type;
301         const gchar *input_file;
302         const gchar *screen_file;
303         ExifItem *item = NULL;
304
305         if (imd->cm) return FALSE;
306
307         if (imd->color_profile_input >= 1 &&
308             imd->color_profile_input <= COLOR_PROFILE_INPUTS)
309                 {
310                 gint n;
311
312                 n = imd->color_profile_input - 1;
313                 if (!color_profile_input_file[n]) return FALSE;
314
315                 input_type = COLOR_PROFILE_FILE;
316                 input_file = color_profile_input_file[n];
317                 }
318         else if (imd->color_profile_input == 0)
319                 {
320                 input_type = COLOR_PROFILE_SRGB;
321                 input_file = NULL;
322                 }
323         else
324                 {
325                 return FALSE;
326                 }
327
328         if (imd->color_profile_screen == 1 &&
329             color_profile_screen_file)
330                 {
331                 screen_type = COLOR_PROFILE_FILE;
332                 screen_file = color_profile_screen_file;
333                 }
334         else if (imd->color_profile_screen == 0)
335                 {
336                 screen_type = COLOR_PROFILE_SRGB;
337                 screen_file = NULL;
338                 }
339         else
340                 {
341                 return FALSE;
342                 }
343
344         if (imd->color_profile_use_image && exif)
345                 {
346                 item = exif_get_item(exif, "ColorProfile");
347                 if (!item)
348                         {
349                         gint cs;
350
351                         /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
352                         if (exif_get_integer(exif, "ColorSpace", &cs) &&
353                             cs == 1)
354                                 {
355                                 input_type = COLOR_PROFILE_SRGB;
356                                 input_file = NULL;
357
358                                 if (debug) printf("Found EXIF ColorSpace of sRGB\n");
359                                 }
360                         }
361                 }
362         if (item && item->format == EXIF_FORMAT_UNDEFINED)
363                 {
364                 if (debug) printf("Found embedded color profile\n");
365
366                 cm = color_man_new_embedded(imd, NULL,
367                                             item->data, item->data_len,
368                                             screen_type, screen_file,
369                                             image_post_process_color_cb, imd);
370                 }
371         else
372                 {
373                 cm = color_man_new(imd, NULL,
374                                    input_type, input_file,
375                                    screen_type, screen_file,
376                                    image_post_process_color_cb, imd);
377                 }
378
379         if (cm)
380                 {
381                 if (start_row > 0)
382                         {
383                         cm->row = start_row;
384                         cm->incremental_sync = TRUE;
385                         }
386
387                 imd->cm = (gpointer)cm;
388                 return TRUE;
389                 }
390
391         return FALSE;
392 }
393
394 static void image_post_process(ImageWindow *imd, gint clamp)
395 {
396         ExifData *exif = NULL;
397
398         if (!image_get_pixbuf(imd)) return;
399
400         if (exif_rotate_enable ||
401             (imd->color_profile_enable && imd->color_profile_use_image) )
402                 {
403                 exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
404                 }
405
406         if (exif_rotate_enable && exif)
407                 {
408                 gint orientation;
409
410                 if (exif_get_integer(exif, "Orientation", &orientation))
411                         {
412                         gint rotate = TRUE;
413
414                         /* see http://jpegclub.org/exif_orientation.html 
415                           1        2       3      4         5            6           7          8
416
417                         888888  888888      88  88      8888888888  88                  88  8888888888
418                         88          88      88  88      88  88      88  88          88  88      88  88
419                         8888      8888    8888  8888    88          8888888888  8888888888          88
420                         88          88      88  88
421                         88          88  888888  888888
422                         */
423                         switch (orientation)
424                                 {
425                                 case EXIF_ORIENTATION_TOP_LEFT:
426                                         /* normal -- nothing to do */
427                                         rotate = FALSE;
428                                         break;
429                                 case EXIF_ORIENTATION_TOP_RIGHT:
430                                         /* mirrored */
431                                         imd->delay_alter_type = ALTER_MIRROR;
432                                         break;
433                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
434                                         /* upside down */
435                                         imd->delay_alter_type = ALTER_ROTATE_180;
436                                         break;
437                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
438                                         /* flipped */
439                                         imd->delay_alter_type = ALTER_FLIP;
440                                         break;
441                                 case EXIF_ORIENTATION_LEFT_TOP:
442                                         /* not implemented -- too wacky to fix in one step */
443                                         rotate = FALSE;
444                                         break;
445                                 case EXIF_ORIENTATION_RIGHT_TOP:
446                                         /* rotated -90 (270) */
447                                         imd->delay_alter_type = ALTER_ROTATE_90;
448                                         break;
449                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
450                                         /* not implemented -- too wacky to fix in one step */
451                                         rotate = FALSE;
452                                         break;
453                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
454                                         /* rotated 90 */
455                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
456                                         break;
457                                 default:
458                                         /* The other values are out of range */
459                                         rotate = FALSE;
460                                         break;
461                                 }
462
463                         if (rotate) image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
464                         }
465                 }
466
467         if (imd->color_profile_enable)
468                 {
469                 if (!image_post_process_color(imd, 0, exif))
470                         {
471                         /* fixme: note error to user */
472                         image_state_set(imd, IMAGE_STATE_COLOR_ADJ);
473                         }
474                 }
475
476         if (!imd->cm) image_post_process_alter(imd, clamp);
477
478         exif_free(exif);
479 }
480
481 /*
482  *-------------------------------------------------------------------
483  * read ahead (prebuffer)
484  *-------------------------------------------------------------------
485  */
486
487 static void image_read_ahead_cancel(ImageWindow *imd)
488 {
489         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
490
491         image_loader_free(imd->read_ahead_il);
492         imd->read_ahead_il = NULL;
493
494         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
495         imd->read_ahead_pixbuf = NULL;
496
497         g_free(imd->read_ahead_path);
498         imd->read_ahead_path = NULL;
499 }
500
501 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
502 {
503         ImageWindow *imd = data;
504
505         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
506
507         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
508         if (imd->read_ahead_pixbuf)
509                 {
510                 g_object_ref(imd->read_ahead_pixbuf);
511                 }
512         else
513                 {
514                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
515                 }
516         image_loader_free(imd->read_ahead_il);
517         imd->read_ahead_il = NULL;
518
519         image_complete_util(imd, TRUE);
520 }
521
522 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
523 {
524         /* we even treat errors as success, maybe at least some of the file was ok */
525         image_read_ahead_done_cb(il, data);
526 }
527
528 static void image_read_ahead_start(ImageWindow *imd)
529 {
530         /* already started ? */
531         if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
532
533         /* still loading ?, do later */
534         if (imd->il || imd->cm) return;
535
536         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
537
538         imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
539
540         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
541         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
542                 {
543                 image_read_ahead_cancel(imd);
544                 image_complete_util(imd, TRUE);
545                 }
546 }
547
548 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
549 {
550         if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
551
552         image_read_ahead_cancel(imd);
553
554         imd->read_ahead_path = g_strdup(path);
555
556         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
557
558         image_read_ahead_start(imd);
559 }
560
561 /*
562  *-------------------------------------------------------------------
563  * post buffering
564  *-------------------------------------------------------------------
565  */
566
567 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
568 {
569         g_free(imd->prev_path);
570         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
571
572         if (path && pixbuf)
573                 {
574                 imd->prev_path = g_strdup(path);
575                         
576                 g_object_ref(pixbuf);
577                 imd->prev_pixbuf = pixbuf;
578                 imd->prev_color_row = color_row;
579                 }
580         else
581                 {
582                 imd->prev_path = NULL;
583                 imd->prev_pixbuf = NULL;
584                 imd->prev_color_row = -1;
585                 }
586
587         if (debug) printf("post buffer set: %s\n", path);
588 }
589
590 static gint image_post_buffer_get(ImageWindow *imd)
591 {
592         gint success;
593
594         if (imd->prev_pixbuf &&
595             imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
596                 {
597                 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
598                 if (imd->prev_color_row >= 0)
599                         {
600                         ExifData *exif = NULL;
601
602                         if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
603                         image_post_process_color(imd, imd->prev_color_row, exif);
604                         exif_free(exif);
605                         }
606                 success = TRUE;
607                 }
608         else
609                 {
610                 success = FALSE;
611                 }
612
613         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
614         imd->prev_pixbuf = NULL;
615
616         g_free(imd->prev_path);
617         imd->prev_path = NULL;
618
619         return success;
620 }
621
622 /*
623  *-------------------------------------------------------------------
624  * loading
625  *-------------------------------------------------------------------
626  */
627
628 static void image_load_pixbuf_ready(ImageWindow *imd)
629 {
630         if (image_get_pixbuf(imd) || !imd->il) return;
631
632         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
633 }
634
635 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
636 {
637         ImageWindow *imd = data;
638         PixbufRenderer *pr;
639
640         pr = (PixbufRenderer *)imd->pr;
641
642         if (imd->delay_flip &&
643             pr->pixbuf != image_loader_get_pixbuf(il))
644                 {
645                 return;
646                 }
647
648         if (!pr->pixbuf) image_load_pixbuf_ready(imd);
649
650         pixbuf_renderer_area_changed(pr, x, y, w, h);
651 }
652
653 static void image_load_done_cb(ImageLoader *il, gpointer data)
654 {
655         ImageWindow *imd = data;
656
657         if (debug) printf ("image done\n");
658
659         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
660         image_state_unset(imd, IMAGE_STATE_LOADING);
661
662         if (imd->delay_flip &&
663             image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
664                 {
665                 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
666                 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
667                 }
668
669         image_loader_free(imd->il);
670         imd->il = NULL;
671
672         image_post_process(imd, TRUE);
673
674         image_read_ahead_start(imd);
675 }
676
677 static void image_load_error_cb(ImageLoader *il, gpointer data)
678 {
679         if (debug) printf ("image error\n");
680
681         /* even on error handle it like it was done,
682          * since we have a pixbuf with _something_ */
683
684         image_load_done_cb(il, data);
685 }
686
687 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
688 static void image_load_buffer_throttle(ImageLoader *il)
689 {
690         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
691
692         /* Larger image files usually have larger chunks of data per pixel...
693          * So increase the buffer read size so that the rendering chunks called
694          * are also larger.
695          */
696
697         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
698 }
699 #endif
700
701 /* this read ahead is located here merely for the callbacks, above */
702
703 static gint image_read_ahead_check(ImageWindow *imd)
704 {
705         if (!imd->read_ahead_path) return FALSE;
706         if (imd->il) return FALSE;
707
708         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
709                 {
710                 image_read_ahead_cancel(imd);
711                 return FALSE;
712                 }
713
714         if (imd->read_ahead_il)
715                 {
716                 imd->il = imd->read_ahead_il;
717                 imd->read_ahead_il = NULL;
718
719                 /* override the old signals */
720                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
721                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
722                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
723
724 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
725                 image_load_buffer_throttle(imd->il);
726 #endif
727
728                 /* do this one directly (probably should add a set func) */
729                 imd->il->func_done = image_load_done_cb;
730
731                 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
732                 image_state_set(imd, IMAGE_STATE_LOADING);
733
734                 if (!imd->delay_flip)
735                         {
736                         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
737                         }
738
739                 image_read_ahead_cancel(imd);
740                 return TRUE;
741                 }
742         else if (imd->read_ahead_pixbuf)
743                 {
744                 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
745                 g_object_unref(imd->read_ahead_pixbuf);
746                 imd->read_ahead_pixbuf = NULL;
747
748                 image_read_ahead_cancel(imd);
749
750                 image_post_process(imd, FALSE);
751                 return TRUE;
752                 }
753
754         image_read_ahead_cancel(imd);
755         return FALSE;
756 }
757
758 static gint image_load_begin(ImageWindow *imd, const gchar *path)
759 {
760         if (debug) printf ("image begin \n");
761
762         if (imd->il) return FALSE;
763
764         imd->completed = FALSE;
765         g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
766
767         if (image_post_buffer_get(imd))
768                 {
769                 if (debug) printf("from post buffer: %s\n", imd->image_path);
770                 return TRUE;
771                 }
772
773         if (image_read_ahead_check(imd))
774                 {
775                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
776                 return TRUE;
777                 }
778
779         if (!imd->delay_flip && image_get_pixbuf(imd))
780                 {
781                 PixbufRenderer *pr;
782
783                 pr = PIXBUF_RENDERER(imd->pr);
784                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
785                 pr->pixbuf = NULL;
786                 }
787
788         g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
789
790         imd->il = image_loader_new(path);
791
792         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
793         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
794         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
795
796         if (!image_loader_start(imd->il, image_load_done_cb, imd))
797                 {
798                 if (debug) printf("image start error\n");
799
800                 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
801
802                 image_loader_free(imd->il);
803                 imd->il = NULL;
804
805                 image_complete_util(imd, FALSE);
806
807                 return FALSE;
808                 }
809
810         image_state_set(imd, IMAGE_STATE_LOADING);
811
812 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
813         image_load_buffer_throttle(imd->il);
814 #endif
815
816         if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
817
818         return TRUE;
819 }
820
821 static void image_reset(ImageWindow *imd)
822 {
823         /* stops anything currently being done */
824
825         if (debug) printf("image reset\n");
826
827         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
828
829         image_loader_free(imd->il);
830         imd->il = NULL;
831
832         color_man_free((ColorMan *)imd->cm);
833         imd->cm = NULL;
834
835         imd->delay_alter_type = ALTER_NONE;
836
837         image_state_set(imd, IMAGE_STATE_NONE);
838 }
839
840 /*
841  *-------------------------------------------------------------------
842  * image changer
843  *-------------------------------------------------------------------
844  */
845
846 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
847 {
848         image_reset(imd);
849
850         if (imd->image_path && isfile(imd->image_path))
851                 {
852                 PixbufRenderer *pr;
853
854                 pr = PIXBUF_RENDERER(imd->pr);
855                 pr->zoom = zoom;        /* store the zoom, needed by the loader */
856
857                 if (image_load_begin(imd, imd->image_path))
858                         {
859                         imd->unknown = FALSE;
860                         }
861                 else
862                         {
863                         GdkPixbuf *pixbuf;
864
865                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
866                         image_change_pixbuf(imd, pixbuf, zoom);
867                         g_object_unref(pixbuf);
868
869                         imd->unknown = TRUE;
870                         }
871                 imd->size = filesize(imd->image_path);
872                 imd->mtime = filetime(imd->image_path);
873                 }
874         else
875                 {
876                 if (imd->image_path)
877                         {
878                         GdkPixbuf *pixbuf;
879
880                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
881                         image_change_pixbuf(imd, pixbuf, zoom);
882                         g_object_unref(pixbuf);
883                         imd->mtime = filetime(imd->image_path);
884                         }
885                 else
886                         {
887                         image_change_pixbuf(imd, NULL, zoom);
888                         imd->mtime = 0;
889                         }
890                 imd->unknown = TRUE;
891                 imd->size = 0;
892                 }
893
894         image_update_util(imd);
895 }
896
897 static void image_change_real(ImageWindow *imd, const gchar *path,
898                               CollectionData *cd, CollectInfo *info, gdouble zoom)
899 {
900         GdkPixbuf *pixbuf;
901         GdkPixbuf *prev_pixbuf = NULL;
902         gchar *prev_path = NULL;
903         gint prev_clear = FALSE;
904         gint prev_color_row = -1;
905
906         imd->collection = cd;
907         imd->collection_info = info;
908
909         pixbuf = image_get_pixbuf(imd);
910
911         if (enable_read_ahead && imd->image_path && pixbuf)
912                 {
913                 if (imd->il)
914                         {
915                         /* current image is not finished */
916                         prev_clear = TRUE;
917                         }
918                 else
919                         {
920                         prev_path = g_strdup(imd->image_path);
921                         prev_pixbuf = pixbuf;
922                         g_object_ref(prev_pixbuf);
923
924                         if (imd->cm)
925                                 {
926                                 ColorMan *cm;
927
928                                 cm = (ColorMan *)imd->cm;
929                                 prev_color_row = cm->row;
930                                 }
931                         }
932                 }
933
934         g_free(imd->image_path);
935         imd->image_path = g_strdup(path);
936         imd->image_name = filename_from_path(imd->image_path);
937
938         image_change_complete(imd, zoom, TRUE);
939
940         if (prev_pixbuf)
941                 {
942                 image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
943                 g_free(prev_path);
944                 g_object_unref(prev_pixbuf);
945                 }
946         else if (prev_clear)
947                 {
948                 image_post_buffer_set(imd, NULL, NULL, -1);
949                 }
950
951         image_update_title(imd);
952         image_state_set(imd, IMAGE_STATE_IMAGE);
953 }
954
955 /*
956  *-------------------------------------------------------------------
957  * focus stuff
958  *-------------------------------------------------------------------
959  */
960
961 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
962 {
963         GtkWidget *widget;
964
965         widget = imd->widget;
966         if (!widget->window) return;
967
968         if (has_focus)
969                 {
970                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
971                                  area, widget, "image_window",
972                                  widget->allocation.x, widget->allocation.y,
973                                  widget->allocation.width - 1, widget->allocation.height - 1);  
974                 }
975         else
976                 {
977                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
978                                   area, widget, "image_window",
979                                   widget->allocation.x, widget->allocation.y,
980                                   widget->allocation.width - 1, widget->allocation.height - 1);
981                 }
982 }
983
984 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
985 {
986         ImageWindow *imd = data;
987
988         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
989         return TRUE;
990 }
991
992 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
993 {
994         ImageWindow *imd = data;
995
996         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
997         image_focus_paint(imd, TRUE, NULL);
998
999         return TRUE;
1000 }
1001
1002 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
1003 {
1004         ImageWindow *imd = data;
1005
1006         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
1007         image_focus_paint(imd, FALSE, NULL);
1008
1009         return TRUE;
1010 }
1011
1012 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
1013                        gint relative, gint always)
1014 {
1015         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
1016 }
1017
1018 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
1019 {
1020         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1021 }
1022
1023 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
1024 {
1025         return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
1026 }
1027
1028 void image_overlay_remove(ImageWindow *imd, gint id)
1029 {
1030         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
1031 }
1032
1033 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
1034 {
1035         ImageWindow *imd = data;
1036
1037         if (imd->func_scroll &&
1038             event && event->type == GDK_SCROLL)
1039                 {
1040                 imd->func_scroll(imd, event->direction, event->time,
1041                                  event->x, event->y, event->state, imd->data_scroll);
1042                 return TRUE;
1043                 }
1044
1045         return FALSE;
1046 }
1047
1048 /*
1049  *-------------------------------------------------------------------
1050  * public interface
1051  *-------------------------------------------------------------------
1052  */
1053
1054 void image_attach_window(ImageWindow *imd, GtkWidget *window,
1055                          const gchar *title, const gchar *title_right, gint show_zoom)
1056 {
1057         imd->top_window = window;
1058         g_free(imd->title);
1059         imd->title = g_strdup(title);
1060         g_free(imd->title_right);
1061         imd->title_right = g_strdup(title_right);
1062         imd->title_show_zoom = show_zoom;
1063
1064         if (!fit_window) window = NULL;
1065
1066         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
1067
1068         image_update_title(imd);
1069 }
1070
1071 void image_set_update_func(ImageWindow *imd,
1072                            void (*func)(ImageWindow *imd, gpointer data),
1073                            gpointer data)
1074 {
1075         imd->func_update = func;
1076         imd->data_update = data;
1077 }
1078
1079 void image_set_complete_func(ImageWindow *imd,
1080                              void (*func)(ImageWindow *imd, gint preload, gpointer data),
1081                              gpointer data)
1082 {
1083         imd->func_complete = func;
1084         imd->data_complete = data;
1085 }
1086
1087 void image_set_state_func(ImageWindow *imd,
1088                         void (*func)(ImageWindow *imd, ImageState state, gpointer data),
1089                         gpointer data)
1090 {
1091         imd->func_state = func;
1092         imd->data_state = data;
1093 }
1094
1095
1096 void image_set_button_func(ImageWindow *imd,
1097                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1098                            gpointer data)
1099 {
1100         imd->func_button = func;
1101         imd->data_button = data;
1102 }
1103
1104 void image_set_scroll_func(ImageWindow *imd,
1105                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
1106                            gpointer data)
1107 {
1108         imd->func_scroll = func;
1109         imd->data_scroll = data;
1110 }
1111
1112 void image_set_scroll_notify_func(ImageWindow *imd,
1113                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
1114                                   gpointer data)
1115 {
1116         imd->func_scroll_notify = func;
1117         imd->data_scroll_notify = data;
1118 }
1119
1120 /* path, name */
1121
1122 const gchar *image_get_path(ImageWindow *imd)
1123 {
1124         return imd->image_path;
1125 }
1126
1127 const gchar *image_get_name(ImageWindow *imd)
1128 {
1129         return imd->image_name;
1130 }
1131
1132 /* merely changes path string, does not change the image! */
1133 void image_set_path(ImageWindow *imd, const gchar *newpath)
1134 {
1135         g_free(imd->image_path);
1136         imd->image_path = g_strdup(newpath);
1137         imd->image_name = filename_from_path(imd->image_path);
1138
1139         image_update_title(imd);
1140         image_state_set(imd, IMAGE_STATE_IMAGE);
1141 }
1142
1143 /* load a new image */
1144
1145 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
1146 {
1147         if (imd->image_path == path ||
1148             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
1149
1150         image_change_real(imd, path, NULL, NULL, zoom);
1151 }
1152
1153 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
1154 {
1155         return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
1156 }
1157
1158 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
1159 {
1160         pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
1161         image_state_set(imd, IMAGE_STATE_IMAGE);
1162 }
1163
1164 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
1165 {
1166         if (!cd || !info || !g_list_find(cd->list, info)) return;
1167
1168         image_change_real(imd, info->path, cd, info, zoom);
1169 }
1170
1171 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
1172 {
1173         if (collection_to_number(imd->collection) >= 0)
1174                 {
1175                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
1176                         {
1177                         if (info) *info = imd->collection_info;
1178                         }
1179                 else
1180                         {
1181                         if (info) *info = NULL;
1182                         }
1183                 return imd->collection;
1184                 }
1185
1186         if (info) *info = NULL;
1187         return NULL;
1188 }
1189
1190 static void image_loader_sync_data(ImageLoader *il, gpointer data)
1191 {
1192         /* change data for the callbacks directly */
1193
1194         il->data_area_ready = data;
1195         il->data_error = data;
1196         il->data_done = data;
1197         il->data_percent = data;
1198 }
1199
1200 /* this is more like a move function
1201  * it moves most data from source to imd
1202  */
1203 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
1204 {
1205         if (imd == source) return;
1206
1207         imd->unknown = source->unknown;
1208
1209         imd->collection = source->collection;
1210         imd->collection_info = source->collection_info;
1211         imd->size = source->size;
1212         imd->mtime = source->mtime;
1213
1214         image_set_path(imd, image_get_path(source));
1215
1216         image_loader_free(imd->il);
1217         imd->il = NULL;
1218
1219         if (source->il)
1220                 {
1221                 imd->il = source->il;
1222                 source->il = NULL;
1223
1224                 image_loader_sync_data(imd->il, imd);
1225
1226                 imd->delay_alter_type = source->delay_alter_type;
1227                 source->delay_alter_type = ALTER_NONE;
1228                 }
1229
1230         imd->color_profile_enable = source->color_profile_enable;
1231         imd->color_profile_input = source->color_profile_input;
1232         imd->color_profile_screen = source->color_profile_screen;
1233         imd->color_profile_use_image = source->color_profile_use_image;
1234         color_man_free((ColorMan *)imd->cm);
1235         imd->cm = NULL;
1236         if (source->cm)
1237                 {
1238                 ColorMan *cm;
1239
1240                 imd->cm = source->cm;
1241                 source->cm = NULL;
1242
1243                 cm = (ColorMan *)imd->cm;
1244                 cm->imd = imd;
1245                 cm->func_done_data = imd;
1246                 }
1247
1248         image_loader_free(imd->read_ahead_il);
1249         imd->read_ahead_il = source->read_ahead_il;
1250         source->read_ahead_il = NULL;
1251         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1252
1253         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1254         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1255         source->read_ahead_pixbuf = NULL;
1256
1257         g_free(imd->read_ahead_path);
1258         imd->read_ahead_path = source->read_ahead_path;
1259         source->read_ahead_path = NULL;
1260
1261         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1262         imd->prev_pixbuf = source->prev_pixbuf;
1263         source->prev_pixbuf = NULL;
1264         imd->prev_color_row = source->prev_color_row;
1265         source->prev_color_row = -1;
1266
1267         g_free(imd->prev_path);
1268         imd->prev_path = source->prev_path;
1269         source->prev_path = NULL;
1270
1271         imd->completed = source->completed;
1272         imd->state = source->state;
1273         source->state = IMAGE_STATE_NONE;
1274
1275         pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1276 }
1277
1278 /* manipulation */
1279
1280 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1281 {
1282         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1283 }
1284
1285 void image_reload(ImageWindow *imd)
1286 {
1287         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1288
1289         image_change_complete(imd, image_zoom_get(imd), FALSE);
1290 }
1291
1292 void image_scroll(ImageWindow *imd, gint x, gint y)
1293 {
1294         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1295 }
1296
1297 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1298                            gdouble x_align, gdouble y_align)
1299 {
1300         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1301 }
1302
1303 void image_alter(ImageWindow *imd, AlterType type)
1304 {
1305         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1306
1307         if (imd->il || imd->cm)
1308                 {
1309                 /* still loading, wait till done */
1310                 imd->delay_alter_type = type;
1311                 image_state_set(imd, IMAGE_STATE_ROTATE_USER);
1312
1313                 if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
1314                         {
1315                         image_state_unset(imd, IMAGE_STATE_ROTATE_AUTO);
1316                         }
1317                 return;
1318                 }
1319
1320         image_alter_real(imd, type, TRUE);
1321 }
1322
1323 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1324 {
1325         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1326 }
1327
1328 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1329 {
1330         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1331 }
1332
1333 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1334 {
1335         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1336 }
1337
1338 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1339 {
1340         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1341 }
1342
1343 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1344 {
1345         PixbufRenderer *pr;
1346         gdouble zoom;
1347         gint width, height;
1348
1349         pr = (PixbufRenderer *)imd->pr;
1350
1351         if (!pixbuf_renderer_get_pixbuf(pr) ||
1352             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1353
1354         if (vertical)
1355                 {
1356                 zoom = (gdouble)pr->window_height / height;
1357                 }
1358         else
1359                 {
1360                 zoom = (gdouble)pr->window_width / width;
1361                 }
1362
1363         if (zoom < 1.0)
1364                 {
1365                 zoom = 0.0 - 1.0 / zoom;
1366                 }
1367
1368         pixbuf_renderer_zoom_set(pr, zoom);
1369 }
1370
1371 gdouble image_zoom_get(ImageWindow *imd)
1372 {
1373         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1374 }
1375
1376 gdouble image_zoom_get_real(ImageWindow *imd)
1377 {
1378         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1379 }
1380
1381 gchar *image_zoom_get_as_text(ImageWindow *imd)
1382 {
1383         gdouble zoom;
1384         gdouble scale;
1385         gdouble l = 1.0;
1386         gdouble r = 1.0;
1387         gint pl = 0;
1388         gint pr = 0;
1389         gchar *approx = " ";
1390
1391         zoom = image_zoom_get(imd);
1392         scale = image_zoom_get_real(imd);
1393
1394         if (zoom > 0.0)
1395                 {
1396                 l = zoom;
1397                 }
1398         else if (zoom < 0.0)
1399                 {
1400                 r = 0.0 - zoom;
1401                 }
1402         else if (zoom == 0.0 && scale != 0.0)
1403                 {
1404                 if (scale >= 1.0)
1405                         {
1406                         l = scale;
1407                         }
1408                 else
1409                         {
1410                         r = 1.0 / scale;
1411                         }
1412                 approx = " ~";
1413                 }
1414
1415         if (rint(l) != l) pl = 1;
1416         if (rint(r) != r) pr = 1;
1417
1418         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1419 }
1420
1421 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1422 {
1423         gdouble zoom;
1424
1425         if (mode == ZOOM_RESET_ORIGINAL)
1426                 {
1427                 zoom = 1.0;
1428                 }
1429         else if (mode == ZOOM_RESET_FIT_WINDOW)
1430                 {
1431                 zoom = 0.0;
1432                 }
1433         else
1434                 {
1435                 if (imd)
1436                         {
1437                         zoom = image_zoom_get(imd);
1438                         }
1439                 else
1440                         {
1441                         zoom = 1.0;
1442                         }
1443                 }
1444
1445         return zoom;
1446 }
1447
1448 /* read ahead */
1449
1450 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1451 {
1452         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1453
1454         if (path)
1455                 {
1456                 image_read_ahead_set(imd, path);
1457                 }
1458         else
1459                 {
1460                 image_read_ahead_cancel(imd);
1461                 }
1462 }
1463
1464 static gint image_auto_refresh_cb(gpointer data)
1465 {
1466         ImageWindow *imd = data;
1467         time_t newtime;
1468         
1469         if (!imd || !image_get_pixbuf(imd) ||
1470             imd->il || !imd->image_path ||
1471             !update_on_time_change) return TRUE;
1472
1473         newtime = filetime(imd->image_path);
1474         if (newtime > 0 && newtime != imd->mtime)
1475                 {
1476                 imd->mtime = newtime;
1477                 image_reload(imd);
1478                 }
1479
1480         return TRUE;
1481 }
1482
1483 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1484
1485 void image_auto_refresh(ImageWindow *imd, gint interval)
1486 {
1487         if (!imd) return;
1488         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1489
1490         if (imd->auto_refresh_id > -1)
1491                 {
1492                 g_source_remove(imd->auto_refresh_id);
1493                 imd->auto_refresh_id = -1;
1494                 imd->auto_refresh_interval = -1;
1495                 }
1496
1497         if (interval < 0) return;
1498
1499         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1500
1501         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1502         imd->auto_refresh_interval = interval;
1503 }
1504
1505 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1506 {
1507         imd->top_window_sync = allow_sync;
1508
1509         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1510 }
1511
1512 void image_background_set_black(ImageWindow *imd, gint black)
1513 {
1514         pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1515 }
1516
1517 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1518 {
1519         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1520 }
1521
1522 void image_color_profile_set(ImageWindow *imd,
1523                              gint input_type, gint screen_type,
1524                              gint use_image)
1525 {
1526         if (!imd) return;
1527
1528         if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
1529             screen_type < 0 || screen_type > 1)
1530                 {
1531                 return;
1532                 }
1533
1534         imd->color_profile_input = input_type;
1535         imd->color_profile_screen = screen_type;
1536         imd->color_profile_use_image = use_image;
1537 }
1538
1539 gint image_color_profile_get(ImageWindow *imd,
1540                              gint *input_type, gint *screen_type,
1541                              gint *use_image)
1542 {
1543         if (!imd) return FALSE;
1544
1545         if (input_type) *input_type = imd->color_profile_input;
1546         if (screen_type) *screen_type = imd->color_profile_screen;
1547         if (use_image) *use_image = imd->color_profile_use_image;
1548
1549         return TRUE;
1550 }
1551
1552 void image_color_profile_set_use(ImageWindow *imd, gint enable)
1553 {
1554         if (!imd) return;
1555
1556         if (imd->color_profile_enable == enable) return;
1557
1558         imd->color_profile_enable = enable;
1559 }
1560
1561 gint image_color_profile_get_use(ImageWindow *imd)
1562 {
1563         if (!imd) return FALSE;
1564
1565         return imd->color_profile_enable;
1566 }
1567
1568 void image_set_delay_flip(ImageWindow *imd, gint delay)
1569 {
1570         if (!imd ||
1571             imd->delay_flip == delay) return;
1572
1573         imd->delay_flip = delay;
1574
1575         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1576
1577         if (!imd->delay_flip && imd->il)
1578                 {
1579                 PixbufRenderer *pr;
1580
1581                 pr = PIXBUF_RENDERER(imd->pr);
1582                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1583                 pr->pixbuf = NULL;
1584
1585                 image_load_pixbuf_ready(imd);
1586                 }
1587 }
1588
1589 void image_to_root_window(ImageWindow *imd, gint scaled)
1590 {
1591         GdkScreen *screen;
1592         GdkWindow *rootwindow;
1593         GdkPixmap *pixmap;
1594         GdkPixbuf *pixbuf;
1595         GdkPixbuf *pb;
1596         gint width, height;
1597
1598         if (!imd) return;
1599
1600         pixbuf = image_get_pixbuf(imd);
1601         if (!pixbuf) return;
1602
1603         screen = gtk_widget_get_screen(imd->widget);
1604         rootwindow = gdk_screen_get_root_window(screen);
1605         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1606
1607         if (scaled)
1608                 {
1609                 width = gdk_screen_width();
1610                 height = gdk_screen_height();
1611                 }
1612         else
1613                 {
1614                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1615                 }
1616
1617         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1618
1619         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1620         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1621         gdk_window_clear(rootwindow);
1622         g_object_unref(pb);
1623         g_object_unref(pixmap);
1624
1625         gdk_flush();
1626 }
1627
1628 /*
1629  *-------------------------------------------------------------------
1630  * prefs sync
1631  *-------------------------------------------------------------------
1632  */
1633
1634 static void image_options_set(ImageWindow *imd)
1635 {
1636         g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1637                                         "zoom_2pass", two_pass_zoom,
1638                                         "zoom_expand", zoom_to_fit_expands,
1639                                         "dither_quality", dither_quality,
1640                                         "scroll_reset", scroll_reset_method,
1641                                         "cache_display", tile_cache_max,
1642                                         "window_fit", (imd->top_window_sync && fit_window),
1643                                         "window_limit", limit_window_size,
1644                                         "window_limit_value", max_window_size,
1645                                         NULL);
1646
1647         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1648 }
1649
1650 void image_options_sync(void)
1651 {
1652         GList *work;
1653
1654         work = image_list;
1655         while (work)
1656                 {
1657                 ImageWindow *imd;
1658
1659                 imd = work->data;
1660                 work = work->next;
1661
1662                 image_options_set(imd);
1663                 }
1664 }
1665
1666 /*
1667  *-------------------------------------------------------------------
1668  * init / destroy
1669  *-------------------------------------------------------------------
1670  */
1671
1672 static void image_free(ImageWindow *imd)
1673 {
1674         image_list = g_list_remove(image_list, imd);
1675
1676         image_reset(imd);
1677
1678         image_read_ahead_cancel(imd);
1679         image_post_buffer_set(imd, NULL, NULL, -1);
1680         image_auto_refresh(imd, -1);
1681
1682         g_free(imd->image_path);
1683         g_free(imd->title);
1684         g_free(imd->title_right);
1685
1686         g_free(imd);
1687 }
1688
1689 static void image_destroy_cb(GtkObject *widget, gpointer data)
1690 {
1691         ImageWindow *imd = data;
1692         image_free(imd);
1693 }
1694
1695 ImageWindow *image_new(gint frame)
1696 {
1697         ImageWindow *imd;
1698
1699         imd = g_new0(ImageWindow, 1);
1700
1701         imd->top_window = NULL;
1702         imd->title = NULL;
1703         imd->title_right = NULL;
1704         imd->title_show_zoom = FALSE;
1705
1706         imd->unknown = TRUE;
1707
1708         imd->has_frame = frame;
1709         imd->top_window_sync = FALSE;
1710
1711         imd->delay_alter_type = ALTER_NONE;
1712
1713         imd->read_ahead_il = NULL;
1714         imd->read_ahead_pixbuf = NULL;
1715         imd->read_ahead_path = NULL;
1716
1717         imd->completed = FALSE;
1718         imd->state = IMAGE_STATE_NONE;
1719
1720         imd->color_profile_enable = FALSE;
1721         imd->color_profile_input = 0;
1722         imd->color_profile_screen = 0;
1723         imd->color_profile_use_image = FALSE;
1724
1725         imd->auto_refresh_id = -1;
1726         imd->auto_refresh_interval = -1;
1727
1728         imd->delay_flip = FALSE;
1729
1730         imd->func_update = NULL;
1731         imd->func_complete = NULL;
1732         imd->func_tile_request = NULL;
1733         imd->func_tile_dispose = NULL;
1734
1735         imd->func_button = NULL;
1736         imd->func_scroll = NULL;
1737
1738         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1739
1740         image_options_set(imd);
1741
1742         if (imd->has_frame)
1743                 {
1744                 imd->widget = gtk_frame_new(NULL);
1745                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1746                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1747                 gtk_widget_show(imd->pr);
1748
1749                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1750                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1751                                  G_CALLBACK(image_focus_in_cb), imd);
1752                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1753                                  G_CALLBACK(image_focus_out_cb), imd);
1754
1755                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1756                                        G_CALLBACK(image_focus_expose), imd);
1757                 }
1758         else
1759                 {
1760                 imd->widget = imd->pr;
1761                 }
1762
1763         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1764                          G_CALLBACK(image_click_cb), imd);
1765         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1766                          G_CALLBACK(image_scroll_notify_cb), imd);
1767
1768         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1769                          G_CALLBACK(image_scroll_cb), imd);
1770
1771         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1772                          G_CALLBACK(image_destroy_cb), imd);
1773
1774         g_signal_connect(G_OBJECT(imd->pr), "zoom",
1775                          G_CALLBACK(image_zoom_cb), imd);
1776         g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1777                          G_CALLBACK(image_render_complete_cb), imd);
1778
1779         image_list = g_list_append(image_list, imd);
1780
1781         return imd;
1782 }
1783