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