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