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