##### Note: GQview CVS on sourceforge is not always up to date, please use #####
[geeqie.git] / src / image.c
1 /*
2  * GQview
3  * (C) 2004 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 "exif.h"
20 #include "pixbuf_util.h"
21 #include "ui_fileops.h"
22
23 #include <math.h>
24
25
26 /* size to use when breaking up image pane for rendering */
27 #define IMAGE_TILE_SIZE 512
28
29 /* default min and max zoom */
30 #define IMAGE_ZOOM_MIN -32.0
31 #define IMAGE_ZOOM_MAX 32.0
32
33 /* size of the image loader buffer (512 bytes x defined number) */
34 #define IMAGE_LOAD_BUFFER_COUNT 8
35
36 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
37 #define IMAGE_THROTTLE_LARGER_IMAGES 1
38
39 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
40 #define IMAGE_THROTTLE_FACTOR 4
41
42 /* the file size at which throttling take place */
43 #define IMAGE_THROTTLE_THRESHOLD 1048576
44
45 /* distance to drag mouse to disable image flip */
46 #define IMAGE_DRAG_SCROLL_THRESHHOLD 4
47
48 /* increase pan rate when holding down shift */
49 #define IMAGE_PAN_SHIFT_MULTIPLIER 6
50
51 /* alpha channel checkerboard background (same as gimp) */
52 #define IMAGE_ALPHA_CHECK1 0x00999999
53 #define IMAGE_ALPHA_CHECK2 0x00666666
54 #define IMAGE_ALPHA_CHECK_SIZE 16
55
56 #define IMAGE_AUTO_REFRESH_TIME 3000
57
58 /* when scaling image to below this size, use nearest pixel for scaling
59  * (below about 4, the other scale types become slow generating their conversion tables)
60  */
61 #define IMAGE_MIN_SCALE_SIZE 8
62
63
64 typedef enum {
65         TILE_RENDER_NONE = 0,   /* do nothing */
66         TILE_RENDER_AREA,       /* render an area of the tile */
67         TILE_RENDER_ALL         /* render the whole tile */
68 } TileRenderType;
69
70 typedef struct _ImageTile ImageTile;
71 struct _ImageTile
72 {
73         GdkPixmap *pixmap;      /* off screen buffer */
74         GdkPixbuf *pixbuf;      /* pixbuf area for zooming */
75         gint x;                 /* x offset into image */
76         gint y;                 /* y offset into image */
77         gint w;                 /* width that is visible (may be less if at edge of image) */
78         gint h;                 /* height '' */
79
80         gint blank;
81
82 /* render_todo: (explanation)
83         NONE    do nothing
84         AREA    render area of tile, usually only used when loading an image
85                 note: will jump to an ALL if render_done is not ALL.
86         ALL     render entire tile, if never done before w/ ALL, for expose events *only*
87 */
88
89         TileRenderType render_todo;     /* what to do (see above) */
90         TileRenderType render_done;     /* highest that has been done before on tile */
91 };
92
93 typedef struct _QueueData QueueData;
94 struct _QueueData
95 {
96         ImageTile *it;
97         gint x;
98         gint y;
99         gint w;
100         gint h;
101         gint new_data;
102 };
103
104 typedef struct _CacheData CacheData;
105 struct _CacheData
106 {
107         GdkPixmap *pixmap;
108         GdkPixbuf *pixbuf;
109         ImageTile *it;
110         guint size;
111 };
112
113 typedef struct _OverlayData OverlayData;
114 struct _OverlayData
115 {
116         gint id;
117
118         GdkPixbuf *pixbuf;
119
120         gint x;
121         gint y;
122         gint relative;  /* x,y coordinates are relative, negative values start bottom right */
123
124         gint visible;
125         gint always;    /* hide temporarily when scrolling */
126 };
127
128 /* needed to be declared before the source_tile stuff */
129 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new);
130 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
131                             gint force, gint blank, gint new,
132                             gint center_point, gint px, gint py);
133 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
134                         gint clamp, TileRenderType render, gint new_data);
135
136 static gint util_clip_region(gint x, gint y, gint w, gint h,
137                              gint clip_x, gint clip_y, gint clip_w, gint clip_h,
138                              gint *rx, gint *ry, gint *rw, gint *rh);
139
140 /*
141  *-------------------------------------------------------------------
142  * source tiles
143  *-------------------------------------------------------------------
144  */
145
146 typedef struct _SourceTile SourceTile;
147 struct _SourceTile
148 {
149         gint x;
150         gint y;
151         GdkPixbuf *pixbuf;
152         gint blank;
153 };
154
155 static void source_tile_free(SourceTile *st)
156 {
157         if (!st) return;
158
159         if (st->pixbuf) gdk_pixbuf_unref(st->pixbuf);
160         g_free(st);
161 }
162
163 static void source_tile_free_all(ImageWindow *imd)
164 {
165         GList *work;
166
167         work = imd->source_tiles;
168         while (work)
169                 {
170                 SourceTile *st = work->data;
171                 work = work->next;
172
173                 source_tile_free(st);
174                 }
175
176         g_list_free(imd->source_tiles);
177         imd->source_tiles = NULL;
178 }
179
180 static gint source_tile_visible(ImageWindow *imd, SourceTile *st)
181 {
182         gint x, y, w, h;
183
184         if (!st) return FALSE;
185
186         x = (imd->x_scroll / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE;
187         y = (imd->y_scroll / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE;
188         w = ((imd->x_scroll + imd->vis_width) / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE + IMAGE_TILE_SIZE;
189         h = ((imd->y_scroll + imd->vis_height) / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE + IMAGE_TILE_SIZE;
190
191         return !((double)st->x * imd->scale < (double)x ||
192                  (double)(st->x + imd->source_tile_width) * imd->scale > (double)w ||
193                  (double)st->y * imd->scale < (double)y ||
194                  (double)(st->y + imd->source_tile_height) * imd->scale > (double)h);
195 }
196
197 static SourceTile *source_tile_new(ImageWindow *imd, gint x, gint y)
198 {
199         SourceTile *st = NULL;
200         gint count;
201
202         if (imd->source_tiles_cache_size < 4) imd->source_tiles_cache_size = 4;
203
204         if (imd->source_tile_width < 1 || imd->source_tile_height < 1)
205                 {
206                 printf("warning: source tile size too small %d x %d\n", imd->source_tile_width, imd->source_tile_height);
207                 return NULL;
208                 }
209
210         count = g_list_length(imd->source_tiles);
211         if (count >= imd->source_tiles_cache_size)
212                 {
213                 GList *work;
214
215                 work = g_list_last(imd->source_tiles);
216                 while (work && count >= imd->source_tiles_cache_size)
217                         {
218                         SourceTile *needle;
219
220                         needle = work->data;
221                         work = work->prev;
222
223                         if (!source_tile_visible(imd, needle))
224                                 {
225                                 imd->source_tiles = g_list_remove(imd->source_tiles, needle);
226
227                                 if (imd->func_tile_dispose)
228                                         {
229                                         if (debug) printf("tile dispose: %d x %d @ %d x %d\n",
230                                                          needle->x, needle->y, imd->x_scroll, imd->y_scroll);
231                                         imd->func_tile_dispose(imd, needle->x, needle->y,
232                                                                imd->source_tile_width, imd->source_tile_height,
233                                                                needle->pixbuf, imd->data_tile);
234                                         }
235
236                                 if (!st)
237                                         {
238                                         st = needle;
239                                         }
240                                 else
241                                         {
242                                         source_tile_free(needle);
243                                         }
244
245                                 count--;
246                                 }
247                         else if (debug)
248                                 {
249                                 printf("we still think %d x %d is visble\n", needle->x, needle->y);
250                                 }
251                         }
252
253                 if (debug)
254                         {
255                         printf("cache count %d, max is %d\n", count, imd->source_tiles_cache_size);
256                         }
257                 }
258
259         if (!st)
260                 {
261                 st = g_new0(SourceTile, 1);
262                 st->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
263                                             imd->source_tile_width, imd->source_tile_height);
264                 }
265
266         st->x = (x / imd->source_tile_width) * imd->source_tile_width;
267         st->y = (y / imd->source_tile_height) * imd->source_tile_height;
268         st->blank = TRUE;
269
270         imd->source_tiles = g_list_prepend(imd->source_tiles, st);
271
272         if (debug)
273                 {
274                 printf("tile request: %d x %d\n", st->x, st->y);
275                 if (!source_tile_visible(imd, st)) printf("tile request for invisible tile!\n");
276                 }
277
278         return st;
279 }
280
281 static void image_tile_invalidate(ImageWindow *imd, gint x, gint y, gint w, gint h)
282 {
283         gint i, j;
284         gint x1, x2;
285         gint y1, y2;
286         GList *work;
287
288         x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
289         x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
290
291         y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
292         y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
293
294         work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
295         for (j = y1; j <= y2; j += imd->tile_height)
296                 {
297                 GList *tmp;
298                 tmp = work;
299                 for (i = x1; i <= x2; i += imd->tile_width)
300                         {
301                         if (tmp)
302                                 {
303                                 ImageTile *it = tmp->data;
304
305                                 it->render_done = TILE_RENDER_NONE;
306                                 it->render_todo = TILE_RENDER_ALL;
307
308                                 tmp = tmp->next;
309                                 }
310                         }
311                 work = g_list_nth(work, imd->tile_cols);        /* step 1 row */
312                 }
313 }
314
315 static SourceTile *source_tile_request(ImageWindow *imd, gint x, gint y)
316 {
317         SourceTile *st;
318
319         st = source_tile_new(imd, x, y);
320
321         if (imd->func_tile_request &&
322             imd->func_tile_request(imd, st->x, st->y,
323                                    imd->source_tile_width, imd->source_tile_height, st->pixbuf, imd->data_tile))
324                 {
325                 st->blank = FALSE;
326                 }
327 #if 0
328         /* fixme: somehow invalidate the new st region */
329         image_queue(imd, st->x, st->y, imd->source_tile_width, imd->source_tile_height, FALSE, TILE_RENDER_AREA, TRUE);
330 #endif
331         image_tile_invalidate(imd, st->x * imd->scale, st->y * imd->scale,
332                               imd->source_tile_width * imd->scale, imd->source_tile_height * imd->scale);
333
334         return st;
335 }
336
337 static SourceTile *source_tile_find(ImageWindow *imd, gint x, gint y)
338 {
339         GList *work;
340
341         work = imd->source_tiles;
342         while (work)
343                 {
344                 SourceTile *st = work->data;
345
346                 if (x >= st->x && x < st->x + imd->source_tile_width &&
347                     y >= st->y && y < st->y + imd->source_tile_height)
348                         {
349                         if (work != imd->source_tiles)
350                                 {
351                                 imd->source_tiles = g_list_remove_link(imd->source_tiles, work);
352                                 imd->source_tiles = g_list_concat(work, imd->source_tiles);
353                                 }
354                         return st;
355                         }
356
357                 work = work->next;
358                 }
359
360         return NULL;
361 }
362
363 static GList *source_tile_compute_region(ImageWindow *imd, gint x, gint y, gint w, gint h, gint request)
364 {
365         gint x1, y1;
366         GList *list = NULL;
367         gint sx, sy;
368
369         if (x < 0) x = 0;
370         if (y < 0) y = 0;
371         if (w > imd->image_width) w = imd->image_width;
372         if (h > imd->image_height) h = imd->image_height;
373
374         sx = (x / imd->source_tile_width) * imd->source_tile_width;
375         sy = (y / imd->source_tile_height) * imd->source_tile_height;
376
377         for (x1 = sx; x1 < x + w; x1+= imd->source_tile_width)
378                 {
379                 for (y1 = sy; y1 < y + h; y1 += imd->source_tile_height)
380                         {
381                         SourceTile *st;
382
383                         st = source_tile_find(imd, x1, y1);
384                         if (!st && request) st = source_tile_request(imd, x1, y1);
385
386                         if (st) list = g_list_prepend(list, st);
387                         }
388                 }
389
390         return g_list_reverse(list);
391 }
392
393 static void source_tile_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
394 {
395         GList *work;
396
397         work = imd->source_tiles;
398         while (work)
399                 {
400                 SourceTile *st;
401                 gint rx, ry, rw, rh;
402
403                 st = work->data;
404                 work = work->next;
405
406                 if (util_clip_region(st->x, st->y, imd->source_tile_width, imd->source_tile_height,
407                                      x, y, width, height,
408                                      &rx, &ry, &rw, &rh))
409                         {
410                         GdkPixbuf *pixbuf;
411
412                         pixbuf = gdk_pixbuf_new_subpixbuf(st->pixbuf, rx - st->x, ry - st->y, rw, rh);
413                         if (imd->func_tile_request &&
414                             imd->func_tile_request(imd, rx, ry, rw, rh, pixbuf, imd->data_tile))
415                                 {
416                                 image_tile_invalidate(imd, rx * imd->scale, ry * imd->scale, rw * imd->scale, rh * imd->scale);
417                                 }
418                         g_object_unref(pixbuf);
419                         }
420                 }
421 }
422
423
424 static gint source_tile_render(ImageWindow *imd, ImageTile *it,
425                                gint x, gint y, gint w, gint h,
426                                gint new_data, gint fast)
427 {
428         GList *list;
429         GList *work;
430         gint draw = FALSE;
431
432         if (imd->zoom == 1.0 || imd->scale == 1.0)
433                 {
434                 list = source_tile_compute_region(imd, it->x + x, it->y + y, w, h, TRUE);
435                 work = list;
436                 while (work)
437                         {
438                         SourceTile *st;
439                         gint rx, ry, rw, rh;
440
441                         st = work->data;
442                         work = work->next;
443
444                         if (util_clip_region(st->x, st->y, imd->source_tile_width, imd->source_tile_height,
445                                              it->x + x, it->y + y, w, h,
446                                              &rx, &ry, &rw, &rh))
447                                 {
448                                 if (st->blank)
449                                         {
450                                         gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
451                                                            rx - st->x, ry - st->y, rw, rh);
452                                         }
453                                 else /* (imd->zoom == 1.0 || imd->scale == 1.0) */
454                                         {
455                                         gdk_draw_pixbuf(it->pixmap,
456                                                         imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
457                                                         st->pixbuf,
458                                                         rx - st->x, ry - st->y,
459                                                         rx - it->x, ry - it->y,
460                                                         rw, rh,
461                                                         (GdkRgbDither)dither_quality, rx, ry);
462                                         }
463                                 }
464                         }
465                 }
466         else
467                 {
468                 double scale_x, scale_y;
469                 gint sx, sy, sw, sh;
470
471                 if (imd->image_width == 0 || imd->image_height == 0) return FALSE;
472                 scale_x = (double)imd->width / imd->image_width;
473                 scale_y = (double)imd->height / imd->image_height;
474
475                 sx = (double)(it->x + x) / scale_x;
476                 sy = (double)(it->y + y) / scale_y;
477                 sw = (double)w / scale_x;
478                 sh = (double)h / scale_y;
479
480                 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
481
482 #if 0
483                 /* draws red over draw region, to check for leaks (regions not filled) */
484                 pixbuf_draw_rect(it->pixbuf, x, y, w, h, 255, 0, 0, 255, FALSE);
485 #endif
486
487                 list = source_tile_compute_region(imd, sx, sy, sw, sh, TRUE);
488                 work = list;
489                 while (work)
490                         {
491                         SourceTile *st;
492                         gint rx, ry, rw, rh;
493                         gint stx, sty, stw, sth;
494
495                         st = work->data;
496                         work = work->next;
497
498                         stx = floor((double)st->x * scale_x);
499                         sty = floor((double)st->y * scale_y);
500                         stw = ceil ((double)(st->x + imd->source_tile_width) * scale_x) - stx;
501                         sth = ceil ((double)(st->y + imd->source_tile_height) * scale_y) - sty;
502
503                         if (util_clip_region(stx, sty, stw, sth,
504                                              it->x + x, it->y + y, w, h,
505                                              &rx, &ry, &rw, &rh))
506                                 {
507                                 if (st->blank)
508                                         {
509                                         gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
510                                            rx - st->x, ry - st->y, rw, rh);
511                                         }
512                                 else
513                                         {
514                                         double offset_x;
515                                         double offset_y;
516
517                                         /* may need to use unfloored stx,sty values here */
518                                         offset_x = ((double)stx < (double)it->x) ?
519                                                     (double)stx - (double)it->x  : 0.0;
520                                         offset_y = ((double)sty < (double)it->y) ?
521                                                     (double)sty - (double)it->y  : 0.0;
522
523                                         gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh,
524                                                  (double) 0.0 + rx - it->x + offset_x,
525                                                  (double) 0.0 + ry - it->y + offset_y,
526                                                  scale_x, scale_y,
527                                                  (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
528                                         draw = TRUE;
529                                         }
530                                 }
531                         }
532                 }
533
534         g_list_free(list);
535
536         return draw;
537 }
538
539 static void image_source_tile_unset(ImageWindow *imd)
540 {
541         source_tile_free_all(imd);
542
543         imd->source_tiles_enabled = FALSE;
544 }
545
546 void image_set_image_as_tiles(ImageWindow *imd, gint width, gint height,
547                               gint tile_width, gint tile_height, gint cache_size,
548                               ImageTileRequestFunc func_tile_request,
549                               ImageTileDisposeFunc func_tile_dispose,
550                               gpointer data,
551                               gdouble zoom)
552 {
553         /* FIXME: unset any current image */
554         image_source_tile_unset(imd);
555
556         if (tile_width < 32 || tile_height < 32)
557                 {
558                 printf("warning: tile size too small %d x %d (min 32x32)\n", tile_width, tile_height);
559                 return;
560                 }
561         if (width < 32 || height < 32)
562                 {
563                 printf("warning: tile canvas too small %d x %d (min 32x32)\n", width, height);
564                 return;
565                 }
566         if (!func_tile_request)
567                 {
568                 printf("warning: tile request function is null\n");
569                 }
570
571         printf("Setting source tiles to size %d x %d, grid is %d x %d\n", tile_width, tile_height, width, height);
572
573         if (cache_size < 4) cache_size = 4;
574
575         imd->source_tiles_enabled = TRUE;
576         imd->source_tiles_cache_size = cache_size;
577         imd->source_tile_width = tile_width;
578         imd->source_tile_height = tile_height;
579
580         imd->image_width = width;
581         imd->image_height = height;
582
583         imd->func_tile_request = func_tile_request;
584         imd->func_tile_dispose = func_tile_dispose;
585         imd->data_tile = data;
586
587         image_zoom_sync(imd, zoom, TRUE, FALSE, TRUE, FALSE, 0, 0);
588 }
589
590
591 static void image_queue_clear(ImageWindow *imd);
592
593 static void image_update_title(ImageWindow *imd);
594 static void image_update_util(ImageWindow *imd);
595 static void image_complete_util(ImageWindow *imd, gint preload);
596
597 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent);
598
599 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h);
600 static void image_overlay_queue_all(ImageWindow *imd);
601
602 static void image_scroller_timer_set(ImageWindow *imd, gint start);
603
604
605 static gint util_clip_region(gint x, gint y, gint w, gint h,
606                              gint clip_x, gint clip_y, gint clip_w, gint clip_h,
607                              gint *rx, gint *ry, gint *rw, gint *rh)
608 {
609         if (clip_x + clip_w <= x ||
610             clip_x >= x + w ||
611             clip_y + clip_h <= y ||
612             clip_y >= y + h)
613                 {
614                 return FALSE;
615                 }
616
617         *rx = MAX(x, clip_x);
618         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
619
620         *ry = MAX(y, clip_y);
621         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
622
623         return TRUE;
624 }
625
626
627 /*
628  *-------------------------------------------------------------------
629  * tile cache
630  *-------------------------------------------------------------------
631  */
632
633 static gint pixmap_calc_size(GdkPixmap *pixmap)
634 {
635         gint w, h, d;
636
637         d = gdk_drawable_get_depth(pixmap);
638         gdk_drawable_get_size(pixmap, &w, &h);
639         return w * h * (d / 8);
640 }
641
642 static void image_tile_cache_remove(ImageWindow *imd, ImageTile *it)
643 {
644         GList *work;
645
646         work = imd->tile_cache;
647         while(work)
648                 {
649                 CacheData *cd = work->data;
650                 work = work->next;
651
652                 if (cd->it == it)
653                         {
654                         imd->tile_cache = g_list_remove(imd->tile_cache, cd);
655                         imd->tile_cache_size -= cd->size;
656                         g_free(cd);
657                         }
658                 }
659 }
660
661 static void image_tile_cache_free(ImageWindow *imd, CacheData *cd)
662 {
663         imd->tile_cache = g_list_remove(imd->tile_cache, cd);
664         if (cd->pixmap)
665                 {
666                 g_object_unref(cd->it->pixmap);
667                 cd->it->pixmap = NULL;
668                 cd->it->render_done = TILE_RENDER_NONE;
669                 }
670         if (cd->pixbuf)
671                 {
672                 gdk_pixbuf_unref(cd->it->pixbuf);
673                 cd->it->pixbuf = NULL;
674                 }
675         imd->tile_cache_size -= cd->size;
676         g_free(cd);
677 }
678
679 static void image_tile_cache_free_space(ImageWindow *imd, gint space, ImageTile *it)
680 {
681         GList *work;
682         gint tile_max;
683
684         work = g_list_last(imd->tile_cache);
685
686         if (imd->source_tiles_enabled && imd->scale < 1.0)
687                 {
688                 gint tiles;
689
690                 tiles = (imd->vis_width / IMAGE_TILE_SIZE + 1) * (imd->vis_width / IMAGE_TILE_SIZE + 1);
691                 tile_max = MAX(tiles * IMAGE_TILE_SIZE * IMAGE_TILE_SIZE * 3,
692                                (gint)((double)tile_cache_max * 1048576.0 * imd->scale));
693                 }
694         else
695                 {
696                 tile_max = tile_cache_max * 1048576;
697                 }
698
699         while (work && imd->tile_cache_size > 0 && imd->tile_cache_size + space > tile_max)
700                 {
701                 CacheData *cd = work->data;
702                 work = work->prev;
703                 if (cd->it != it) image_tile_cache_free(imd, cd);
704                 }
705 }
706
707 static void image_tile_cache_add(ImageWindow *imd, ImageTile *it,
708                                  GdkPixmap *pixmap, GdkPixbuf *pixbuf, guint size)
709 {
710         CacheData *cd;
711
712         cd = g_new(CacheData, 1);
713         cd->pixmap = pixmap;
714         cd->pixbuf = pixbuf;
715         cd->it = it;
716         cd->size = size;
717
718         imd->tile_cache = g_list_prepend(imd->tile_cache, cd);
719
720         imd->tile_cache_size += cd->size;
721 }
722
723 static void image_tile_prepare(ImageWindow *imd, ImageTile *it)
724 {
725         if (!it->pixmap)
726                 {
727                 GdkPixmap *pixmap;
728                 guint size;
729
730                 pixmap = gdk_pixmap_new(imd->image->window, imd->tile_width, imd->tile_height, -1);
731
732                 size = pixmap_calc_size(pixmap);
733                 image_tile_cache_free_space(imd, size, it);
734
735                 it->pixmap = pixmap;
736                 image_tile_cache_add(imd, it, pixmap, NULL, size);
737                 }
738         
739         if ((imd->zoom != 1.0 || imd->source_tiles_enabled || (imd->pixbuf && gdk_pixbuf_get_has_alpha(imd->pixbuf)) ) &&
740             !it->pixbuf)
741                 {
742                 GdkPixbuf *pixbuf;
743                 guint size;
744
745                 if (imd->pixbuf)
746                         {
747                         pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(imd->pixbuf),
748                                                 gdk_pixbuf_get_has_alpha(imd->pixbuf),
749                                                 gdk_pixbuf_get_bits_per_sample(imd->pixbuf),
750                                                 imd->tile_width, imd->tile_height);
751                         }
752                 else
753                         {
754                          pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, imd->tile_width, imd->tile_height);
755                         }
756
757                 size = gdk_pixbuf_get_rowstride(pixbuf) * imd->tile_height;
758                 image_tile_cache_free_space(imd, size, it);
759
760                 it->pixbuf = pixbuf;
761                 image_tile_cache_add(imd, it, NULL, pixbuf, size);
762                 }
763 }
764
765 /*
766  *-------------------------------------------------------------------
767  * tiles
768  *-------------------------------------------------------------------
769  */
770
771 static ImageTile *image_tile_new(gint w, gint h)
772 {
773         ImageTile *it;
774
775         it = g_new0(ImageTile, 1);
776         it->w = w;
777         it->h = h;
778         it->pixmap = NULL;
779         it->pixbuf = NULL;
780         it->blank = TRUE;
781         it->render_todo = TILE_RENDER_NONE;
782         it->render_done = TILE_RENDER_NONE;
783
784         return it;
785 }
786
787 static void image_tile_free(ImageTile *it)
788 {
789         if (!it) return;
790
791         if (it->pixbuf) gdk_pixbuf_unref(it->pixbuf);
792         if (it->pixmap) g_object_unref(it->pixmap);
793
794         g_free(it);
795 }
796
797 static void image_tile_sync_count(ImageWindow *imd, gint n)
798 {
799         gint l;
800
801         l = g_list_length(imd->tiles);
802
803         if (l == n) return;
804
805         if (l < n)
806                 {
807                 while (l < n)
808                         {
809                         imd->tiles = g_list_prepend(imd->tiles, image_tile_new(imd->tile_width, imd->tile_height));
810                         l++;
811                         }
812                 }
813         else
814                 {
815                 /* This should remove from the tail of the GList, but with large images there are many tiles,
816                  * making this significantly faster for those cases.
817                  */
818                 while (l > n && imd->tiles)
819                         {
820                         ImageTile *it = imd->tiles->data;
821                         imd->tiles = g_list_remove(imd->tiles, it);
822                         image_tile_cache_remove(imd, it);
823                         image_tile_free(it);
824                         l--;
825                         }
826                 }
827 }
828
829 static void image_tile_sync(ImageWindow *imd, gint width, gint height, gint blank)
830 {
831         gint rows;
832         gint x, y;
833         GList *work;
834
835         imd->tile_cols = (width + imd->tile_width - 1) / imd->tile_width;
836
837         rows = (height + imd->tile_height - 1) / imd->tile_height;
838
839         image_tile_sync_count(imd, imd->tile_cols * rows);
840
841         x = y = 0;
842         work = imd->tiles;
843         while(work)
844                 {
845                 ImageTile *it = work->data;
846                 work = work->next;
847
848                 it->x = x;
849                 it->y = y;
850                 if (x + imd->tile_width > width)
851                         {
852                         it->w = width - x;
853                         }       
854                 else
855                         {
856                         it->w = imd->tile_width;
857                         }
858                 if (y + imd->tile_height > height)
859                         {
860                         it->h = height - y;
861                         }
862                 else
863                         {
864                         it->h = imd->tile_height;
865                         }
866
867                 it->blank = blank;
868                 it->render_todo = TILE_RENDER_NONE;
869                 it->render_done = TILE_RENDER_NONE;
870
871                 x += imd->tile_width;
872                 if (x >= width)
873                         {
874                         x = 0;
875                         y += imd->tile_height;
876                         }
877                 }
878
879         /* all it's are now useless in queue */
880         image_queue_clear(imd);
881 }
882
883 static void image_tile_render(ImageWindow *imd, ImageTile *it,
884                               gint x, gint y, gint w, gint h,
885                               gint new_data, gint fast)
886 {
887         gint has_alpha;
888         gint draw = FALSE;
889
890         if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return;
891
892         if (it->render_done != TILE_RENDER_ALL)
893                 {
894                 x = 0;
895                 y = 0;
896                 w = it->w;
897                 h = it->h;
898                 if (!fast) it->render_done = TILE_RENDER_ALL;
899                 }
900         else if (it->render_todo != TILE_RENDER_AREA)
901                 {
902                 if (!fast) it->render_todo = TILE_RENDER_NONE;
903                 return;
904                 }
905
906         if (!fast) it->render_todo = TILE_RENDER_NONE;
907
908         if (new_data) it->blank = FALSE;
909
910         image_tile_prepare(imd, it);
911         has_alpha = (imd->pixbuf && gdk_pixbuf_get_has_alpha(imd->pixbuf));
912
913         /* FIXME checker colors for alpha should be configurable,
914          * also should be drawn for blank = TRUE
915          */
916
917         if (it->blank)
918                 {
919                 /* no data, do fast rect fill */
920                 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
921                                    0, 0, it->w, it->h);
922                 }
923         else if (imd->source_tiles_enabled)
924                 {
925                 draw = source_tile_render(imd, it, x, y, w, h, new_data, fast);
926                 }
927         else if (imd->zoom == 1.0 || imd->scale == 1.0)
928                 {
929                 if (has_alpha)
930                         {
931                         gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
932                                          (double) 0.0 - it->x,
933                                          (double) 0.0 - it->y,
934                                          1.0, 1.0, GDK_INTERP_NEAREST,
935                                          255, it->x + x, it->y + y,
936                                          IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
937                         draw = TRUE;
938                         }
939                 else
940                         {
941                         /* faster, simple */
942                         gdk_draw_pixbuf(it->pixmap,
943                                         imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
944                                         imd->pixbuf,
945                                         it->x + x, it->y + y,
946                                         x, y,
947                                         w, h,
948                                         (GdkRgbDither)dither_quality, it->x + x, it->y + y);
949                         }
950                 }
951         else
952                 {
953                 double scale_x, scale_y;
954
955                 if (imd->image_width == 0 || imd->image_height == 0) return;
956                 scale_x = (double)imd->width / imd->image_width;
957                 scale_y = (double)imd->height / imd->image_height;
958
959                 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
960                  * small sizes for anything but GDK_INTERP_NEAREST
961                  */
962                 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
963
964                 if (!has_alpha)
965                         {
966                         gdk_pixbuf_scale(imd->pixbuf, it->pixbuf, x, y, w, h,
967                                          (double) 0.0 - it->x,
968                                          (double) 0.0 - it->y,
969                                          scale_x, scale_y,
970                                          (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
971                         }
972                 else
973                         {
974                         gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
975                                          (double) 0.0 - it->x,
976                                          (double) 0.0 - it->y,
977                                          scale_x, scale_y,
978                                          (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality,
979                                          255, it->x + x, it->y + y,
980                                          IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
981                         }
982                 draw = TRUE;
983                 }
984
985         if (draw && it->pixbuf && !it->blank)
986                 {
987                 gdk_draw_pixbuf(it->pixmap,
988                                 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
989                                 it->pixbuf,
990                                 x, y,
991                                 x, y,
992                                 w, h,
993                                 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
994                 }
995 }
996
997 static void image_tile_expose(ImageWindow *imd, ImageTile *it,
998                               gint x, gint y, gint w, gint h,
999                               gint new_data, gint fast)
1000 {
1001         image_tile_render(imd, it, x, y, w, h, new_data, fast);
1002
1003         gdk_draw_drawable(imd->image->window, imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
1004                           it->pixmap, x, y,
1005                           imd->x_offset + (it->x - imd->x_scroll) + x, imd->y_offset + (it->y - imd->y_scroll) + y, w, h);
1006
1007         if (imd->overlay_list)
1008                 {
1009                 image_overlay_draw(imd, imd->x_offset + (it->x - imd->x_scroll) + x,
1010                                         imd->y_offset + (it->y - imd->y_scroll) + y,
1011                                         w, h);
1012                 }
1013 }
1014
1015 static gint image_tile_is_visible(ImageWindow *imd, ImageTile *it)
1016 {
1017         return (it->x + it->w >= imd->x_scroll && it->x <= imd->x_scroll + imd->window_width - imd->x_offset * 2 &&
1018                 it->y + it->h >= imd->y_scroll && it->y <= imd->y_scroll + imd->window_height - imd->y_offset * 2);
1019 }
1020
1021 /*
1022  *-------------------------------------------------------------------
1023  * render queue
1024  *-------------------------------------------------------------------
1025  */
1026
1027
1028 static gint image_queue_draw_idle_cb(gpointer data)
1029 {
1030         ImageWindow *imd = data;
1031         QueueData *qd;
1032         gint fast;
1033
1034         if ((!imd->pixbuf && !imd->source_tiles_enabled) || (!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1)
1035                 {
1036                 if (!imd->completed) image_complete_util(imd, FALSE);
1037
1038                 imd->draw_idle_id = -1;
1039                 return FALSE;
1040                 }
1041
1042         if (imd->draw_queue)
1043                 {
1044                 qd = imd->draw_queue->data;
1045                 fast = (two_pass_zoom && (GdkInterpType)zoom_quality != GDK_INTERP_NEAREST && imd->scale != 1.0);
1046                 }
1047         else
1048                 {
1049                 if (imd->il)
1050                         {
1051                         /* still loading, wait till done (also drops the higher priority) */
1052
1053                         imd->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
1054                                                             image_queue_draw_idle_cb, imd, NULL);
1055                         imd->draw_idle_high = FALSE;
1056                         return FALSE;
1057                         }
1058                 qd = imd->draw_queue_2pass->data;
1059                 fast = FALSE;
1060                 }
1061
1062         if (GTK_WIDGET_REALIZED(imd->image))
1063                 {
1064                 if (image_tile_is_visible(imd, qd->it))
1065                         {
1066                         image_tile_expose(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1067                         }
1068                 else if (qd->new_data)
1069                         {
1070                         /* if new pixel data, and we already have a pixmap, update the tile */
1071                         qd->it->blank = FALSE;
1072                         if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
1073                                 {
1074                                 image_tile_render(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1075                                 }
1076                         }
1077                 }
1078
1079         if (imd->draw_queue)
1080                 {
1081                 imd->draw_queue = g_list_remove(imd->draw_queue, qd);
1082                 if (fast)
1083                         {
1084                         imd->draw_queue_2pass = g_list_append(imd->draw_queue_2pass, qd);
1085                         }
1086                 else
1087                         {
1088                         g_free(qd);
1089                         }
1090                 }
1091         else
1092                 {
1093                 imd->draw_queue_2pass = g_list_remove(imd->draw_queue_2pass, qd);
1094                 g_free(qd);
1095                 }
1096
1097         if (!imd->draw_queue && !imd->draw_queue_2pass)
1098                 {
1099                 if (!imd->completed) image_complete_util(imd, FALSE);
1100
1101                 imd->draw_idle_id = -1;
1102                 return FALSE;
1103                 }
1104
1105         return TRUE;
1106 }
1107
1108 static QueueData *image_queue_combine(ImageWindow *imd, QueueData *qd)
1109 {
1110         QueueData *found = NULL;
1111         GList *work;
1112
1113         work = imd->draw_queue;
1114         while (work && !found)
1115                 {
1116                 found = work->data;
1117                 work = work->next;
1118
1119                 if (found->it != qd->it) found = NULL;
1120                 }
1121
1122         if (found)
1123                 {
1124                 if (found->x + found->w < qd->x + qd->w) found->w += (qd->x + qd->w) - (found->x + found->w);
1125                 if (found->x > qd->x)
1126                         {
1127                         found->w += found->x - qd->x;
1128                         found->x = qd->x;
1129                         }
1130
1131                 if (found->y + found->h < qd->y + qd->h) found->h += (qd->y + qd->h) - (found->y + found->h);
1132                 if (found->y > qd->y)
1133                         {
1134                         found->h += found->y - qd->y;
1135                         found->y = qd->y;
1136                         }
1137                 found->new_data |= qd->new_data;
1138                 }
1139
1140         return found;
1141 }
1142
1143 static gint image_clamp_to_visible(ImageWindow *imd, gint *x, gint *y, gint *w, gint *h)
1144 {
1145         gint nx, ny;
1146         gint nw, nh;
1147         gint vx, vy;
1148         gint vw, vh;
1149
1150         vw = imd->vis_width;
1151         vh = imd->vis_height;
1152
1153         vx = imd->x_scroll;
1154         vy = imd->y_scroll;
1155
1156         if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1157
1158         /* now clamp it */
1159         nx = CLAMP(*x, vx, vx + vw);
1160         nw = CLAMP(*w - (nx - *x), 1, vw);
1161
1162         ny = CLAMP(*y, vy, vy + vh);
1163         nh = CLAMP(*h - (ny - *y), 1, vh);
1164
1165         *x = nx;
1166         *y = ny;
1167         *w = nw;
1168         *h = nh;
1169
1170         return TRUE;
1171 }
1172
1173 static gint image_queue_to_tiles(ImageWindow *imd, gint x, gint y, gint w, gint h,
1174                                  gint clamp, TileRenderType render, gint new_data)
1175 {
1176         gint i, j;
1177         gint x1, x2;
1178         gint y1, y2;
1179         GList *work;
1180
1181         if (clamp && !image_clamp_to_visible(imd, &x, &y, &w, &h)) return FALSE;
1182
1183         x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
1184         x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
1185
1186         y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
1187         y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
1188
1189         work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
1190         for (j = y1; j <= y2; j += imd->tile_height)
1191                 {
1192                 GList *tmp;
1193                 tmp = work;
1194                 for (i = x1; i <= x2; i += imd->tile_width)
1195                         {
1196                         if (tmp)
1197                                 {
1198                                 ImageTile *it = tmp->data;
1199                                 QueueData *qd;
1200
1201                                 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1202                                     (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1203                                         {
1204                                         it->render_todo = render;
1205                                         }
1206
1207                                 qd = g_new(QueueData, 1);
1208                                 qd->it = it;
1209                                 qd->new_data = new_data;
1210
1211                                 if (i < x)
1212                                         {
1213                                         qd->x = x - i;
1214                                         }
1215                                 else
1216                                         {
1217                                         qd->x = 0;
1218                                         }
1219                                 qd->w = x + w - i - qd->x;
1220                                 if (qd->x + qd->w > imd->tile_width) qd->w = imd->tile_width - qd->x;
1221
1222
1223                                 if (j < y)
1224                                         {
1225                                         qd->y = y - j;
1226                                         }
1227                                 else
1228                                         {
1229                                         qd->y = 0;
1230                                         }
1231                                 qd->h = y + h - j - qd->y;
1232                                 if (qd->y + qd->h > imd->tile_height) qd->h = imd->tile_height - qd->y;
1233
1234                                 if (qd->w < 1 || qd->h < 1 || /* <--- sanity checks, rare cases cause this */
1235                                     image_queue_combine(imd, qd))
1236                                         {
1237                                         g_free(qd);
1238                                         }
1239                                 else
1240                                         {
1241                                         imd->draw_queue = g_list_append(imd->draw_queue, qd);
1242                                         }
1243
1244                                 tmp = tmp->next;
1245                                 }
1246                         }
1247                 work = g_list_nth(work, imd->tile_cols);        /* step 1 row */
1248                 }
1249
1250         return TRUE;
1251 }
1252
1253 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
1254                         gint clamp, TileRenderType render, gint new_data)
1255 {
1256         gint nx, ny;
1257
1258         nx = CLAMP(x, 0, imd->width - 1);
1259         ny = CLAMP(y, 0, imd->height - 1);
1260         w -= (nx - x);
1261         h -= (ny - y);
1262         w = CLAMP(w, 0, imd->width - nx);
1263         h = CLAMP(h, 0, imd->height - ny);
1264         if (w < 1 || h < 1) return;
1265
1266         if (image_queue_to_tiles(imd, nx, ny, w, h, clamp, render, new_data) &&
1267             ((!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1 || !imd->draw_idle_high))
1268                 {
1269                 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
1270                 imd->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW,
1271                                                     image_queue_draw_idle_cb, imd, NULL);
1272                 imd->draw_idle_high = TRUE;
1273                 }
1274 }
1275
1276 static void image_queue_list_free(GList *list)
1277 {
1278         GList *work;
1279
1280         work = list;
1281         while (work)
1282                 {
1283                 QueueData *qd;
1284
1285                 qd = work->data;
1286                 work = work->next;
1287                 g_free(qd);
1288                 }
1289
1290         g_list_free(list);
1291 }
1292
1293 static void image_queue_clear(ImageWindow *imd)
1294 {
1295         image_queue_list_free(imd->draw_queue);
1296         imd->draw_queue = NULL;
1297
1298         image_queue_list_free(imd->draw_queue_2pass);
1299         imd->draw_queue_2pass = NULL;
1300
1301         if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
1302         imd->draw_idle_id = -1;
1303 }
1304
1305 /*
1306  *-------------------------------------------------------------------
1307  * core calculations
1308  *-------------------------------------------------------------------
1309  */
1310
1311 static gint image_top_window_sizable(ImageWindow *imd)
1312 {
1313         if (!imd->top_window) return FALSE;
1314         if (!fit_window) return FALSE;
1315         if (!imd->top_window_sync) return FALSE;
1316         if (!imd->image->window) return FALSE;
1317         if (window_maximized(imd->top_window)) return FALSE;
1318
1319         return TRUE;
1320 }
1321
1322 static gint image_size_top_window(ImageWindow *imd, gint w, gint h)
1323 {
1324         gint ww, wh;
1325
1326         if (!image_top_window_sizable(imd)) return FALSE;
1327
1328         if (limit_window_size)
1329                 {
1330                 gint sw = gdk_screen_width() * max_window_size / 100;
1331                 gint sh = gdk_screen_height() * max_window_size / 100;
1332
1333                 if (w > sw) w = sw;
1334                 if (h > sh) h = sh;
1335                 }
1336
1337         w += (imd->top_window->allocation.width - imd->image->allocation.width);
1338         h += (imd->top_window->allocation.height - imd->image->allocation.height);
1339
1340         gdk_drawable_get_size(imd->top_window->window, &ww, &wh);
1341         if (w == ww && h == wh) return FALSE;
1342
1343         gdk_window_resize(imd->top_window->window, w, h);
1344
1345         return TRUE;
1346 }
1347
1348 static void image_redraw(ImageWindow *imd, gint new_data)
1349 {
1350         image_queue_clear(imd);
1351         image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, new_data);
1352 }
1353
1354 static void image_border_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
1355 {
1356         gint rx, ry, rw, rh;
1357
1358         if (!imd->image->window) return;
1359
1360         if (!imd->pixbuf && !imd->source_tiles_enabled)
1361                 {
1362                 if (util_clip_region(x, y, w, h,
1363                         0, 0,
1364                         imd->window_width, imd->window_height,
1365                         &rx, &ry, &rw, &rh))
1366                         {
1367                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1368                         image_overlay_draw(imd, rx, ry, rw, rh);
1369                         }
1370                 return;
1371                 }
1372
1373         if (imd->vis_width < imd->window_width)
1374                 {
1375                 if (imd->x_offset > 0 &&
1376                     util_clip_region(x, y, w, h,
1377                         0, 0,
1378                         imd->x_offset, imd->window_height,
1379                         &rx, &ry, &rw, &rh))
1380                         {
1381                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1382                         image_overlay_draw(imd, rx, ry, rw, rh);
1383                         }
1384                 if (imd->window_width - imd->vis_width - imd->x_offset > 0 &&
1385                     util_clip_region(x, y, w, h,
1386                         imd->x_offset + imd->vis_width, 0,
1387                         imd->window_width - imd->vis_width - imd->x_offset, imd->window_height,
1388                         &rx, &ry, &rw, &rh))
1389                         {
1390                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1391                         image_overlay_draw(imd, rx, ry, rw, rh);
1392                         }
1393                 }
1394         if (imd->vis_height < imd->window_height)
1395                 {
1396                 if (imd->y_offset > 0 &&
1397                     util_clip_region(x, y, w, h,
1398                         imd->x_offset, 0,
1399                         imd->vis_width, imd->y_offset,
1400                         &rx, &ry, &rw, &rh))
1401                         {
1402                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1403                         image_overlay_draw(imd, rx, ry, rw, rh);
1404                         }
1405                 if (imd->window_height - imd->vis_height - imd->y_offset > 0 &&
1406                     util_clip_region(x, y, w, h,
1407                         imd->x_offset, imd->y_offset + imd->vis_height,
1408                         imd->vis_width, imd->window_height - imd->vis_height - imd->y_offset,
1409                         &rx, &ry, &rw, &rh))
1410                         {
1411                         gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1412                         image_overlay_draw(imd, rx, ry, rw, rh);
1413                         }
1414                 }
1415 }
1416
1417 static void image_border_clear(ImageWindow *imd)
1418 {
1419         image_border_draw(imd, 0, 0, imd->window_width, imd->window_height);
1420 }
1421
1422 static void image_scroll_notify(ImageWindow *imd)
1423 {
1424         if (imd->func_scroll_notify && imd->scale)
1425                 {
1426                 imd->func_scroll_notify(imd,
1427                                         (gint)((gdouble)imd->x_scroll / imd->scale),
1428                                         (gint)((gdouble)imd->y_scroll / imd->scale),
1429                                         (gint)((gdouble)imd->image_width - imd->vis_width / imd->scale),
1430                                         (gint)((gdouble)imd->image_height - imd->vis_height / imd->scale),
1431                                         imd->data_scroll_notify);
1432                 }
1433 }
1434
1435 static gint image_scroll_clamp(ImageWindow *imd)
1436 {
1437         gint old_xs;
1438         gint old_ys;
1439
1440         if (imd->zoom == 0.0)
1441                 {
1442                 imd->x_scroll = 0;
1443                 imd->y_scroll = 0;
1444
1445                 image_scroll_notify(imd);
1446                 return FALSE;
1447                 }
1448
1449         old_xs = imd->x_scroll;
1450         old_ys = imd->y_scroll;
1451
1452         if (imd->x_offset > 0)
1453                 {
1454                 imd->x_scroll = 0;
1455                 }
1456         else
1457                 {
1458                 imd->x_scroll = CLAMP(imd->x_scroll, 0, imd->width - imd->vis_width);
1459                 }
1460
1461         if (imd->y_offset > 0)
1462                 {
1463                 imd->y_scroll = 0;
1464                 }
1465         else
1466                 {
1467                 imd->y_scroll = CLAMP(imd->y_scroll, 0, imd->height - imd->vis_height);
1468                 }
1469
1470         image_scroll_notify(imd);
1471
1472         return (old_xs != imd->x_scroll || old_ys != imd->y_scroll);
1473 }
1474
1475 static gint image_zoom_clamp(ImageWindow *imd, gdouble zoom, gint force, gint new)
1476 {
1477         gint w, h;
1478         gdouble scale;
1479
1480         zoom = CLAMP(zoom, imd->zoom_min, imd->zoom_max);
1481
1482         if (imd->zoom == zoom && !force) return FALSE;
1483
1484         w = imd->image_width;
1485         h = imd->image_height;
1486
1487         if (zoom == 0.0 && !imd->pixbuf)
1488                 {
1489                 scale = 1.0;
1490                 }
1491         else if (zoom == 0.0)
1492                 {
1493                 gint max_w;
1494                 gint max_h;
1495                 gint sizeable;
1496
1497                 sizeable = (new && image_top_window_sizable(imd));
1498
1499                 if (sizeable)
1500                         {
1501                         max_w = gdk_screen_width();
1502                         max_h = gdk_screen_height();
1503
1504                         if (limit_window_size)
1505                                 {
1506                                 max_w = max_w * max_window_size / 100;
1507                                 max_h = max_h * max_window_size / 100;
1508                                 }
1509                         }
1510                 else
1511                         {
1512                         max_w = imd->window_width;
1513                         max_h = imd->window_height;
1514                         }
1515
1516                 if ((zoom_to_fit_expands && !sizeable) || w > max_w || h > max_h)
1517                         {
1518                         if ((gdouble)max_w / w > (gdouble)max_h / h)
1519                                 {
1520                                 scale = (gdouble)max_h / h;
1521                                 h = max_h;
1522                                 w = w * scale + 0.5;
1523                                 if (w > max_w) w = max_w;
1524                                 }
1525                         else
1526                                 {
1527                                 scale = (gdouble)max_w / w;
1528                                 w = max_w;
1529                                 h = h * scale + 0.5;
1530                                 if (h > max_h) h = max_h;
1531                                 }
1532                         if (w < 1) w = 1;
1533                         if (h < 1) h = 1;
1534                         }
1535                 else
1536                         {
1537                         scale = 1.0;
1538                         }
1539                 }
1540         else if (zoom > 0.0) /* zoom orig, in */
1541                 {
1542                 scale = zoom;
1543                 w = w * scale;
1544                 h = h * scale;
1545                 }
1546         else /* zoom out */
1547                 {
1548                 scale = 1.0 / (0.0 - zoom);
1549                 w = w * scale;
1550                 h = h * scale;
1551                 }
1552
1553         imd->zoom = zoom;
1554         imd->width = w;
1555         imd->height = h;
1556         imd->scale = scale;
1557
1558         return TRUE;
1559 }
1560
1561 static gint image_size_clamp(ImageWindow *imd)
1562 {
1563         gint old_vw, old_vh;
1564
1565         old_vw = imd->vis_width;
1566         old_vh = imd->vis_height;
1567
1568         if (imd->width < imd->window_width)
1569                 {
1570                 imd->vis_width = imd->width;
1571                 imd->x_offset = (imd->window_width - imd->width) / 2;
1572                 }
1573         else
1574                 {
1575                 imd->vis_width = imd->window_width;
1576                 imd->x_offset = 0;
1577                 }
1578
1579         if (imd->height < imd->window_height)
1580                 {
1581                 imd->vis_height = imd->height;
1582                 imd->y_offset = (imd->window_height - imd->height) / 2;
1583                 }
1584         else
1585                 {
1586                 imd->vis_height = imd->window_height;
1587                 imd->y_offset = 0;
1588                 }
1589
1590         return (old_vw != imd->vis_width || old_vh != imd->vis_height);
1591 }
1592
1593 static void image_size_sync(ImageWindow *imd, gint new_width, gint new_height)
1594 {
1595         if (imd->window_width == new_width && imd->window_height == new_height) return;
1596
1597         imd->window_width = new_width;
1598         imd->window_height = new_height;
1599
1600         if (imd->zoom == 0.0) image_zoom_clamp(imd, 0.0, TRUE, FALSE);
1601
1602         image_size_clamp(imd);
1603         image_scroll_clamp(imd);
1604
1605 #if 0
1606         gtk_widget_set_size_request(imd->image, imd->window_width, imd->window_height);
1607 #endif
1608
1609         /* ensure scroller remains visible */
1610         if (imd->scroller_overlay != -1)
1611                 {
1612                 gint update = FALSE;
1613
1614                 if (imd->scroller_x > new_width)
1615                         {
1616                         imd->scroller_x = new_width;
1617                         imd->scroller_xpos = new_width;
1618                         update = TRUE;
1619                         }
1620                 if (imd->scroller_y > new_height)
1621                         {
1622                         imd->scroller_y = new_height;
1623                         imd->scroller_ypos = new_height;
1624                         update = TRUE;
1625                         }
1626
1627                 if (update)
1628                         {
1629                         GdkPixbuf *pixbuf;
1630
1631                         if (image_overlay_get(imd, imd->scroller_overlay, &pixbuf, NULL, NULL))
1632                                 {
1633                                 gint w, h;
1634
1635                                 w = gdk_pixbuf_get_width(pixbuf);
1636                                 h = gdk_pixbuf_get_height(pixbuf);
1637                                 image_overlay_set(imd, imd->scroller_overlay, pixbuf,
1638                                                   imd->scroller_x - w / 2, imd->scroller_y - h / 2);
1639                                 }
1640                         }
1641                 }
1642
1643         /* clear any borders */
1644         image_border_clear(imd);
1645         
1646         image_tile_sync(imd, imd->width, imd->height, FALSE);
1647 #if 0
1648         /* no longer needed? (expose event should be doing this for us) */
1649         image_redraw(imd, FALSE);
1650 #endif
1651
1652         if (imd->title_show_zoom) image_update_title(imd);
1653         image_update_util(imd);
1654 }
1655
1656 /*
1657  *-------------------------------------------------------------------
1658  * misc
1659  *-------------------------------------------------------------------
1660  */
1661
1662 static void image_update_title(ImageWindow *imd)
1663 {
1664         gchar *title = NULL;
1665         gchar *zoom = NULL;
1666         gchar *collection = NULL;
1667
1668         if (!imd->top_window) return;
1669
1670         if (imd->collection && collection_to_number(imd->collection) >= 0)
1671                 {
1672                 const gchar *name;
1673                 name = imd->collection->name;
1674                 if (!name) name = _("Untitled");
1675                 collection = g_strdup_printf(" (Collection %s)", name);
1676                 }
1677
1678         if (imd->title_show_zoom)
1679                 {
1680                 gchar *buf = image_zoom_get_as_text(imd);
1681                 zoom = g_strconcat(" [", buf, "]", NULL);
1682                 g_free(buf);
1683                 }
1684
1685         title = g_strdup_printf("%s%s%s%s%s%s",
1686                 imd->title ? imd->title : "",
1687                 imd->image_name ? imd->image_name : "",
1688                 zoom ? zoom : "",
1689                 collection ? collection : "",
1690                 imd->image_name ? " - " : "",
1691                 imd->title_right ? imd->title_right : "");
1692
1693         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
1694
1695         g_free(title);
1696         g_free(zoom);
1697         g_free(collection);
1698 }
1699
1700 static void image_update_util(ImageWindow *imd)
1701 {
1702         if (imd->func_update) imd->func_update(imd, imd->data_update);
1703 }
1704
1705 static void image_complete_util(ImageWindow *imd, gint preload)
1706 {
1707         if (imd->il && imd->pixbuf != image_loader_get_pixbuf(imd->il)) return;
1708
1709         if (debug) printf("image load completed \"%s\" (%s)\n",
1710                           (preload) ? imd->read_ahead_path : imd->image_path,
1711                           (preload) ? "preload" : "current");
1712
1713         if (!preload) imd->completed = TRUE;
1714         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
1715 }
1716
1717 static void image_new_util(ImageWindow *imd)
1718 {
1719         if (imd->func_new) imd->func_new(imd, imd->data_new);
1720 }
1721
1722 static void image_scroll_real(ImageWindow *imd, gint x, gint y)
1723 {
1724         gint old_x, old_y;
1725         gint x_off, y_off;
1726         gint w, h;
1727
1728         if (!imd->pixbuf && !imd->source_tiles_enabled) return;
1729
1730         old_x = imd->x_scroll;
1731         old_y = imd->y_scroll;
1732
1733         imd->x_scroll += x;
1734         imd->y_scroll += y;
1735
1736         image_scroll_clamp(imd);
1737         if (imd->x_scroll == old_x && imd->y_scroll == old_y) return;
1738
1739         if (imd->overlay_list)
1740                 {
1741                 gint new_x, new_y;
1742
1743                 new_x = imd->x_scroll;
1744                 new_y = imd->y_scroll;
1745                 imd->x_scroll = old_x;
1746                 imd->y_scroll = old_y;
1747                 image_overlay_queue_all(imd);
1748                 imd->x_scroll = new_x;
1749                 imd->y_scroll = new_y;
1750                 }
1751
1752         x_off = imd->x_scroll - old_x;
1753         y_off = imd->y_scroll - old_y;
1754
1755         w = imd->vis_width - abs(x_off);
1756         h = imd->vis_height - abs(y_off);
1757
1758         if (w < 1 || h < 1)
1759                 {
1760                 /* scrolled completely to new material */
1761                 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, FALSE);
1762                 return;
1763                 }
1764         else
1765                 {
1766                 gint x1, y1;
1767                 gint x2, y2;
1768                 GdkGC *gc;
1769
1770                 if (x_off < 0)
1771                         {
1772                         x1 = abs(x_off);
1773                         x2 = 0;
1774                         }
1775                 else
1776                         {
1777                         x1 = 0;
1778                         x2 = abs(x_off);
1779                         }
1780
1781                 if (y_off < 0)
1782                         {
1783                         y1 = abs(y_off);
1784                         y2 = 0;
1785                         }
1786                 else
1787                         {
1788                         y1 = 0;
1789                         y2 = abs(y_off);
1790                         }
1791
1792                 gc = gdk_gc_new(imd->image->window);
1793                 gdk_gc_set_exposures(gc, TRUE);
1794                 gdk_draw_drawable(imd->image->window, gc,
1795                                   imd->image->window,
1796                                   x2 + imd->x_offset, y2 + imd->y_offset,
1797                                   x1 + imd->x_offset, y1 + imd->y_offset, w, h);
1798                 g_object_unref(gc);
1799
1800                 if (imd->overlay_list)
1801                         {
1802                         image_overlay_queue_all(imd);
1803                         }
1804
1805                 w = imd->vis_width - w;
1806                 h = imd->vis_height - h;
1807
1808                 if (w > 0)
1809                         {
1810                         image_queue(imd,
1811                                     x_off > 0 ? imd->x_scroll + (imd->vis_width - w) : imd->x_scroll, imd->y_scroll,
1812                                     w, imd->vis_height, TRUE, TILE_RENDER_ALL, FALSE);
1813                         }
1814                 if (h > 0)
1815                         {
1816                         /* FIXME, to optimize this, remove overlap */
1817                         image_queue(imd,
1818                                     imd->x_scroll, y_off > 0 ? imd->y_scroll + (imd->vis_height - h) : imd->y_scroll,
1819                                     imd->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE);
1820                         }
1821                 }
1822 }
1823
1824 static void widget_set_cursor(GtkWidget *widget, gint icon)
1825 {
1826         GdkCursor *cursor;
1827
1828         if (!widget->window) return;
1829
1830         if (icon == -1)
1831                 {
1832                 cursor = NULL;
1833                 }
1834         else
1835                 {
1836                 cursor = gdk_cursor_new (icon);
1837                 }
1838
1839         gdk_window_set_cursor(widget->window, cursor);
1840
1841         if (cursor) gdk_cursor_unref(cursor);
1842 }
1843
1844 /*
1845  *-------------------------------------------------------------------
1846  * image pixbuf handling
1847  *-------------------------------------------------------------------
1848  */
1849
1850 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
1851                             gint force, gint blank, gint new,
1852                             gint center_point, gint px, gint py)
1853 {
1854         gdouble old_scale;
1855         gint old_cx, old_cy;
1856         gint clamped;
1857         gint sized;
1858
1859         old_scale = imd->scale;
1860         if (center_point)
1861                 {
1862                 px = CLAMP(px, 0, imd->width);
1863                 py = CLAMP(py, 0, imd->height);
1864                 old_cx = imd->x_scroll + (px - imd->x_offset);
1865                 old_cy = imd->y_scroll + (py - imd->y_offset);
1866                 }
1867         else
1868                 {
1869                 px = py = 0;
1870                 old_cx = imd->x_scroll + imd->vis_width / 2;
1871                 old_cy = imd->y_scroll + imd->vis_height / 2;
1872                 }
1873
1874         if (!image_zoom_clamp(imd, zoom, force, new)) return;
1875
1876         clamped = image_size_clamp(imd);
1877         sized = image_size_top_window(imd, imd->width, imd->height);
1878
1879         if (force)
1880                 {
1881                 /* force means new image, so update scroll offset per options */
1882                 switch (scroll_reset_method)
1883                         {
1884                         case SCROLL_RESET_NOCHANGE:
1885                                 /* maintain old scroll position, do nothing */
1886                                 break;
1887                         case SCROLL_RESET_CENTER:
1888                                 /* center new image */
1889                                 imd->x_scroll = ((double)imd->image_width / 2.0 * imd->scale) - imd->vis_width / 2;
1890                                 imd->y_scroll = ((double)imd->image_height / 2.0 * imd->scale) - imd->vis_height / 2;
1891                                 break;
1892                         case SCROLL_RESET_TOPLEFT:
1893                         default:
1894                                 /* reset to upper left */
1895                                 imd->x_scroll = 0;
1896                                 imd->y_scroll = 0;
1897                                 break;
1898                         }
1899                 }
1900         else
1901                 {
1902                 /* user zoom does not force, so keep visible center point */
1903                 if (center_point)
1904                         {
1905                         imd->x_scroll = old_cx / old_scale * imd->scale - (px - imd->x_offset);
1906                         imd->y_scroll = old_cy / old_scale * imd->scale - (py - imd->y_offset);
1907                         }
1908                 else
1909                         {
1910                         imd->x_scroll = old_cx / old_scale * imd->scale - (imd->vis_width / 2);
1911                         imd->y_scroll = old_cy / old_scale * imd->scale - (imd->vis_height / 2);
1912                         }
1913                 }
1914         image_scroll_clamp(imd);
1915
1916         image_tile_sync(imd, imd->width, imd->height, blank);
1917
1918         /* If the window was not sized, redraw the image - we know there will be no size/expose signal.
1919          * But even if a size is claimed, there is no guarantee that the window manager will allow it,
1920          * so redraw the window anyway :/
1921          */
1922         if (sized || clamped) image_border_clear(imd);
1923         image_redraw(imd, FALSE);
1924
1925         if (imd->title_show_zoom) image_update_title(imd);
1926         image_update_util(imd);
1927 }
1928
1929 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new)
1930 {
1931         if (!imd->pixbuf)
1932                 {
1933                 /* no pixbuf so just clear the window */
1934                 imd->image_width = 0;
1935                 imd->image_height = 0;
1936                 imd->scale = 1.0;
1937
1938                 if (imd->image->window)
1939                         {
1940                         gdk_window_clear(imd->image->window);
1941                         image_overlay_draw(imd, 0, 0, imd->window_width, imd->window_height);
1942                         }
1943
1944                 image_update_util(imd);
1945                 
1946                 return;
1947                 }
1948
1949         imd->image_width = gdk_pixbuf_get_width(imd->pixbuf);
1950         imd->image_height = gdk_pixbuf_get_height(imd->pixbuf);
1951
1952 #if 0
1953         /* reset scrolling */
1954         imd->x_scroll = 0;
1955         imd->y_scroll = 0;
1956 #endif
1957
1958         image_zoom_sync(imd, zoom, TRUE, blank, new, FALSE, 0, 0);
1959 }
1960
1961 static void image_set_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gint new)
1962 {
1963         if (pixbuf) g_object_ref(pixbuf);
1964         if (imd->pixbuf) g_object_unref(imd->pixbuf);
1965         imd->pixbuf = pixbuf;
1966
1967         image_pixbuf_sync(imd, zoom, FALSE, new);
1968 }
1969
1970 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
1971 {
1972         GdkPixbuf *new = NULL;
1973         gint x, y;
1974         gint t;
1975
1976         imd->delay_alter_type = ALTER_NONE;
1977
1978         if (!imd->pixbuf) return;
1979
1980         x = imd->x_scroll + (imd->vis_width / 2);
1981         y = imd->y_scroll + (imd->vis_height / 2);
1982
1983         switch (type)
1984                 {
1985                 case ALTER_ROTATE_90:
1986                         new = pixbuf_copy_rotate_90(imd->pixbuf, FALSE);
1987                         t = x;
1988                         x = imd->height - y;
1989                         y = t;
1990                         break;
1991                 case ALTER_ROTATE_90_CC:
1992                         new = pixbuf_copy_rotate_90(imd->pixbuf, TRUE);
1993                         t = x;
1994                         x = y;
1995                         y = imd->width - t;
1996                         break;
1997                 case ALTER_ROTATE_180:
1998                         new = pixbuf_copy_mirror(imd->pixbuf, TRUE, TRUE);
1999                         x = imd->width - x;
2000                         y = imd->height - y;
2001                         break;
2002                 case ALTER_MIRROR:
2003                         new = pixbuf_copy_mirror(imd->pixbuf, TRUE, FALSE);
2004                         x = imd->width - x;
2005                         break;
2006                 case ALTER_FLIP:
2007                         new = pixbuf_copy_mirror(imd->pixbuf, FALSE, TRUE);
2008                         y = imd->height - y;
2009                         break;
2010                 case ALTER_NONE:
2011                 default:
2012                         return;
2013                         break;
2014                 }
2015
2016         if (!new) return;
2017
2018         if (clamp)
2019                 {
2020                 image_set_pixbuf(imd, new, imd->zoom, TRUE);
2021                 g_object_unref(new);
2022
2023                 if (imd->zoom != 0.0)
2024                         {
2025                         image_scroll(imd, x - (imd->vis_width / 2), y - (imd->vis_height / 2));
2026                         }
2027                 }
2028         else
2029                 {
2030                 g_object_unref(imd->pixbuf);
2031                 imd->pixbuf = new;
2032                 }
2033 }
2034
2035 static void image_post_process(ImageWindow *imd, gint clamp)
2036 {
2037         if (exif_rotate_enable && imd->pixbuf)
2038                 {
2039                 ExifData *ed;
2040                 gint orientation;
2041
2042                 ed = exif_read(imd->image_path);
2043                 if (ed && exif_get_integer(ed, "Orientation", &orientation))
2044                         {
2045                         /* see http://jpegclub.org/exif_orientation.html 
2046                           1        2       3      4         5            6           7          8
2047
2048                         888888  888888      88  88      8888888888  88                  88  8888888888
2049                         88          88      88  88      88  88      88  88          88  88      88  88
2050                         8888      8888    8888  8888    88          8888888888  8888888888          88
2051                         88          88      88  88
2052                         88          88  888888  888888
2053                         */
2054
2055                         switch (orientation)
2056                                 {
2057                                 case EXIF_ORIENTATION_TOP_LEFT:
2058                                         /* normal -- nothing to do */
2059                                         break;
2060                                 case EXIF_ORIENTATION_TOP_RIGHT:
2061                                         /* mirrored */
2062                                         imd->delay_alter_type = ALTER_MIRROR;
2063                                         break;
2064                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
2065                                         /* upside down */
2066                                         imd->delay_alter_type = ALTER_ROTATE_180;
2067                                         break;
2068                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
2069                                         /* flipped */
2070                                         imd->delay_alter_type = ALTER_FLIP;
2071                                         break;
2072                                 case EXIF_ORIENTATION_LEFT_TOP:
2073                                         /* not implemented -- too wacky to fix in one step */
2074                                         break;
2075                                 case EXIF_ORIENTATION_RIGHT_TOP:
2076                                         /* rotated -90 (270) */
2077                                         imd->delay_alter_type = ALTER_ROTATE_90;
2078                                         break;
2079                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
2080                                         /* not implemented -- too wacky to fix in one step */
2081                                         break;
2082                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
2083                                         /* rotated 90 */
2084                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
2085                                         break;
2086                                 default:
2087                                         /* The other values are out of range */
2088                                         break;
2089                                 }
2090                         }
2091                 exif_free(ed);
2092                 }
2093
2094         if (imd->delay_alter_type != ALTER_NONE)
2095                 {
2096                 image_alter_real(imd, imd->delay_alter_type, clamp);
2097                 }
2098 }
2099
2100 /*
2101  *-------------------------------------------------------------------
2102  * read ahead (prebuffer)
2103  *-------------------------------------------------------------------
2104  */
2105
2106 static void image_read_ahead_cancel(ImageWindow *imd)
2107 {
2108         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
2109
2110         image_loader_free(imd->read_ahead_il);
2111         imd->read_ahead_il = NULL;
2112
2113         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
2114         imd->read_ahead_pixbuf = NULL;
2115
2116         g_free(imd->read_ahead_path);
2117         imd->read_ahead_path = NULL;
2118 }
2119
2120 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
2121 {
2122         ImageWindow *imd = data;
2123
2124         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
2125
2126         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
2127         if (imd->read_ahead_pixbuf)
2128                 {
2129                 g_object_ref(imd->read_ahead_pixbuf);
2130                 }
2131         else
2132                 {
2133                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2134                 }
2135         image_loader_free(imd->read_ahead_il);
2136         imd->read_ahead_il = NULL;
2137
2138         image_complete_util(imd, TRUE);
2139 }
2140
2141 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
2142 {
2143         /* we even treat errors as success, maybe at least some of the file was ok */
2144         image_read_ahead_done_cb(il, data);
2145 }
2146
2147 static void image_read_ahead_start(ImageWindow *imd)
2148 {
2149         /* already started ? */
2150         if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
2151
2152         /* still loading ?, do later */
2153         if (imd->il) return;
2154
2155         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
2156
2157         imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
2158
2159         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
2160         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
2161                 {
2162                 image_read_ahead_cancel(imd);
2163                 image_complete_util(imd, TRUE);
2164                 }
2165 }
2166
2167 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
2168 {
2169         if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
2170
2171         image_read_ahead_cancel(imd);
2172
2173         imd->read_ahead_path = g_strdup(path);
2174
2175         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
2176
2177         image_read_ahead_start(imd);
2178 }
2179
2180 /*
2181  *-------------------------------------------------------------------
2182  * post buffering
2183  *-------------------------------------------------------------------
2184  */
2185
2186 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
2187 {
2188         g_free(imd->prev_path);
2189         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2190
2191         if (path && pixbuf)
2192                 {
2193                 imd->prev_path = g_strdup(path);
2194                         
2195                 g_object_ref(pixbuf);
2196                 imd->prev_pixbuf = pixbuf;
2197                 }
2198         else
2199                 {
2200                 imd->prev_path = NULL;
2201                 imd->prev_pixbuf = NULL;
2202                 }
2203
2204         if (debug) printf("post buffer set: %s\n", path);
2205 }
2206
2207 static gint image_post_buffer_get(ImageWindow *imd)
2208 {
2209         gint success;
2210
2211         if (imd->prev_pixbuf &&
2212             imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
2213                 {
2214                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2215                 imd->pixbuf = imd->prev_pixbuf;
2216                 success = TRUE;
2217                 }
2218         else
2219                 {
2220                 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2221                 success = FALSE;
2222                 }
2223
2224         imd->prev_pixbuf = NULL;
2225
2226         g_free(imd->prev_path);
2227         imd->prev_path = NULL;
2228
2229         return success;
2230 }
2231
2232 /*
2233  *-------------------------------------------------------------------
2234  * loading
2235  *-------------------------------------------------------------------
2236  */
2237
2238 static void image_load_pixbuf_ready(ImageWindow *imd)
2239 {
2240         if (imd->pixbuf || !imd->il) return;
2241
2242         imd->pixbuf = image_loader_get_pixbuf(imd->il);
2243
2244         if (imd->pixbuf) g_object_ref(imd->pixbuf);
2245
2246         image_pixbuf_sync(imd, imd->zoom, TRUE, TRUE);
2247 }
2248
2249 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
2250 {
2251         ImageWindow *imd = data;
2252
2253         if (imd->delay_flip &&
2254             imd->pixbuf != image_loader_get_pixbuf(il))
2255                 {
2256                 return;
2257                 }
2258
2259         if (!imd->pixbuf) image_load_pixbuf_ready(imd);
2260
2261         if (imd->scale != 1.0)
2262                 {
2263                 x = (guint) floor((double)x * imd->scale);
2264                 y = (guint) floor((double)y * imd->scale);
2265                 w = (guint) ceil((double)w * imd->scale);
2266                 h = (guint) ceil((double)h * imd->scale);
2267
2268                 if (w == 0) w = 1;
2269                 if (h == 0) h = 1;
2270
2271                 if ((GdkInterpType)zoom_quality != GDK_INTERP_NEAREST)
2272                         {
2273                         /* some scaling types use surrounding pixels to smooth the image,
2274                          * this will expand the new area to cover up for faint black
2275                          * lines caused by previous renders with non-complete image
2276                          */
2277                         y -= 1;
2278                         h += 2;
2279                         }
2280
2281                 }
2282
2283         image_queue(imd, (gint) x, (gint) y, (gint) w, (gint) h, FALSE, TILE_RENDER_AREA, TRUE);
2284 }
2285
2286 static void image_load_done_cb(ImageLoader *il, gpointer data)
2287 {
2288         ImageWindow *imd = data;
2289
2290         if (debug) printf ("image done\n");
2291
2292         if (imd->delay_flip &&
2293             imd->pixbuf != image_loader_get_pixbuf(imd->il))
2294                 {
2295                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2296                 imd->pixbuf = image_loader_get_pixbuf(imd->il);
2297                 if (imd->pixbuf) g_object_ref(imd->pixbuf);
2298                 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2299                 }
2300
2301         image_loader_free(imd->il);
2302         imd->il = NULL;
2303
2304         image_post_process(imd, TRUE);
2305
2306         image_read_ahead_start(imd);
2307 }
2308
2309 static void image_load_error_cb(ImageLoader *il, gpointer data)
2310 {
2311         if (debug) printf ("image error\n");
2312
2313         /* even on error handle it like it was done,
2314          * since we have a pixbuf with _something_ */
2315
2316         image_load_done_cb(il, data);
2317 }
2318
2319 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2320 static void image_load_buffer_throttle(ImageLoader *il)
2321 {
2322         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
2323
2324         /* Larger image files usually have larger chunks of data per pixel...
2325          * So increase the buffer read size so that the rendering chunks called
2326          * are also larger.
2327          */
2328
2329         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
2330 }
2331 #endif
2332
2333 /* this read ahead is located here merely for the callbacks, above */
2334
2335 static gint image_read_ahead_check(ImageWindow *imd)
2336 {
2337         if (!imd->read_ahead_path) return FALSE;
2338         if (imd->il) return FALSE;
2339
2340         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
2341                 {
2342                 image_read_ahead_cancel(imd);
2343                 return FALSE;
2344                 }
2345
2346         if (imd->read_ahead_il)
2347                 {
2348                 imd->il = imd->read_ahead_il;
2349                 imd->read_ahead_il = NULL;
2350
2351                 /* override the old signals */
2352                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
2353                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
2354                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
2355
2356 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2357                 image_load_buffer_throttle(imd->il);
2358 #endif
2359
2360                 /* do this one directly (probably should add a set func) */
2361                 imd->il->func_done = image_load_done_cb;
2362
2363                 if (!imd->delay_flip)
2364                         {
2365                         if (imd->pixbuf) g_object_unref(imd->pixbuf);
2366                         imd->pixbuf = image_loader_get_pixbuf(imd->il);
2367                         if (imd->pixbuf) g_object_ref(imd->pixbuf);
2368                         }
2369
2370                 image_read_ahead_cancel(imd);
2371                 return TRUE;
2372                 }
2373         else if (imd->read_ahead_pixbuf)
2374                 {
2375                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2376                 imd->pixbuf = imd->read_ahead_pixbuf;
2377                 imd->read_ahead_pixbuf = NULL;
2378
2379                 image_read_ahead_cancel(imd);
2380
2381                 image_post_process(imd, FALSE);
2382                 return TRUE;
2383                 }
2384
2385         image_read_ahead_cancel(imd);
2386         return FALSE;
2387 }
2388
2389 static gint image_load_begin(ImageWindow *imd, const gchar *path)
2390 {
2391         if (debug) printf ("image begin \n");
2392
2393         if (imd->il) return FALSE;
2394
2395         imd->completed = FALSE;
2396
2397         if (image_post_buffer_get(imd))
2398                 {
2399                 if (debug) printf("from post buffer: %s\n", imd->image_path);
2400
2401                 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2402                 return TRUE;
2403                 }
2404
2405         if (image_read_ahead_check(imd))
2406                 {
2407                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
2408
2409                 if (!imd->delay_flip || !imd->il) image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2410                 return TRUE;
2411                 }
2412
2413         if (!imd->delay_flip && imd->pixbuf)
2414                 {
2415                 g_object_unref(imd->pixbuf);
2416                 imd->pixbuf = NULL;
2417                 }
2418
2419         imd->il = image_loader_new(path);
2420
2421         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
2422         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
2423         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
2424
2425         if (!image_loader_start(imd->il, image_load_done_cb, imd))
2426                 {
2427                 if (debug) printf("image start error\n");
2428
2429                 image_loader_free(imd->il);
2430                 imd->il = NULL;
2431
2432                 image_complete_util(imd, FALSE);
2433
2434                 return FALSE;
2435                 }
2436
2437 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2438         image_load_buffer_throttle(imd->il);
2439 #endif
2440
2441         if (!imd->delay_flip && !imd->pixbuf) image_load_pixbuf_ready(imd);
2442
2443         return TRUE;
2444 }
2445
2446 static void image_reset(ImageWindow *imd)
2447 {
2448         /* stops anything currently being done */
2449
2450         if (debug) printf("image reset\n");
2451
2452         image_loader_free(imd->il);
2453         imd->il = NULL;
2454
2455         image_queue_clear(imd);
2456         imd->delay_alter_type = ALTER_NONE;
2457 }
2458
2459 /*
2460  *-------------------------------------------------------------------
2461  * image changer
2462  *-------------------------------------------------------------------
2463  */
2464
2465 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
2466 {
2467         gint sync = TRUE;
2468
2469         image_source_tile_unset(imd);
2470
2471         imd->zoom = zoom;       /* store the zoom, needed by the loader */
2472
2473         image_reset(imd);
2474
2475         if (imd->image_path && isfile(imd->image_path))
2476                 {
2477                 if (image_load_begin(imd, imd->image_path))
2478                         {
2479                         imd->unknown = FALSE;
2480                         sync = FALSE;
2481                         }
2482                 else
2483                         {
2484                         if (imd->pixbuf) g_object_unref(imd->pixbuf);
2485                         imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2486                         imd->unknown = TRUE;
2487                         }
2488                 imd->size = filesize(imd->image_path);
2489                 imd->mtime = filetime(imd->image_path);
2490                 }
2491         else
2492                 {
2493                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2494                 imd->pixbuf = NULL;
2495
2496                 if (imd->image_path)
2497                         {
2498                         imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2499                         imd->mtime = filetime(imd->image_path);
2500                         }
2501                 else
2502                         {
2503                         imd->pixbuf = NULL;
2504                         imd->mtime = 0;
2505                         }
2506                 imd->unknown = TRUE;
2507                 imd->size = 0;
2508                 }
2509
2510         if (sync)
2511                 {
2512                 image_pixbuf_sync(imd, zoom, FALSE, new);
2513                 }
2514         else
2515                 {
2516                 image_update_util(imd);
2517                 }
2518 }
2519
2520 static void image_change_real(ImageWindow *imd, const gchar *path,
2521                               CollectionData *cd, CollectInfo *info, gdouble zoom)
2522 {
2523         GdkPixbuf *prev_pixbuf = NULL;
2524         gchar *prev_path = NULL;
2525         gint prev_clear = FALSE;
2526
2527         imd->collection = cd;
2528         imd->collection_info = info;
2529
2530         if (enable_read_ahead && imd->image_path && imd->pixbuf)
2531                 {
2532                 if (imd->il)
2533                         {
2534                         /* current image is not finished */
2535                         prev_clear = TRUE;
2536                         }
2537                 else
2538                         {
2539                         prev_path = g_strdup(imd->image_path);
2540                         prev_pixbuf = imd->pixbuf;
2541                         g_object_ref(prev_pixbuf);
2542                         }
2543                 }
2544
2545         g_free(imd->image_path);
2546         imd->image_path = g_strdup(path);
2547         imd->image_name = filename_from_path(imd->image_path);
2548
2549         image_change_complete(imd, zoom, TRUE);
2550
2551         if (prev_pixbuf)
2552                 {
2553                 image_post_buffer_set(imd, prev_path, prev_pixbuf);
2554                 g_free(prev_path);
2555                 g_object_unref(prev_pixbuf);
2556                 }
2557         else if (prev_clear)
2558                 {
2559                 image_post_buffer_set(imd, NULL, NULL);
2560                 }
2561
2562         image_update_title(imd);
2563         image_new_util(imd);
2564 }
2565
2566 /*
2567  *-------------------------------------------------------------------
2568  * callbacks
2569  *-------------------------------------------------------------------
2570  */
2571
2572 static gint image_expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2573 {
2574         gint x, y;
2575
2576         ImageWindow *imd = data;
2577
2578         image_border_draw(imd, event->area.x, event->area.y,
2579                           event->area.width, event->area.height);
2580
2581         /* image */
2582         x = MAX(0, (gint)event->area.x - imd->x_offset + imd->x_scroll);
2583         y = MAX(0, (gint)event->area.y - imd->y_offset + imd->y_scroll);
2584
2585         image_queue(imd, x, y,
2586                     MIN((gint)event->area.width, imd->width - x),
2587                     MIN((gint)event->area.height, imd->height - y),
2588                     FALSE, TILE_RENDER_ALL, FALSE);
2589
2590         return TRUE;
2591 }
2592
2593 static void image_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
2594 {
2595         ImageWindow *imd = data;
2596
2597         image_size_sync(imd, allocation->width, allocation->height);
2598 }
2599
2600 /*
2601  *-------------------------------------------------------------------
2602  * focus stuff
2603  *-------------------------------------------------------------------
2604  */
2605
2606 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
2607 {
2608         GtkWidget *widget;
2609
2610         widget = imd->widget;
2611         if (!widget->window) return;
2612
2613         if (has_focus)
2614                 {
2615                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
2616                                  area, widget, "image_window",
2617                                  widget->allocation.x, widget->allocation.y,
2618                                  widget->allocation.width - 1, widget->allocation.height - 1);  
2619                 }
2620         else
2621                 {
2622                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
2623                                   area, widget, "image_window",
2624                                   widget->allocation.x, widget->allocation.y,
2625                                   widget->allocation.width - 1, widget->allocation.height - 1);
2626                 }
2627 }
2628
2629 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2630 {
2631         ImageWindow *imd = data;
2632
2633         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
2634         return TRUE;
2635 }
2636
2637 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2638 {
2639         ImageWindow *imd = data;
2640
2641         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2642         image_focus_paint(imd, TRUE, NULL);
2643
2644         return TRUE;
2645 }
2646
2647 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2648 {
2649         ImageWindow *imd = data;
2650
2651         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2652         image_focus_paint(imd, FALSE, NULL);
2653
2654         return TRUE;
2655 }
2656
2657
2658 /*
2659  *-------------------------------------------------------------------
2660  * overlays
2661  *-------------------------------------------------------------------
2662  */
2663
2664 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
2665 {
2666         GList *work;
2667
2668         work = imd->overlay_list;
2669         while (work)
2670                 {
2671                 OverlayData *od;
2672                 gint px, py, pw, ph;
2673                 gint rx, ry, rw, rh;
2674
2675                 od = work->data;
2676                 work = work->next;
2677
2678                 if (!od->visible) continue;
2679
2680                 pw = gdk_pixbuf_get_width(od->pixbuf);
2681                 ph = gdk_pixbuf_get_height(od->pixbuf);
2682                 px = od->x;
2683                 py = od->y;
2684
2685                 if (od->relative)
2686                         {
2687                         if (px < 0) px = imd->window_width - pw + px;
2688                         if (py < 0) py = imd->window_height - ph + py;
2689                         }
2690
2691                 if (util_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
2692                         {
2693                         gdk_draw_pixbuf(imd->image->window,
2694                                         imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
2695                                         od->pixbuf,
2696                                         rx - px, ry - py,
2697                                         rx, ry, rw, rh,
2698                                         (GdkRgbDither)dither_quality, rx, ry);
2699                         }
2700                 }
2701 }
2702
2703 static void image_overlay_queue_draw(ImageWindow *imd, OverlayData *od, gint hidden)
2704 {
2705         gint x, y, w, h;
2706         gint old_vis;
2707
2708         w = gdk_pixbuf_get_width(od->pixbuf);
2709         h = gdk_pixbuf_get_height(od->pixbuf);
2710         x = od->x;
2711         y = od->y;
2712
2713         if (od->relative)
2714                 {
2715                 if (x < 0) x = imd->window_width - w + x;
2716                 if (y < 0) y = imd->window_height - h + y;
2717                 }
2718
2719         image_queue(imd, imd->x_scroll - imd->x_offset + x,
2720                          imd->y_scroll - imd->y_offset + y,
2721                          w, h,
2722                          FALSE, TILE_RENDER_ALL, FALSE);
2723
2724         old_vis = od->visible;
2725         if (hidden) od->visible = FALSE;
2726         image_border_draw(imd, x, y, w, h);
2727         od->visible = old_vis;
2728 }
2729
2730 static void image_overlay_queue_all(ImageWindow *imd)
2731 {
2732         GList *work;
2733
2734         work = imd->overlay_list;
2735         while (work)
2736                 {
2737                 OverlayData *od = work->data;
2738                 work = work->next;
2739
2740                 image_overlay_queue_draw(imd, od, FALSE);
2741                 }
2742 }
2743
2744 static OverlayData *image_overlay_find(ImageWindow *imd, gint id)
2745 {
2746         GList *work;
2747
2748         work = imd->overlay_list;
2749         while (work)
2750                 {
2751                 OverlayData *od = work->data;
2752                 work = work->next;
2753
2754                 if (od->id == id) return od;
2755                 }
2756
2757         return NULL;
2758 }
2759
2760 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
2761                        gint relative, gint always)
2762 {
2763         OverlayData *od;
2764         gint id;
2765
2766         if (!imd || !pixbuf) return -1;
2767
2768         id = 1;
2769         while (image_overlay_find(imd, id)) id++;
2770
2771         od = g_new0(OverlayData, 1);
2772         od->id = id;
2773         od->pixbuf = pixbuf;
2774         g_object_ref(G_OBJECT(od->pixbuf));
2775         od->x = x;
2776         od->y = y;
2777         od->relative = relative;
2778         od->visible = TRUE;
2779         od->always = always;
2780
2781         imd->overlay_list = g_list_append(imd->overlay_list, od);
2782
2783         image_overlay_queue_draw(imd, od, FALSE);
2784
2785         return od->id;
2786 }
2787
2788 static void image_overlay_free(ImageWindow *imd, OverlayData *od)
2789 {
2790         imd->overlay_list = g_list_remove(imd->overlay_list, od);
2791
2792         if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
2793         g_free(od);
2794 }
2795
2796 static void image_overlay_list_clear(ImageWindow *imd)
2797 {
2798         while (imd->overlay_list)
2799                 {
2800                 OverlayData *od;
2801
2802                 od = imd->overlay_list->data;
2803                 image_overlay_free(imd, od);
2804                 }
2805 }
2806
2807 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
2808 {
2809         OverlayData *od;
2810
2811         if (!imd) return;
2812
2813         od = image_overlay_find(imd, id);
2814         if (!od) return;
2815
2816         if (pixbuf)
2817                 {
2818                 image_overlay_queue_draw(imd, od, TRUE);
2819
2820                 g_object_ref(G_OBJECT(pixbuf));
2821                 g_object_unref(G_OBJECT(od->pixbuf));
2822                 od->pixbuf = pixbuf;
2823
2824                 od->x = x;
2825                 od->y = y;
2826
2827                 image_overlay_queue_draw(imd, od, FALSE);
2828                 }
2829         else
2830                 {
2831                 image_overlay_queue_draw(imd, od, TRUE);
2832                 image_overlay_free(imd, od);
2833                 }
2834 }
2835
2836 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
2837 {
2838         OverlayData *od;
2839
2840         if (!imd) return FALSE;
2841
2842         od = image_overlay_find(imd, id);
2843         if (!od) return FALSE;
2844
2845         if (pixbuf) *pixbuf = od->pixbuf;
2846         if (x) *x = od->x;
2847         if (y) *y = od->y;
2848
2849         return TRUE;
2850 }
2851
2852 void image_overlay_remove(ImageWindow *imd, gint id)
2853 {
2854         image_overlay_set(imd, id, NULL, 0, 0);
2855 }
2856
2857 /*
2858  *-------------------------------------------------------------------
2859  * scroller
2860  *-------------------------------------------------------------------
2861  */
2862
2863 #define SCROLLER_UPDATES_PER_SEC 30
2864 #define SCROLLER_DEAD_ZONE 6
2865
2866
2867 static gboolean image_scroller_update_cb(gpointer data)
2868 {
2869         ImageWindow *imd = data;
2870         gint x, y;
2871         gint xinc, yinc;
2872
2873         /* this was a simple scroll by difference between scroller and mouse position,
2874          * but all this math results in a smoother result and accounts for a dead zone.
2875          */
2876
2877         if (abs(imd->scroller_xpos - imd->scroller_x) < SCROLLER_DEAD_ZONE)
2878                 {
2879                 x = 0;
2880                 }
2881         else
2882                 {
2883                 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2884                 x = (imd->scroller_xpos - imd->scroller_x) / 2 * SCROLLER_UPDATES_PER_SEC;
2885                 x += (x > 0) ? -shift : shift;
2886                 }
2887
2888         if (abs(imd->scroller_ypos - imd->scroller_y) < SCROLLER_DEAD_ZONE)
2889                 {
2890                 y = 0;
2891                 }
2892         else
2893                 {
2894                 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2895                 y = (imd->scroller_ypos - imd->scroller_y) / 2 * SCROLLER_UPDATES_PER_SEC;
2896                 y += (y > 0) ? -shift : shift;
2897                 }
2898
2899         if (abs(x) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2900                 {
2901                 xinc = x;
2902                 }
2903         else
2904                 {
2905                 xinc = imd->scroller_xinc;
2906
2907                 if (x >= 0)
2908                         {
2909                         if (xinc < 0) xinc = 0;
2910                         if (x < xinc) xinc = x;
2911                         if (x > xinc) xinc = MIN(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2912                         }
2913                 else
2914                         {
2915                         if (xinc > 0) xinc = 0;
2916                         if (x > xinc) xinc = x;
2917                         if (x < xinc) xinc = MAX(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2918                         }
2919                 }
2920
2921         if (abs(y) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2922                 {
2923                 yinc = y;
2924                 }
2925         else
2926                 {
2927                 yinc = imd->scroller_yinc;
2928
2929                 if (y >= 0)
2930                         {
2931                         if (yinc < 0) yinc = 0;
2932                         if (y < yinc) yinc = y;
2933                         if (y > yinc) yinc = MIN(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2934                         }
2935                 else
2936                         {
2937                         if (yinc > 0) yinc = 0;
2938                         if (y > yinc) yinc = y;
2939                         if (y < yinc) yinc = MAX(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2940                         }
2941                 }
2942
2943         imd->scroller_xinc = xinc;
2944         imd->scroller_yinc = yinc;
2945
2946         xinc = xinc / SCROLLER_UPDATES_PER_SEC;
2947         yinc = yinc / SCROLLER_UPDATES_PER_SEC;
2948
2949         image_scroll(imd, xinc, yinc);
2950
2951         return TRUE;
2952 }
2953
2954 static void image_scroller_timer_set(ImageWindow *imd, gint start)
2955 {
2956         if (imd->scroller_id != -1)
2957                 {
2958                 g_source_remove(imd->scroller_id);
2959                 imd->scroller_id = -1;
2960                 }
2961
2962         if (start)
2963                 {
2964                 imd->scroller_id = g_timeout_add(1000 / SCROLLER_UPDATES_PER_SEC,
2965                                                    image_scroller_update_cb, imd);
2966                 }
2967 }
2968
2969 static void image_scroller_start(ImageWindow *imd, gint x, gint y)
2970 {
2971         if (imd->scroller_overlay == -1)
2972                 {
2973                 GdkPixbuf *pixbuf;
2974                 gint w, h;
2975
2976                 pixbuf = pixbuf_inline(PIXBUF_INLINE_SCROLLER);
2977                 w = gdk_pixbuf_get_width(pixbuf);
2978                 h = gdk_pixbuf_get_height(pixbuf);
2979
2980                 imd->scroller_overlay = image_overlay_add(imd, pixbuf, x - w / 2, y - h / 2, FALSE, TRUE);
2981                 }
2982
2983         imd->scroller_x = x;
2984         imd->scroller_y = y;
2985         imd->scroller_xpos = x;
2986         imd->scroller_ypos = y;
2987
2988         image_scroller_timer_set(imd, TRUE);
2989 }
2990
2991 static void image_scroller_stop(ImageWindow *imd)
2992 {
2993         if (imd->scroller_id == -1) return;
2994
2995         image_overlay_remove(imd, imd->scroller_overlay);
2996         imd->scroller_overlay = -1;
2997
2998         image_scroller_timer_set(imd, FALSE);
2999 }
3000
3001 /*
3002  *-------------------------------------------------------------------
3003  * mouse stuff
3004  *-------------------------------------------------------------------
3005  */
3006
3007 static gint image_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3008 {
3009         ImageWindow *imd = data;
3010         gint accel;
3011
3012         if (imd->scroller_id != -1)
3013                 {
3014                 imd->scroller_xpos = bevent->x;
3015                 imd->scroller_ypos = bevent->y;
3016                 }
3017
3018         if (!imd->in_drag || !gdk_pointer_is_grabbed()) return FALSE;
3019
3020         if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
3021                 {
3022                 imd->drag_moved++;
3023                 }
3024         else
3025                 {
3026                 widget_set_cursor (imd->image, GDK_FLEUR);
3027                 }
3028
3029         if (bevent->state & GDK_SHIFT_MASK)
3030                 {
3031                 accel = IMAGE_PAN_SHIFT_MULTIPLIER;
3032                 }
3033         else
3034                 {
3035                 accel = 1;
3036                 }
3037
3038         /* do the scroll */
3039         image_scroll_real(imd, (imd->drag_last_x - bevent->x) * accel,
3040                                (imd->drag_last_y - bevent->y) * accel);
3041
3042         imd->drag_last_x = bevent->x;
3043         imd->drag_last_y = bevent->y;
3044
3045         return FALSE;
3046 }
3047
3048 static gint image_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3049 {
3050         ImageWindow *imd = data;
3051
3052         if (imd->scroller_id != -1) return TRUE;
3053
3054         switch (bevent->button)
3055                 {
3056                 case 1:
3057                         imd->in_drag = TRUE;
3058                         imd->drag_last_x = bevent->x;
3059                         imd->drag_last_y = bevent->y;
3060                         imd->drag_moved = 0;
3061                         gdk_pointer_grab(imd->image->window, FALSE,
3062                                 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3063                                 NULL, NULL, bevent->time);
3064                         gtk_grab_add(imd->image);
3065                         break;
3066                 case 2:
3067                         imd->drag_moved = 0;
3068                         break;
3069                 case 3:
3070                         image_button_do(imd, bevent);
3071                         break;
3072                 default:
3073                         break;
3074                 }
3075
3076         gtk_widget_grab_focus(imd->widget);
3077
3078         return FALSE;
3079 }
3080
3081 static gint image_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3082 {
3083         ImageWindow *imd = data;
3084
3085         if (imd->scroller_id != -1)
3086                 {
3087                 image_scroller_stop(imd);
3088                 return TRUE;
3089                 }
3090
3091         if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(imd->image))
3092                 {
3093                 gtk_grab_remove(imd->image);
3094                 gdk_pointer_ungrab(bevent->time);
3095                 widget_set_cursor(imd->image, -1);
3096                 }
3097
3098         if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
3099                 {
3100                 if (bevent->button == 1 && (bevent->state & GDK_SHIFT_MASK))
3101                         {
3102                         image_scroller_start(imd, bevent->x, bevent->y);
3103                         }
3104                 else if (bevent->button == 1 || bevent->button == 2)
3105                         {
3106                         image_button_do(imd, bevent);
3107                         }
3108                 }
3109
3110         imd->in_drag = FALSE;
3111
3112         return FALSE;
3113 }
3114
3115 static gint image_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
3116 {
3117         ImageWindow *imd = data;
3118
3119         if (imd->scroller_id != -1)
3120                 {
3121                 imd->scroller_xpos = imd->scroller_x;
3122                 imd->scroller_ypos = imd->scroller_y;
3123                 imd->scroller_xinc = 0;
3124                 imd->scroller_yinc = 0;
3125                 }
3126
3127         return FALSE;
3128 }
3129
3130 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
3131 {
3132         ImageWindow *imd = data;
3133
3134         if (imd->func_scroll &&
3135             event && event->type == GDK_SCROLL)
3136                 {
3137                 imd->func_scroll(imd, event->direction, event->time,
3138                                  event->x, event->y, event->state, imd->data_scroll);
3139                 return TRUE;
3140                 }
3141
3142         return FALSE;
3143 }
3144
3145 static void image_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data)
3146 {
3147         ImageWindow *imd = data;
3148
3149         imd->drag_moved = IMAGE_DRAG_SCROLL_THRESHHOLD;
3150 }
3151
3152 /*
3153  *-------------------------------------------------------------------
3154  * drag and drop
3155  *-------------------------------------------------------------------
3156  */
3157
3158 /*
3159  *-------------------------------------------------------------------
3160  * public interface
3161  *-------------------------------------------------------------------
3162  */
3163
3164 void image_attach_window(ImageWindow *imd, GtkWidget *window,
3165                          const gchar *title, const gchar *title_right, gint show_zoom)
3166 {
3167         imd->top_window = window;
3168         g_free(imd->title);
3169         imd->title = g_strdup(title);
3170         g_free(imd->title_right);
3171         imd->title_right = g_strdup(title_right);
3172         imd->title_show_zoom = show_zoom;
3173
3174         image_update_title(imd);
3175 }
3176
3177 void image_set_update_func(ImageWindow *imd,
3178                            void (*func)(ImageWindow *imd, gpointer data),
3179                            gpointer data)
3180 {
3181         imd->func_update = func;
3182         imd->data_update = data;
3183 }
3184
3185 void image_set_complete_func(ImageWindow *imd,
3186                              void (*func)(ImageWindow *, gint preload, gpointer),
3187                              gpointer data)
3188 {
3189         imd->func_complete = func;
3190         imd->data_complete = data;
3191 }
3192
3193 void image_set_new_func(ImageWindow *imd,
3194                         void (*func)(ImageWindow *, gpointer),
3195                         gpointer data)
3196 {
3197         imd->func_new = func;
3198         imd->data_new = data;
3199 }
3200
3201
3202 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent)
3203 {
3204         if (imd->func_button &&
3205             bevent &&
3206             (bevent->type == GDK_BUTTON_PRESS || bevent->type == GDK_BUTTON_RELEASE))
3207                 {
3208                 imd->func_button(imd, bevent->button, bevent->time,
3209                                  bevent->x, bevent->y, bevent->state, imd->data_button);
3210                 }
3211 }
3212
3213 void image_set_button_func(ImageWindow *imd,
3214                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
3215                            gpointer data)
3216 {
3217         imd->func_button = func;
3218         imd->data_button = data;
3219 }
3220
3221 void image_set_scroll_func(ImageWindow *imd,
3222                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
3223                            gpointer data)
3224 {
3225         imd->func_scroll = func;
3226         imd->data_scroll = data;
3227 }
3228
3229 void image_set_scroll_notify_func(ImageWindow *imd,
3230                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
3231                                   gpointer data)
3232 {
3233         imd->func_scroll_notify = func;
3234         imd->data_scroll_notify = data;
3235 }
3236
3237 /* path, name */
3238
3239 const gchar *image_get_path(ImageWindow *imd)
3240 {
3241         return imd->image_path;
3242 }
3243
3244 const gchar *image_get_name(ImageWindow *imd)
3245 {
3246         return imd->image_name;
3247 }
3248
3249 /* merely changes path string, does not change the image! */
3250 void image_set_path(ImageWindow *imd, const gchar *newpath)
3251 {
3252         g_free(imd->image_path);
3253         imd->image_path = g_strdup(newpath);
3254         imd->image_name = filename_from_path(imd->image_path);
3255
3256         image_update_title(imd);
3257         image_new_util(imd);
3258 }
3259
3260 /* load a new image */
3261
3262 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
3263 {
3264         if (imd->image_path == path ||
3265             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
3266
3267         image_source_tile_unset(imd);
3268
3269         image_change_real(imd, path, NULL, NULL, zoom);
3270 }
3271
3272 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
3273 {
3274         image_source_tile_unset(imd);
3275
3276         image_set_pixbuf(imd, pixbuf, zoom, TRUE);
3277         image_new_util(imd);
3278 }
3279
3280 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
3281 {
3282         if (!cd || !info || !g_list_find(cd->list, info)) return;
3283
3284         image_source_tile_unset(imd);
3285
3286         image_change_real(imd, info->path, cd, info, zoom);
3287 }
3288
3289 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
3290 {
3291         if (collection_to_number(imd->collection) >= 0)
3292                 {
3293                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
3294                         {
3295                         if (info) *info = imd->collection_info;
3296                         }
3297                 else
3298                         {
3299                         if (info) *info = NULL;
3300                         }
3301                 return imd->collection;
3302                 }
3303
3304         if (info) *info = NULL;
3305         return NULL;
3306 }
3307
3308 static void image_loader_sync_data(ImageLoader *il, gpointer data)
3309 {
3310         /* change data for the callbacks directly */
3311
3312         il->data_area_ready = data;
3313         il->data_error = data;
3314         il->data_done = data;
3315         il->data_percent = data;
3316 }
3317
3318 /* this is more like a move function
3319  * it moves most data from source to imd, source does keep a ref on the pixbuf
3320  */
3321 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
3322 {
3323         if (imd == source) return;
3324
3325         imd->zoom_min = source->zoom_min;
3326         imd->zoom_max = source->zoom_max;
3327
3328         imd->unknown = source->unknown;
3329
3330         image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
3331
3332         imd->collection = source->collection;
3333         imd->collection_info = source->collection_info;
3334         imd->size = source->size;
3335         imd->mtime = source->mtime;
3336
3337         image_set_path(imd, image_get_path(source));
3338
3339         image_loader_free(imd->il);
3340         imd->il = NULL;
3341
3342         if (imd->pixbuf && source->il)
3343                 {
3344                 imd->il = source->il;
3345                 source->il = NULL;
3346
3347                 image_loader_sync_data(imd->il, imd);
3348
3349                 imd->delay_alter_type = source->delay_alter_type;
3350                 source->delay_alter_type = ALTER_NONE;
3351                 }
3352
3353         image_loader_free(imd->read_ahead_il);
3354         imd->read_ahead_il = source->read_ahead_il;
3355         source->read_ahead_il = NULL;
3356         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
3357
3358         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
3359         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
3360         source->read_ahead_pixbuf = NULL;
3361
3362         g_free(imd->read_ahead_path);
3363         imd->read_ahead_path = source->read_ahead_path;
3364         source->read_ahead_path = NULL;
3365
3366         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
3367         imd->prev_pixbuf = source->prev_pixbuf;
3368         source->prev_pixbuf = NULL;
3369
3370         g_free(imd->prev_path);
3371         imd->prev_path = source->prev_path;
3372         source->prev_path = NULL;
3373
3374         imd->completed = source->completed;
3375
3376         imd->x_scroll = source->x_scroll;
3377         imd->y_scroll = source->y_scroll;
3378
3379         if (imd->source_tiles_enabled)
3380                 {
3381                 image_source_tile_unset(imd);
3382                 }
3383
3384         if (source->source_tiles_enabled)
3385                 {
3386                 imd->source_tiles_enabled = source->source_tiles_enabled;
3387                 imd->source_tiles_cache_size = source->source_tiles_cache_size;
3388                 imd->source_tiles = source->source_tiles;
3389                 imd->source_tile_width = source->source_tile_width;
3390                 imd->source_tile_height = source->source_tile_height;
3391
3392                 source->source_tiles_enabled = FALSE;
3393                 source->source_tiles = NULL;
3394
3395                 imd->func_tile_request = source->func_tile_request;
3396                 imd->func_tile_dispose = source->func_tile_dispose;
3397                 imd->data_tile = source->data_tile;
3398
3399                 source->func_tile_request = NULL;
3400                 source->func_tile_dispose = NULL;
3401                 source->data_tile = NULL;
3402
3403                 imd->image_width = source->image_width;
3404                 imd->image_height = source->image_height;
3405
3406                 if (image_zoom_clamp(imd, source->zoom, TRUE, TRUE))
3407                         {
3408                         image_size_clamp(imd);
3409                         image_scroll_clamp(imd);
3410                         image_tile_sync(imd, imd->width, imd->height, FALSE);
3411                         image_redraw(imd, FALSE);
3412                         }
3413                 return;
3414                 }
3415
3416         image_scroll_clamp(imd);
3417 }
3418
3419 /* manipulation */
3420
3421 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
3422 {
3423         gint sx, sy, sw, sh;
3424
3425         sx = (gint)floor((double)x * imd->scale);
3426         sy = (gint)floor((double)y * imd->scale);
3427         sw = (gint)ceil((double)width * imd->scale);
3428         sh = (gint)ceil((double)height * imd->scale);
3429
3430         if (imd->source_tiles_enabled)
3431                 {
3432                 source_tile_changed(imd, x, y, width, height);
3433                 }
3434
3435         image_queue(imd, sx, sy, sw, sh, FALSE, TILE_RENDER_AREA, TRUE);
3436 }
3437
3438 void image_reload(ImageWindow *imd)
3439 {
3440         if (imd->source_tiles_enabled) return;
3441
3442         image_change_complete(imd, imd->zoom, FALSE);
3443 }
3444
3445 void image_scroll(ImageWindow *imd, gint x, gint y)
3446 {
3447         image_scroll_real(imd, x, y);
3448 }
3449
3450 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
3451                            gdouble x_align, gdouble y_align)
3452 {
3453         gint px, py;
3454         gint ax, ay;
3455
3456         x_align = CLAMP(x_align, 0.0, 1.0);
3457         y_align = CLAMP(y_align, 0.0, 1.0);
3458
3459         ax = (gdouble)imd->vis_width * x_align;
3460         ay = (gdouble)imd->vis_height * y_align;
3461
3462         px = (gdouble)x * imd->scale - (imd->x_scroll + ax);
3463         py = (gdouble)y * imd->scale - (imd->y_scroll + ay);
3464
3465         image_scroll(imd, px, py);
3466 }
3467
3468 void image_alter(ImageWindow *imd, AlterType type)
3469 {
3470         if (imd->source_tiles_enabled) return;
3471
3472         if (imd->il)
3473                 {
3474                 /* still loading, wait till done */
3475                 imd->delay_alter_type = type;
3476                 return;
3477                 }
3478
3479         image_alter_real(imd, type, TRUE);
3480 }
3481
3482 /* zoom */
3483
3484 static void image_zoom_adjust_real(ImageWindow *imd, gdouble increment,
3485                                    gint center_point, gint x, gint y)
3486 {
3487         gdouble zoom = imd->zoom;
3488
3489         if (increment == 0.0) return; /* avoid possible div by zero, a no-op anyway... */
3490
3491         if (zoom == 0.0)
3492                 {
3493                 if (imd->scale < 1.0)
3494                         {
3495                         zoom = 0.0 - 1.0 / imd->scale;
3496                         }
3497                 else
3498                         {
3499                         zoom = imd->scale;
3500                         }
3501                 }
3502
3503         if (increment < 0.0)
3504                 {
3505                 if (zoom >= 1.0 && zoom + increment < 1.0)
3506                         {
3507                         zoom = zoom + increment - 2.0;
3508                         }
3509                 else
3510                         {
3511                         zoom = zoom + increment;
3512                         }
3513                 }
3514         else
3515                 {
3516                 if (zoom <= -1.0 && zoom + increment > -1.0)
3517                         {
3518                         zoom = zoom + increment + 2.0;
3519                         }
3520                 else
3521                         {
3522                         zoom = zoom + increment;
3523                         }
3524                 }
3525
3526         image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, center_point, x, y);
3527 }
3528
3529 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
3530 {
3531         image_zoom_adjust_real(imd, increment, FALSE, 0, 0);
3532 }
3533
3534 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
3535 {
3536         image_zoom_adjust_real(imd, increment, TRUE, x, y);
3537 }
3538
3539 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
3540 {
3541         if (min > 1.0 || max < 1.0) return;
3542         if (min < 1.0 && min > -1.0) return;
3543         if (min < -200.0 || max > 200.0) return;
3544
3545         imd->zoom_min = min;
3546         imd->zoom_max = max;
3547 }
3548
3549 void image_zoom_set(ImageWindow *imd, gdouble zoom)
3550 {
3551         image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, FALSE, 0, 0);
3552 }
3553
3554 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
3555 {
3556         gdouble zoom;
3557
3558         if (!imd->pixbuf || imd->image_width < 1 || imd->image_height < 1) return;
3559
3560         if (vertical)
3561                 {
3562                 zoom = (gdouble)imd->window_height / imd->image_height;
3563                 }
3564         else
3565                 {
3566                 zoom = (gdouble)imd->window_width / imd->image_width;
3567                 }
3568
3569         if (zoom < 1.0)
3570                 {
3571                 zoom = 0.0 - 1.0 / zoom;
3572                 }
3573
3574         image_zoom_set(imd, zoom);
3575 }
3576
3577 gdouble image_zoom_get(ImageWindow *imd)
3578 {
3579         return imd->zoom;
3580 }
3581
3582 gdouble image_zoom_get_real(ImageWindow *imd)
3583 {
3584         return imd->scale;
3585 }
3586
3587 gchar *image_zoom_get_as_text(ImageWindow *imd)
3588 {
3589         gdouble l = 1.0;
3590         gdouble r = 1.0;
3591         gint pl = 0;
3592         gint pr = 0;
3593         gchar *approx = " ";
3594
3595         if (imd->zoom > 0.0)
3596                 {
3597                 l = imd->zoom;
3598                 }
3599         else if (imd->zoom < 0.0)
3600                 {
3601                 r = 0.0 - imd->zoom;
3602                 }
3603         else if (imd->zoom == 0.0 && imd->scale != 0.0)
3604                 {
3605                 if (imd->scale >= 1.0)
3606                         {
3607                         l = imd->scale;
3608                         }
3609                 else
3610                         {
3611                         r = 1.0 / imd->scale;
3612                         }
3613                 approx = " ~";
3614                 }
3615
3616         if (rint(l) != l) pl = 1;
3617         if (rint(r) != r) pr = 1;
3618
3619         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
3620 }
3621
3622 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
3623 {
3624         gdouble zoom;
3625
3626         if (mode == ZOOM_RESET_ORIGINAL)
3627                 {
3628                 zoom = 1.0;
3629                 }
3630         else if (mode == ZOOM_RESET_FIT_WINDOW)
3631                 {
3632                 zoom = 0.0;
3633                 }
3634         else
3635                 {
3636                 if (imd)
3637                         {
3638                         zoom = image_zoom_get(imd);
3639                         }
3640                 else
3641                         {
3642                         zoom = 1.0;
3643                         }
3644                 }
3645
3646         return zoom;
3647 }
3648
3649 /* read ahead */
3650
3651 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
3652 {
3653         if (imd->source_tiles_enabled) return;
3654
3655         if (path)
3656                 {
3657                 image_read_ahead_set(imd, path);
3658                 }
3659         else
3660                 {
3661                 image_read_ahead_cancel(imd);
3662                 }
3663 }
3664
3665 static gint image_auto_refresh_cb(gpointer data)
3666 {
3667         ImageWindow *imd = data;
3668         time_t newtime;
3669         
3670         if (!imd || !imd->pixbuf ||
3671             imd->il || !imd->image_path ||
3672             !update_on_time_change) return TRUE;
3673
3674         newtime = filetime(imd->image_path);
3675         if (newtime > 0 && newtime != imd->mtime)
3676                 {
3677                 imd->mtime = newtime;
3678                 image_reload(imd);
3679                 }
3680
3681         return TRUE;
3682 }
3683
3684 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
3685
3686 void image_auto_refresh(ImageWindow *imd, gint interval)
3687 {
3688         if (!imd) return;
3689         if (imd->source_tiles_enabled) return;
3690
3691         if (imd->auto_refresh_id > -1)
3692                 {
3693                 g_source_remove(imd->auto_refresh_id);
3694                 imd->auto_refresh_id = -1;
3695                 imd->auto_refresh_interval = -1;
3696                 }
3697
3698         if (interval < 0) return;
3699
3700         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
3701
3702         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
3703         imd->auto_refresh_interval = interval;
3704 }
3705
3706 /* allow top window to be resized ? */
3707
3708 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
3709 {
3710         imd->top_window_sync = allow_sync;
3711 }
3712
3713 /* background colors */
3714
3715 void image_background_set_black(ImageWindow *imd, gint black)
3716 {
3717         GtkStyle *style;
3718
3719         if (!imd) return;
3720
3721         style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3722         g_object_ref(G_OBJECT(style));
3723
3724         if (black)
3725                 {
3726                 style->bg[GTK_STATE_NORMAL] = style->black;
3727                 }
3728
3729         gtk_widget_set_style(imd->image, style);
3730         g_object_unref(G_OBJECT(style));
3731
3732         if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3733 }
3734
3735 void image_background_set_color(ImageWindow *imd, GdkColor *color)
3736 {
3737         GtkStyle *style;
3738
3739         if (!imd) return;
3740
3741         style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3742         g_object_ref(G_OBJECT(style));
3743
3744         if (color)
3745                 {
3746                 GdkColor *slot;
3747
3748                 slot = &style->bg[GTK_STATE_NORMAL];
3749
3750                 slot->red = color->red;
3751                 slot->green = color->green;
3752                 slot->blue = color->blue;
3753                 }
3754
3755         gtk_widget_set_style(imd->image, style);
3756         g_object_unref(G_OBJECT(style));
3757
3758         if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3759 }
3760
3761 void image_set_delay_flip(ImageWindow *imd, gint delay)
3762 {
3763         if (!imd ||
3764             imd->delay_flip == delay) return;
3765
3766         imd->delay_flip = delay;
3767         if (!imd->delay_flip && imd->il)
3768                 {
3769                 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3770                 imd->pixbuf = NULL;
3771                 image_load_pixbuf_ready(imd);
3772
3773                 image_queue_clear(imd);
3774                 image_queue(imd, 0, 0, imd->width, imd->height, FALSE, TILE_RENDER_AREA, TRUE);
3775                 }
3776 }
3777
3778 /* wallpaper util */
3779
3780 void image_to_root_window(ImageWindow *imd, gint scaled)
3781 {
3782         GdkScreen *screen;
3783         GdkWindow *rootwindow;
3784         GdkPixmap *pixmap;
3785         GdkPixbuf *pb;
3786
3787         if (!imd || !imd->pixbuf) return;
3788
3789         screen = gtk_widget_get_screen(imd->image);
3790         rootwindow = gdk_screen_get_root_window(screen);
3791         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
3792
3793         if (scaled)
3794                 {
3795                 pb = gdk_pixbuf_scale_simple(imd->pixbuf, gdk_screen_width(), gdk_screen_height(), (GdkInterpType)zoom_quality);
3796                 }
3797         else
3798                 {
3799                 pb = gdk_pixbuf_scale_simple(imd->pixbuf, imd->width, imd->height, (GdkInterpType)zoom_quality);
3800                 }
3801
3802         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
3803         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
3804         gdk_window_clear(rootwindow);
3805         g_object_unref(pb);
3806         g_object_unref(pixmap);
3807
3808         gdk_flush();
3809 }
3810
3811
3812 /*
3813  *-------------------------------------------------------------------
3814  * init / destroy
3815  *-------------------------------------------------------------------
3816  */
3817
3818 static void image_free(ImageWindow *imd)
3819 {
3820         image_read_ahead_cancel(imd);
3821         image_post_buffer_set(imd, NULL, NULL);
3822         image_auto_refresh(imd, -1);
3823
3824         g_free(imd->image_path);
3825         g_free(imd->title);
3826         g_free(imd->title_right);
3827
3828         image_reset(imd);
3829         image_tile_sync_count(imd, 0);
3830         if (imd->pixbuf) g_object_unref(imd->pixbuf);
3831
3832         image_scroller_timer_set(imd, FALSE);
3833
3834         image_overlay_list_clear(imd);
3835
3836         source_tile_free_all(imd);
3837
3838         g_free(imd);
3839 }
3840
3841 static void image_destroy_cb(GtkObject *widget, gpointer data)
3842 {
3843         ImageWindow *imd = data;
3844         image_free(imd);
3845 }
3846
3847 ImageWindow *image_new(gint frame)
3848 {
3849         ImageWindow *imd;
3850
3851         imd = g_new0(ImageWindow, 1);
3852
3853         imd->zoom_min = IMAGE_ZOOM_MIN;
3854         imd->zoom_max = IMAGE_ZOOM_MAX;
3855         imd->zoom = 1.0;
3856         imd->scale = 1.0;
3857
3858         imd->draw_idle_id = -1;
3859
3860         imd->tile_width = IMAGE_TILE_SIZE;
3861         imd->tile_height = IMAGE_TILE_SIZE;
3862
3863         imd->top_window = NULL;
3864         imd->title = NULL;
3865         imd->title_right = NULL;
3866         imd->title_show_zoom = FALSE;
3867
3868         imd->unknown = TRUE;
3869
3870         imd->pixbuf = NULL;
3871
3872         imd->has_frame = frame;
3873         imd->top_window_sync = FALSE;
3874
3875         imd->tile_cache = NULL;
3876         imd->tile_cache_size = 0;
3877
3878         imd->delay_alter_type = ALTER_NONE;
3879
3880         imd->read_ahead_il = NULL;
3881         imd->read_ahead_pixbuf = NULL;
3882         imd->read_ahead_path = NULL;
3883
3884         imd->completed = FALSE;
3885
3886         imd->auto_refresh_id = -1;
3887         imd->auto_refresh_interval = -1;
3888
3889         imd->delay_flip = FALSE;
3890
3891         imd->func_update = NULL;
3892         imd->func_complete = NULL;
3893         imd->func_tile_request = NULL;
3894         imd->func_tile_dispose = NULL;
3895
3896         imd->func_button = NULL;
3897         imd->func_scroll = NULL;
3898
3899         imd->scroller_id = -1;
3900         imd->scroller_overlay = -1;
3901
3902         imd->source_tiles_enabled = FALSE;
3903         imd->source_tiles = NULL;
3904
3905         imd->image = gtk_drawing_area_new();
3906         gtk_widget_set_double_buffered(imd->image, FALSE);
3907
3908         if (imd->has_frame)
3909                 {
3910                 imd->widget = gtk_frame_new(NULL);
3911                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
3912                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->image);
3913                 gtk_widget_show(imd->image);
3914
3915                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
3916                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
3917                                  G_CALLBACK(image_focus_in_cb), imd);
3918                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
3919                                  G_CALLBACK(image_focus_out_cb), imd);
3920
3921                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
3922                                        G_CALLBACK(image_focus_expose), imd);
3923                 }
3924         else
3925                 {
3926                 imd->widget = imd->image;
3927                 }
3928
3929         g_signal_connect(G_OBJECT(imd->image), "motion_notify_event",
3930                          G_CALLBACK(image_mouse_motion_cb), imd);
3931         g_signal_connect(G_OBJECT(imd->image), "button_press_event",
3932                          G_CALLBACK(image_mouse_press_cb), imd);
3933         g_signal_connect(G_OBJECT(imd->image), "button_release_event",
3934                          G_CALLBACK(image_mouse_release_cb), imd);
3935         g_signal_connect(G_OBJECT(imd->image), "leave_notify_event",
3936                          G_CALLBACK(image_mouse_leave_cb), imd);
3937         gtk_widget_set_events(imd->image, GDK_POINTER_MOTION_MASK |
3938                                           GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
3939                                           GDK_LEAVE_NOTIFY_MASK);
3940
3941         g_signal_connect(G_OBJECT(imd->image), "expose_event",
3942                          G_CALLBACK(image_expose_cb), imd);
3943         g_signal_connect_after(G_OBJECT(imd->image), "size_allocate",
3944                          G_CALLBACK(image_size_cb), imd);
3945
3946         g_signal_connect(G_OBJECT(imd->image), "drag_begin",
3947                          G_CALLBACK(image_mouse_drag_cb), imd);
3948         g_signal_connect(G_OBJECT(imd->image), "scroll_event",
3949                          G_CALLBACK(image_scroll_cb), imd);
3950
3951         g_signal_connect(G_OBJECT(imd->widget), "destroy",
3952                          G_CALLBACK(image_destroy_cb), imd);
3953
3954         return imd;
3955 }
3956