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