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