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