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!
17 #include "image-load.h"
20 #include "pixbuf_util.h"
21 #include "ui_fileops.h"
26 #define IMAGE_TILE_SIZE 512
27 #define IMAGE_ZOOM_MIN -32.0
28 #define IMAGE_ZOOM_MAX 32.0
30 /* size of the image loader buffer (512 bytes x defined number) */
31 #define IMAGE_LOAD_BUFFER_COUNT 8
33 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
34 #define IMAGE_THROTTLE_LARGER_IMAGES 1
36 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
37 #define IMAGE_THROTTLE_FACTOR 4
39 /* the file size at which throttling take place */
40 #define IMAGE_THROTTLE_THRESHOLD 1048576
42 /* distance to drag mouse to disable image flip */
43 #define IMAGE_DRAG_SCROLL_THRESHHOLD 4
45 /* alpha channel checkerboard background (same as gimp) */
46 #define IMAGE_ALPHA_CHECK1 0x00999999
47 #define IMAGE_ALPHA_CHECK2 0x00666666
48 #define IMAGE_ALPHA_CHECK_SIZE 16
50 #define IMAGE_AUTO_REFRESH_TIME 3000
52 /* when scaling image to below this size, use nearest pixel for scaling
53 * (below about 4, the other scale types become slow generating their conversion tables)
55 #define IMAGE_MIN_SCALE_SIZE 8
59 TILE_RENDER_NONE = 0, /* do nothing */
60 TILE_RENDER_AREA, /* render an area of the tile */
61 TILE_RENDER_ALL /* render the whole tile */
64 typedef struct _ImageTile ImageTile;
67 GdkPixmap *pixmap; /* off screen buffer */
68 GdkPixbuf *pixbuf; /* pixbuf area for zooming */
69 gint x; /* x offset into image */
70 gint y; /* y offset into image */
71 gint w; /* width that is visible (may be less if at edge of image) */
72 gint h; /* height '' */
76 /* render_todo: (explanation)
78 AREA render area of tile, usually only used when loading an image
79 note: will jump to an ALL if render_done is not ALL.
80 ALL render entire tile, if never done before w/ ALL, for expose events *only*
83 TileRenderType render_todo; /* what to do (see above) */
84 TileRenderType render_done; /* highest that has been done before on tile */
87 typedef struct _QueueData QueueData;
98 typedef struct _CacheData CacheData;
107 typedef struct _OverlayData OverlayData;
116 gint relative; /* x,y coordinates are relative, negative values start bottom right */
119 gint always; /* hide temporarily when scrolling */
122 /* needed to be declared before the source_tile stuff */
123 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new);
124 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
125 gint force, gint blank, gint new,
126 gint center_point, gint px, gint py);
127 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
128 gint clamp, TileRenderType render, gint new_data);
130 static gint util_clip_region(gint x, gint y, gint w, gint h,
131 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
132 gint *rx, gint *ry, gint *rw, gint *rh);
135 *-------------------------------------------------------------------
137 *-------------------------------------------------------------------
140 typedef struct _SourceTile SourceTile;
149 static void source_tile_free(SourceTile *st)
153 if (st->pixbuf) gdk_pixbuf_unref(st->pixbuf);
157 static void source_tile_free_all(ImageWindow *imd)
161 work = imd->source_tiles;
164 SourceTile *st = work->data;
167 source_tile_free(st);
170 g_list_free(imd->source_tiles);
171 imd->source_tiles = NULL;
174 static gint source_tile_visible(ImageWindow *imd, SourceTile *st)
178 if (!st) return FALSE;
180 x = (imd->x_scroll / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE;
181 y = (imd->y_scroll / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE;
182 w = ((imd->x_scroll + imd->vis_width) / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE + IMAGE_TILE_SIZE;
183 h = ((imd->y_scroll + imd->vis_height) / IMAGE_TILE_SIZE) * IMAGE_TILE_SIZE + IMAGE_TILE_SIZE;
185 return !((double)st->x * imd->scale < (double)x ||
186 (double)(st->x + imd->source_tile_width) * imd->scale > (double)w ||
187 (double)st->y * imd->scale < (double)y ||
188 (double)(st->y + imd->source_tile_height) * imd->scale > (double)h);
191 static SourceTile *source_tile_new(ImageWindow *imd, gint x, gint y)
193 SourceTile *st = NULL;
196 if (imd->source_tiles_cache_size < 4) imd->source_tiles_cache_size = 4;
198 if (imd->source_tile_width < 1 || imd->source_tile_height < 1)
200 printf("warning: source tile size too small %d x %d\n", imd->source_tile_width, imd->source_tile_height);
204 count = g_list_length(imd->source_tiles);
205 if (count >= imd->source_tiles_cache_size)
209 work = g_list_last(imd->source_tiles);
210 while (work && count >= imd->source_tiles_cache_size)
217 if (!source_tile_visible(imd, needle))
219 imd->source_tiles = g_list_remove(imd->source_tiles, needle);
221 if (imd->func_tile_dispose)
223 if (debug) printf("tile dispose: %d x %d @ %d x %d\n",
224 needle->x, needle->y, imd->x_scroll, imd->y_scroll);
225 imd->func_tile_dispose(imd, needle->x, needle->y,
226 imd->source_tile_width, imd->source_tile_height,
227 needle->pixbuf, imd->data_tile);
236 source_tile_free(needle);
243 printf("we still think %d x %d is visble\n", needle->x, needle->y);
249 printf("cache count %d, max is %d\n", count, imd->source_tiles_cache_size);
255 st = g_new0(SourceTile, 1);
256 st->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
257 imd->source_tile_width, imd->source_tile_height);
260 st->x = (x / imd->source_tile_width) * imd->source_tile_width;
261 st->y = (y / imd->source_tile_height) * imd->source_tile_height;
264 imd->source_tiles = g_list_prepend(imd->source_tiles, st);
268 printf("tile request: %d x %d\n", st->x, st->y);
269 if (!source_tile_visible(imd, st)) printf("tile request for invisible tile!\n");
275 static void image_tile_invalidate(ImageWindow *imd, gint x, gint y, gint w, gint h)
282 x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
283 x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
285 y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
286 y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
288 work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
289 for (j = y1; j <= y2; j += imd->tile_height)
293 for (i = x1; i <= x2; i += imd->tile_width)
297 ImageTile *it = tmp->data;
299 it->render_done = TILE_RENDER_NONE;
300 it->render_todo = TILE_RENDER_ALL;
305 work = g_list_nth(work, imd->tile_cols); /* step 1 row */
309 static SourceTile *source_tile_request(ImageWindow *imd, gint x, gint y)
313 st = source_tile_new(imd, x, y);
315 if (imd->func_tile_request &&
316 imd->func_tile_request(imd, st->x, st->y,
317 imd->source_tile_width, imd->source_tile_height, st->pixbuf, imd->data_tile))
322 /* fixme: somehow invalidate the new st region */
323 image_queue(imd, st->x, st->y, imd->source_tile_width, imd->source_tile_height, FALSE, TILE_RENDER_AREA, TRUE);
325 image_tile_invalidate(imd, st->x * imd->scale, st->y * imd->scale,
326 imd->source_tile_width * imd->scale, imd->source_tile_height * imd->scale);
331 static SourceTile *source_tile_find(ImageWindow *imd, gint x, gint y)
335 work = imd->source_tiles;
338 SourceTile *st = work->data;
340 if (x >= st->x && x < st->x + imd->source_tile_width &&
341 y >= st->y && y < st->y + imd->source_tile_height)
343 if (work != imd->source_tiles)
345 imd->source_tiles = g_list_remove_link(imd->source_tiles, work);
346 imd->source_tiles = g_list_concat(work, imd->source_tiles);
357 static GList *source_tile_compute_region(ImageWindow *imd, gint x, gint y, gint w, gint h, gint request)
365 if (w > imd->image_width) w = imd->image_width;
366 if (h > imd->image_height) h = imd->image_height;
368 sx = (x / imd->source_tile_width) * imd->source_tile_width;
369 sy = (y / imd->source_tile_height) * imd->source_tile_height;
371 for (x1 = sx; x1 < x + w; x1+= imd->source_tile_width)
373 for (y1 = sy; y1 < y + h; y1 += imd->source_tile_height)
377 st = source_tile_find(imd, x1, y1);
378 if (!st && request) st = source_tile_request(imd, x1, y1);
380 if (st) list = g_list_prepend(list, st);
384 return g_list_reverse(list);
387 static void source_tile_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
391 work = imd->source_tiles;
400 if (util_clip_region(st->x, st->y, imd->source_tile_width, imd->source_tile_height,
406 pixbuf = gdk_pixbuf_new_subpixbuf(st->pixbuf, rx - st->x, ry - st->y, rw, rh);
407 if (imd->func_tile_request &&
408 imd->func_tile_request(imd, rx, ry, rw, rh, pixbuf, imd->data_tile))
410 image_tile_invalidate(imd, rx * imd->scale, ry * imd->scale, rw * imd->scale, rh * imd->scale);
412 g_object_unref(pixbuf);
418 static gint source_tile_render(ImageWindow *imd, ImageTile *it,
419 gint x, gint y, gint w, gint h,
420 gint new_data, gint fast)
426 if (imd->zoom == 1.0 || imd->scale == 1.0)
428 list = source_tile_compute_region(imd, it->x + x, it->y + y, w, h, TRUE);
438 if (util_clip_region(st->x, st->y, imd->source_tile_width, imd->source_tile_height,
439 it->x + x, it->y + y, w, h,
444 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
445 rx - st->x, ry - st->y, rw, rh);
447 else /* (imd->zoom == 1.0 || imd->scale == 1.0) */
449 gdk_draw_pixbuf(it->pixmap,
450 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
452 rx - st->x, ry - st->y,
453 rx - it->x, ry - it->y,
455 (GdkRgbDither)dither_quality, rx, ry);
462 double scale_x, scale_y;
465 if (imd->image_width == 0 || imd->image_height == 0) return FALSE;
466 scale_x = (double)imd->width / imd->image_width;
467 scale_y = (double)imd->height / imd->image_height;
469 sx = (double)(it->x + x) / scale_x;
470 sy = (double)(it->y + y) / scale_y;
471 sw = (double)w / scale_x;
472 sh = (double)h / scale_y;
474 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
477 /* draws red over draw region, to check for leaks (regions not filled) */
478 pixbuf_draw_rect(it->pixbuf, x, y, w, h, 255, 0, 0, 255, FALSE);
481 list = source_tile_compute_region(imd, sx, sy, sw, sh, TRUE);
487 gint stx, sty, stw, sth;
492 stx = floor((double)st->x * scale_x);
493 sty = floor((double)st->y * scale_y);
494 stw = ceil ((double)(st->x + imd->source_tile_width) * scale_x) - stx;
495 sth = ceil ((double)(st->y + imd->source_tile_height) * scale_y) - sty;
497 if (util_clip_region(stx, sty, stw, sth,
498 it->x + x, it->y + y, w, h,
503 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
504 rx - st->x, ry - st->y, rw, rh);
511 /* may need to use unfloored stx,sty values here */
512 offset_x = ((double)stx < (double)it->x) ?
513 (double)stx - (double)it->x : 0.0;
514 offset_y = ((double)sty < (double)it->y) ?
515 (double)sty - (double)it->y : 0.0;
517 gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh,
518 (double) 0.0 + rx - it->x + offset_x,
519 (double) 0.0 + ry - it->y + offset_y,
521 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
533 static void image_source_tile_unset(ImageWindow *imd)
535 source_tile_free_all(imd);
537 imd->source_tiles_enabled = FALSE;
540 void image_set_image_as_tiles(ImageWindow *imd, gint width, gint height,
541 gint tile_width, gint tile_height, gint cache_size,
542 ImageTileRequestFunc func_tile_request,
543 ImageTileDisposeFunc func_tile_dispose,
547 /* FIXME: unset any current image */
548 image_source_tile_unset(imd);
550 if (tile_width < 32 || tile_height < 32)
552 printf("warning: tile size too small %d x %d (min 32x32)\n", tile_width, tile_height);
555 if (width < 32 || height < 32)
557 printf("warning: tile canvas too small %d x %d (min 32x32)\n", width, height);
560 if (!func_tile_request)
562 printf("warning: tile request function is null\n");
565 printf("Setting source tiles to size %d x %d, grid is %d x %d\n", tile_width, tile_height, width, height);
567 if (cache_size < 4) cache_size = 4;
569 imd->source_tiles_enabled = TRUE;
570 imd->source_tiles_cache_size = cache_size;
571 imd->source_tile_width = tile_width;
572 imd->source_tile_height = tile_height;
574 imd->image_width = width;
575 imd->image_height = height;
577 imd->func_tile_request = func_tile_request;
578 imd->func_tile_dispose = func_tile_dispose;
579 imd->data_tile = data;
581 image_zoom_sync(imd, zoom, TRUE, FALSE, TRUE, FALSE, 0, 0);
585 static void image_queue_clear(ImageWindow *imd);
587 static void image_update_title(ImageWindow *imd);
588 static void image_update_util(ImageWindow *imd);
589 static void image_complete_util(ImageWindow *imd, gint preload);
591 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent);
593 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h);
594 static void image_overlay_queue_all(ImageWindow *imd);
596 static void image_scroller_timer_set(ImageWindow *imd, gint start);
599 static gint util_clip_region(gint x, gint y, gint w, gint h,
600 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
601 gint *rx, gint *ry, gint *rw, gint *rh)
603 if (clip_x + clip_w <= x ||
605 clip_y + clip_h <= y ||
611 *rx = MAX(x, clip_x);
612 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
614 *ry = MAX(y, clip_y);
615 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
622 *-------------------------------------------------------------------
624 *-------------------------------------------------------------------
627 static gint pixmap_calc_size(GdkPixmap *pixmap)
631 d = gdk_drawable_get_depth(pixmap);
632 gdk_drawable_get_size(pixmap, &w, &h);
633 return w * h * (d / 8);
636 static void image_tile_cache_remove(ImageWindow *imd, ImageTile *it)
640 work = imd->tile_cache;
643 CacheData *cd = work->data;
648 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
649 imd->tile_cache_size -= cd->size;
655 static void image_tile_cache_free(ImageWindow *imd, CacheData *cd)
657 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
660 g_object_unref(cd->it->pixmap);
661 cd->it->pixmap = NULL;
662 cd->it->render_done = TILE_RENDER_NONE;
666 gdk_pixbuf_unref(cd->it->pixbuf);
667 cd->it->pixbuf = NULL;
669 imd->tile_cache_size -= cd->size;
673 static void image_tile_cache_free_space(ImageWindow *imd, gint space, ImageTile *it)
678 work = g_list_last(imd->tile_cache);
680 if (imd->source_tiles_enabled && imd->scale < 1.0)
684 tiles = (imd->vis_width / IMAGE_TILE_SIZE + 1) * (imd->vis_width / IMAGE_TILE_SIZE + 1);
685 tile_max = MAX(tiles * IMAGE_TILE_SIZE * IMAGE_TILE_SIZE * 3,
686 (gint)((double)tile_cache_max * 1048576.0 * imd->scale));
690 tile_max = tile_cache_max * 1048576;
693 while (work && imd->tile_cache_size > 0 && imd->tile_cache_size + space > tile_max)
695 CacheData *cd = work->data;
697 if (cd->it != it) image_tile_cache_free(imd, cd);
701 static void image_tile_cache_add(ImageWindow *imd, ImageTile *it,
702 GdkPixmap *pixmap, GdkPixbuf *pixbuf, guint size)
706 cd = g_new(CacheData, 1);
712 imd->tile_cache = g_list_prepend(imd->tile_cache, cd);
714 imd->tile_cache_size += cd->size;
717 static void image_tile_prepare(ImageWindow *imd, ImageTile *it)
724 pixmap = gdk_pixmap_new(imd->image->window, imd->tile_width, imd->tile_height, -1);
726 size = pixmap_calc_size(pixmap);
727 image_tile_cache_free_space(imd, size, it);
730 image_tile_cache_add(imd, it, pixmap, NULL, size);
733 if ((imd->zoom != 1.0 || imd->source_tiles_enabled || (imd->pixbuf && gdk_pixbuf_get_has_alpha(imd->pixbuf)) ) &&
741 pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(imd->pixbuf),
742 gdk_pixbuf_get_has_alpha(imd->pixbuf),
743 gdk_pixbuf_get_bits_per_sample(imd->pixbuf),
744 imd->tile_width, imd->tile_height);
748 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, imd->tile_width, imd->tile_height);
751 size = gdk_pixbuf_get_rowstride(pixbuf) * imd->tile_height;
752 image_tile_cache_free_space(imd, size, it);
755 image_tile_cache_add(imd, it, NULL, pixbuf, size);
760 *-------------------------------------------------------------------
762 *-------------------------------------------------------------------
765 static ImageTile *image_tile_new(gint w, gint h)
769 it = g_new0(ImageTile, 1);
775 it->render_todo = TILE_RENDER_NONE;
776 it->render_done = TILE_RENDER_NONE;
781 static void image_tile_free(ImageTile *it)
785 if (it->pixbuf) gdk_pixbuf_unref(it->pixbuf);
786 if (it->pixmap) g_object_unref(it->pixmap);
791 static void image_tile_sync_count(ImageWindow *imd, gint n)
795 l = g_list_length(imd->tiles);
803 imd->tiles = g_list_prepend(imd->tiles, image_tile_new(imd->tile_width, imd->tile_height));
809 /* This should remove from the tail of the GList, but with large images there are many tiles,
810 * making this significantly faster for those cases.
812 while (l > n && imd->tiles)
814 ImageTile *it = imd->tiles->data;
815 imd->tiles = g_list_remove(imd->tiles, it);
816 image_tile_cache_remove(imd, it);
823 static void image_tile_sync(ImageWindow *imd, gint width, gint height, gint blank)
829 imd->tile_cols = (width + imd->tile_width - 1) / imd->tile_width;
831 rows = (height + imd->tile_height - 1) / imd->tile_height;
833 image_tile_sync_count(imd, imd->tile_cols * rows);
839 ImageTile *it = work->data;
844 if (x + imd->tile_width > width)
850 it->w = imd->tile_width;
852 if (y + imd->tile_height > height)
858 it->h = imd->tile_height;
862 it->render_todo = TILE_RENDER_NONE;
863 it->render_done = TILE_RENDER_NONE;
865 x += imd->tile_width;
869 y += imd->tile_height;
873 /* all it's are now useless in queue */
874 image_queue_clear(imd);
877 static void image_tile_render(ImageWindow *imd, ImageTile *it,
878 gint x, gint y, gint w, gint h,
879 gint new_data, gint fast)
884 if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return;
886 if (it->render_done != TILE_RENDER_ALL)
892 if (!fast) it->render_done = TILE_RENDER_ALL;
894 else if (it->render_todo != TILE_RENDER_AREA)
896 if (!fast) it->render_todo = TILE_RENDER_NONE;
900 if (!fast) it->render_todo = TILE_RENDER_NONE;
902 if (new_data) it->blank = FALSE;
904 image_tile_prepare(imd, it);
905 has_alpha = (imd->pixbuf && gdk_pixbuf_get_has_alpha(imd->pixbuf));
907 /* FIXME checker colors for alpha should be configurable,
908 * also should be drawn for blank = TRUE
913 /* no data, do fast rect fill */
914 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
917 else if (imd->source_tiles_enabled)
919 draw = source_tile_render(imd, it, x, y, w, h, new_data, fast);
921 else if (imd->zoom == 1.0 || imd->scale == 1.0)
925 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
926 (double) 0.0 - it->x,
927 (double) 0.0 - it->y,
928 1.0, 1.0, GDK_INTERP_NEAREST,
929 255, it->x + x, it->y + y,
930 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
936 gdk_draw_pixbuf(it->pixmap,
937 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
939 it->x + x, it->y + y,
942 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
947 double scale_x, scale_y;
949 if (imd->image_width == 0 || imd->image_height == 0) return;
950 scale_x = (double)imd->width / imd->image_width;
951 scale_y = (double)imd->height / imd->image_height;
953 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
954 * small sizes for anything but GDK_INTERP_NEAREST
956 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
960 gdk_pixbuf_scale(imd->pixbuf, it->pixbuf, x, y, w, h,
961 (double) 0.0 - it->x,
962 (double) 0.0 - it->y,
964 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
968 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
969 (double) 0.0 - it->x,
970 (double) 0.0 - it->y,
972 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality,
973 255, it->x + x, it->y + y,
974 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
979 if (draw && it->pixbuf && !it->blank)
981 gdk_draw_pixbuf(it->pixmap,
982 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
987 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
991 static void image_tile_expose(ImageWindow *imd, ImageTile *it,
992 gint x, gint y, gint w, gint h,
993 gint new_data, gint fast)
995 image_tile_render(imd, it, x, y, w, h, new_data, fast);
997 gdk_draw_drawable(imd->image->window, imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
999 imd->x_offset + (it->x - imd->x_scroll) + x, imd->y_offset + (it->y - imd->y_scroll) + y, w, h);
1001 if (imd->overlay_list)
1003 image_overlay_draw(imd, imd->x_offset + (it->x - imd->x_scroll) + x,
1004 imd->y_offset + (it->y - imd->y_scroll) + y,
1009 static gint image_tile_is_visible(ImageWindow *imd, ImageTile *it)
1011 return (it->x + it->w >= imd->x_scroll && it->x <= imd->x_scroll + imd->window_width - imd->x_offset * 2 &&
1012 it->y + it->h >= imd->y_scroll && it->y <= imd->y_scroll + imd->window_height - imd->y_offset * 2);
1016 *-------------------------------------------------------------------
1018 *-------------------------------------------------------------------
1022 static gint image_queue_draw_idle_cb(gpointer data)
1024 ImageWindow *imd = data;
1028 if ((!imd->pixbuf && !imd->source_tiles_enabled) || (!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1)
1030 if (!imd->completed) image_complete_util(imd, FALSE);
1032 imd->draw_idle_id = -1;
1036 if (imd->draw_queue)
1038 qd = imd->draw_queue->data;
1039 fast = (two_pass_zoom && (GdkInterpType)zoom_quality != GDK_INTERP_NEAREST && imd->scale != 1.0);
1045 /* still loading, wait till done (also drops the higher priority) */
1047 imd->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
1048 image_queue_draw_idle_cb, imd, NULL);
1049 imd->draw_idle_high = FALSE;
1052 qd = imd->draw_queue_2pass->data;
1056 if (GTK_WIDGET_REALIZED(imd->image))
1058 if (image_tile_is_visible(imd, qd->it))
1060 image_tile_expose(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1062 else if (qd->new_data)
1064 /* if new pixel data, and we already have a pixmap, update the tile */
1065 qd->it->blank = FALSE;
1066 if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
1068 image_tile_render(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1073 if (imd->draw_queue)
1075 imd->draw_queue = g_list_remove(imd->draw_queue, qd);
1078 imd->draw_queue_2pass = g_list_append(imd->draw_queue_2pass, qd);
1087 imd->draw_queue_2pass = g_list_remove(imd->draw_queue_2pass, qd);
1091 if (!imd->draw_queue && !imd->draw_queue_2pass)
1093 if (!imd->completed) image_complete_util(imd, FALSE);
1095 imd->draw_idle_id = -1;
1102 static QueueData *image_queue_combine(ImageWindow *imd, QueueData *qd)
1104 QueueData *found = NULL;
1107 work = imd->draw_queue;
1108 while (work && !found)
1113 if (found->it != qd->it) found = NULL;
1118 if (found->x + found->w < qd->x + qd->w) found->w += (qd->x + qd->w) - (found->x + found->w);
1119 if (found->x > qd->x)
1121 found->w += found->x - qd->x;
1125 if (found->y + found->h < qd->y + qd->h) found->h += (qd->y + qd->h) - (found->y + found->h);
1126 if (found->y > qd->y)
1128 found->h += found->y - qd->y;
1131 found->new_data |= qd->new_data;
1137 static gint image_clamp_to_visible(ImageWindow *imd, gint *x, gint *y, gint *w, gint *h)
1144 vw = imd->vis_width;
1145 vh = imd->vis_height;
1150 if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1153 nx = CLAMP(*x, vx, vx + vw);
1154 nw = CLAMP(*w - (nx - *x), 1, vw);
1156 ny = CLAMP(*y, vy, vy + vh);
1157 nh = CLAMP(*h - (ny - *y), 1, vh);
1167 static gint image_queue_to_tiles(ImageWindow *imd, gint x, gint y, gint w, gint h,
1168 gint clamp, TileRenderType render, gint new_data)
1175 if (clamp && !image_clamp_to_visible(imd, &x, &y, &w, &h)) return FALSE;
1177 x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
1178 x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
1180 y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
1181 y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
1183 work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
1184 for (j = y1; j <= y2; j += imd->tile_height)
1188 for (i = x1; i <= x2; i += imd->tile_width)
1192 ImageTile *it = tmp->data;
1195 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1196 (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1198 it->render_todo = render;
1201 qd = g_new(QueueData, 1);
1203 qd->new_data = new_data;
1213 qd->w = x + w - i - qd->x;
1214 if (qd->x + qd->w > imd->tile_width) qd->w = imd->tile_width - qd->x;
1225 qd->h = y + h - j - qd->y;
1226 if (qd->y + qd->h > imd->tile_height) qd->h = imd->tile_height - qd->y;
1228 if (qd->w < 1 || qd->h < 1 || /* <--- sanity checks, rare cases cause this */
1229 image_queue_combine(imd, qd))
1235 imd->draw_queue = g_list_append(imd->draw_queue, qd);
1241 work = g_list_nth(work, imd->tile_cols); /* step 1 row */
1247 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
1248 gint clamp, TileRenderType render, gint new_data)
1252 nx = CLAMP(x, 0, imd->width - 1);
1253 ny = CLAMP(y, 0, imd->height - 1);
1256 w = CLAMP(w, 0, imd->width - nx);
1257 h = CLAMP(h, 0, imd->height - ny);
1258 if (w < 1 || h < 1) return;
1260 if (image_queue_to_tiles(imd, nx, ny, w, h, clamp, render, new_data) &&
1261 ((!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1 || !imd->draw_idle_high))
1263 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
1264 imd->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW,
1265 image_queue_draw_idle_cb, imd, NULL);
1266 imd->draw_idle_high = TRUE;
1270 static void image_queue_list_free(GList *list)
1287 static void image_queue_clear(ImageWindow *imd)
1289 image_queue_list_free(imd->draw_queue);
1290 imd->draw_queue = NULL;
1292 image_queue_list_free(imd->draw_queue_2pass);
1293 imd->draw_queue_2pass = NULL;
1295 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
1296 imd->draw_idle_id = -1;
1300 *-------------------------------------------------------------------
1302 *-------------------------------------------------------------------
1305 static gint image_top_window_sizable(ImageWindow *imd)
1307 if (!imd->top_window) return FALSE;
1308 if (!fit_window) return FALSE;
1309 if (!imd->top_window_sync) return FALSE;
1310 if (!imd->image->window) return FALSE;
1311 if (window_maximized(imd->top_window)) return FALSE;
1316 static gint image_size_top_window(ImageWindow *imd, gint w, gint h)
1320 if (!image_top_window_sizable(imd)) return FALSE;
1322 if (limit_window_size)
1324 gint sw = gdk_screen_width() * max_window_size / 100;
1325 gint sh = gdk_screen_height() * max_window_size / 100;
1331 w += (imd->top_window->allocation.width - imd->image->allocation.width);
1332 h += (imd->top_window->allocation.height - imd->image->allocation.height);
1334 gdk_drawable_get_size(imd->top_window->window, &ww, &wh);
1335 if (w == ww && h == wh) return FALSE;
1337 gdk_window_resize(imd->top_window->window, w, h);
1342 static void image_redraw(ImageWindow *imd, gint new_data)
1344 image_queue_clear(imd);
1345 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, new_data);
1348 static void image_border_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
1350 gint rx, ry, rw, rh;
1352 if (!imd->image->window) return;
1354 if (!imd->pixbuf && !imd->source_tiles_enabled)
1356 if (util_clip_region(x, y, w, h,
1358 imd->window_width, imd->window_height,
1359 &rx, &ry, &rw, &rh))
1361 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1362 image_overlay_draw(imd, rx, ry, rw, rh);
1367 if (imd->vis_width < imd->window_width)
1369 if (imd->x_offset > 0 &&
1370 util_clip_region(x, y, w, h,
1372 imd->x_offset, imd->window_height,
1373 &rx, &ry, &rw, &rh))
1375 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1376 image_overlay_draw(imd, rx, ry, rw, rh);
1378 if (imd->window_width - imd->vis_width - imd->x_offset > 0 &&
1379 util_clip_region(x, y, w, h,
1380 imd->x_offset + imd->vis_width, 0,
1381 imd->window_width - imd->vis_width - imd->x_offset, imd->window_height,
1382 &rx, &ry, &rw, &rh))
1384 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1385 image_overlay_draw(imd, rx, ry, rw, rh);
1388 if (imd->vis_height < imd->window_height)
1390 if (imd->y_offset > 0 &&
1391 util_clip_region(x, y, w, h,
1393 imd->vis_width, imd->y_offset,
1394 &rx, &ry, &rw, &rh))
1396 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1397 image_overlay_draw(imd, rx, ry, rw, rh);
1399 if (imd->window_height - imd->vis_height - imd->y_offset > 0 &&
1400 util_clip_region(x, y, w, h,
1401 imd->x_offset, imd->y_offset + imd->vis_height,
1402 imd->vis_width, imd->window_height - imd->vis_height - imd->y_offset,
1403 &rx, &ry, &rw, &rh))
1405 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
1406 image_overlay_draw(imd, rx, ry, rw, rh);
1411 static void image_border_clear(ImageWindow *imd)
1413 image_border_draw(imd, 0, 0, imd->window_width, imd->window_height);
1416 static void image_scroll_notify(ImageWindow *imd)
1418 if (imd->func_scroll_notify && imd->scale)
1420 imd->func_scroll_notify(imd,
1421 (gint)((gdouble)imd->x_scroll / imd->scale),
1422 (gint)((gdouble)imd->y_scroll / imd->scale),
1423 (gint)((gdouble)imd->image_width - imd->vis_width / imd->scale),
1424 (gint)((gdouble)imd->image_height - imd->vis_height / imd->scale),
1425 imd->data_scroll_notify);
1429 static gint image_scroll_clamp(ImageWindow *imd)
1434 if (imd->zoom == 0.0)
1439 image_scroll_notify(imd);
1443 old_xs = imd->x_scroll;
1444 old_ys = imd->y_scroll;
1446 if (imd->x_offset > 0)
1452 imd->x_scroll = CLAMP(imd->x_scroll, 0, imd->width - imd->vis_width);
1455 if (imd->y_offset > 0)
1461 imd->y_scroll = CLAMP(imd->y_scroll, 0, imd->height - imd->vis_height);
1464 image_scroll_notify(imd);
1466 return (old_xs != imd->x_scroll || old_ys != imd->y_scroll);
1469 static gint image_zoom_clamp(ImageWindow *imd, gdouble zoom, gint force, gint new)
1474 zoom = CLAMP(zoom, imd->zoom_min, imd->zoom_max);
1476 if (imd->zoom == zoom && !force) return FALSE;
1478 w = imd->image_width;
1479 h = imd->image_height;
1481 if (zoom == 0.0 && !imd->pixbuf)
1485 else if (zoom == 0.0)
1491 sizeable = (new && image_top_window_sizable(imd));
1495 max_w = gdk_screen_width();
1496 max_h = gdk_screen_height();
1498 if (limit_window_size)
1500 max_w = max_w * max_window_size / 100;
1501 max_h = max_h * max_window_size / 100;
1506 max_w = imd->window_width;
1507 max_h = imd->window_height;
1510 if ((zoom_to_fit_expands && !sizeable) || w > max_w || h > max_h)
1512 if ((gdouble)max_w / w > (gdouble)max_h / h)
1514 scale = (gdouble)max_h / h;
1516 w = w * scale + 0.5;
1517 if (w > max_w) w = max_w;
1521 scale = (gdouble)max_w / w;
1523 h = h * scale + 0.5;
1524 if (h > max_h) h = max_h;
1534 else if (zoom > 0.0) /* zoom orig, in */
1542 scale = 1.0 / (0.0 - zoom);
1555 static gint image_size_clamp(ImageWindow *imd)
1557 gint old_vw, old_vh;
1559 old_vw = imd->vis_width;
1560 old_vh = imd->vis_height;
1562 if (imd->width < imd->window_width)
1564 imd->vis_width = imd->width;
1565 imd->x_offset = (imd->window_width - imd->width) / 2;
1569 imd->vis_width = imd->window_width;
1573 if (imd->height < imd->window_height)
1575 imd->vis_height = imd->height;
1576 imd->y_offset = (imd->window_height - imd->height) / 2;
1580 imd->vis_height = imd->window_height;
1584 return (old_vw != imd->vis_width || old_vh != imd->vis_height);
1587 static void image_size_sync(ImageWindow *imd, gint new_width, gint new_height)
1589 if (imd->window_width == new_width && imd->window_height == new_height) return;
1591 imd->window_width = new_width;
1592 imd->window_height = new_height;
1594 if (imd->zoom == 0.0) image_zoom_clamp(imd, 0.0, TRUE, FALSE);
1596 image_size_clamp(imd);
1597 image_scroll_clamp(imd);
1600 gtk_widget_set_size_request(imd->image, imd->window_width, imd->window_height);
1603 /* ensure scroller remains visible */
1604 if (imd->scroller_overlay != -1)
1606 gint update = FALSE;
1608 if (imd->scroller_x > new_width)
1610 imd->scroller_x = new_width;
1611 imd->scroller_xpos = new_width;
1614 if (imd->scroller_y > new_height)
1616 imd->scroller_y = new_height;
1617 imd->scroller_ypos = new_height;
1625 if (image_overlay_get(imd, imd->scroller_overlay, &pixbuf, NULL, NULL))
1629 w = gdk_pixbuf_get_width(pixbuf);
1630 h = gdk_pixbuf_get_height(pixbuf);
1631 image_overlay_set(imd, imd->scroller_overlay, pixbuf,
1632 imd->scroller_x - w / 2, imd->scroller_y - h / 2);
1637 /* clear any borders */
1638 image_border_clear(imd);
1640 image_tile_sync(imd, imd->width, imd->height, FALSE);
1642 /* no longer needed? (expose event should be doing this for us) */
1643 image_redraw(imd, FALSE);
1646 if (imd->title_show_zoom) image_update_title(imd);
1647 image_update_util(imd);
1651 *-------------------------------------------------------------------
1653 *-------------------------------------------------------------------
1656 static void image_update_title(ImageWindow *imd)
1658 gchar *title = NULL;
1660 gchar *collection = NULL;
1662 if (!imd->top_window) return;
1664 if (imd->collection && collection_to_number(imd->collection) >= 0)
1667 name = imd->collection->name;
1668 if (!name) name = _("Untitled");
1669 collection = g_strdup_printf(" (Collection %s)", name);
1672 if (imd->title_show_zoom)
1674 gchar *buf = image_zoom_get_as_text(imd);
1675 zoom = g_strconcat(" [", buf, "]", NULL);
1679 title = g_strdup_printf("%s%s%s%s%s%s",
1680 imd->title ? imd->title : "",
1681 imd->image_name ? imd->image_name : "",
1683 collection ? collection : "",
1684 imd->image_name ? " - " : "",
1685 imd->title_right ? imd->title_right : "");
1687 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
1694 static void image_update_util(ImageWindow *imd)
1696 if (imd->func_update) imd->func_update(imd, imd->data_update);
1699 static void image_complete_util(ImageWindow *imd, gint preload)
1701 if (imd->il && imd->pixbuf != image_loader_get_pixbuf(imd->il)) return;
1703 if (debug) printf("image load completed \"%s\" (%s)\n",
1704 (preload) ? imd->read_ahead_path : imd->image_path,
1705 (preload) ? "preload" : "current");
1707 if (!preload) imd->completed = TRUE;
1708 if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
1711 static void image_new_util(ImageWindow *imd)
1713 if (imd->func_new) imd->func_new(imd, imd->data_new);
1716 static void image_scroll_real(ImageWindow *imd, gint x, gint y)
1722 if (!imd->pixbuf && !imd->source_tiles_enabled) return;
1724 old_x = imd->x_scroll;
1725 old_y = imd->y_scroll;
1730 image_scroll_clamp(imd);
1731 if (imd->x_scroll == old_x && imd->y_scroll == old_y) return;
1733 if (imd->overlay_list)
1737 new_x = imd->x_scroll;
1738 new_y = imd->y_scroll;
1739 imd->x_scroll = old_x;
1740 imd->y_scroll = old_y;
1741 image_overlay_queue_all(imd);
1742 imd->x_scroll = new_x;
1743 imd->y_scroll = new_y;
1746 x_off = imd->x_scroll - old_x;
1747 y_off = imd->y_scroll - old_y;
1749 w = imd->vis_width - abs(x_off);
1750 h = imd->vis_height - abs(y_off);
1754 /* scrolled completely to new material */
1755 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, FALSE);
1786 gc = gdk_gc_new(imd->image->window);
1787 gdk_gc_set_exposures(gc, TRUE);
1788 gdk_draw_drawable(imd->image->window, gc,
1790 x2 + imd->x_offset, y2 + imd->y_offset,
1791 x1 + imd->x_offset, y1 + imd->y_offset, w, h);
1794 if (imd->overlay_list)
1796 image_overlay_queue_all(imd);
1799 w = imd->vis_width - w;
1800 h = imd->vis_height - h;
1805 x_off > 0 ? imd->x_scroll + (imd->vis_width - w) : imd->x_scroll, imd->y_scroll,
1806 w, imd->vis_height, TRUE, TILE_RENDER_ALL, FALSE);
1810 /* FIXME, to optimize this, remove overlap */
1812 imd->x_scroll, y_off > 0 ? imd->y_scroll + (imd->vis_height - h) : imd->y_scroll,
1813 imd->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE);
1818 static void widget_set_cursor(GtkWidget *widget, gint icon)
1822 if (!widget->window) return;
1830 cursor = gdk_cursor_new (icon);
1833 gdk_window_set_cursor(widget->window, cursor);
1835 if (cursor) gdk_cursor_unref(cursor);
1839 *-------------------------------------------------------------------
1840 * image pixbuf handling
1841 *-------------------------------------------------------------------
1844 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
1845 gint force, gint blank, gint new,
1846 gint center_point, gint px, gint py)
1849 gint old_cx, old_cy;
1853 old_scale = imd->scale;
1856 px = CLAMP(px, 0, imd->width);
1857 py = CLAMP(py, 0, imd->height);
1858 old_cx = imd->x_scroll + (px - imd->x_offset);
1859 old_cy = imd->y_scroll + (py - imd->y_offset);
1864 old_cx = imd->x_scroll + imd->vis_width / 2;
1865 old_cy = imd->y_scroll + imd->vis_height / 2;
1868 if (!image_zoom_clamp(imd, zoom, force, new)) return;
1870 clamped = image_size_clamp(imd);
1871 sized = image_size_top_window(imd, imd->width, imd->height);
1875 /* force means new image, so update scroll offset per options */
1876 switch (scroll_reset_method)
1878 case SCROLL_RESET_NOCHANGE:
1879 /* maintain old scroll position, do nothing */
1881 case SCROLL_RESET_CENTER:
1882 /* center new image */
1883 imd->x_scroll = ((double)imd->image_width / 2.0 * imd->scale) - imd->vis_width / 2;
1884 imd->y_scroll = ((double)imd->image_height / 2.0 * imd->scale) - imd->vis_height / 2;
1886 case SCROLL_RESET_TOPLEFT:
1888 /* reset to upper left */
1896 /* user zoom does not force, so keep visible center point */
1899 imd->x_scroll = old_cx / old_scale * imd->scale - (px - imd->x_offset);
1900 imd->y_scroll = old_cy / old_scale * imd->scale - (py - imd->y_offset);
1904 imd->x_scroll = old_cx / old_scale * imd->scale - (imd->vis_width / 2);
1905 imd->y_scroll = old_cy / old_scale * imd->scale - (imd->vis_height / 2);
1908 image_scroll_clamp(imd);
1910 image_tile_sync(imd, imd->width, imd->height, blank);
1912 /* If the window was not sized, redraw the image - we know there will be no size/expose signal.
1913 * But even if a size is claimed, there is no guarantee that the window manager will allow it,
1914 * so redraw the window anyway :/
1916 if (sized || clamped) image_border_clear(imd);
1917 image_redraw(imd, FALSE);
1919 if (imd->title_show_zoom) image_update_title(imd);
1920 image_update_util(imd);
1923 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new)
1927 /* no pixbuf so just clear the window */
1928 imd->image_width = 0;
1929 imd->image_height = 0;
1932 if (imd->image->window)
1934 gdk_window_clear(imd->image->window);
1935 image_overlay_draw(imd, 0, 0, imd->window_width, imd->window_height);
1938 image_update_util(imd);
1943 imd->image_width = gdk_pixbuf_get_width(imd->pixbuf);
1944 imd->image_height = gdk_pixbuf_get_height(imd->pixbuf);
1947 /* reset scrolling */
1952 image_zoom_sync(imd, zoom, TRUE, blank, new, FALSE, 0, 0);
1955 static void image_set_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gint new)
1957 if (pixbuf) g_object_ref(pixbuf);
1958 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1959 imd->pixbuf = pixbuf;
1961 image_pixbuf_sync(imd, zoom, FALSE, new);
1964 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
1966 GdkPixbuf *new = NULL;
1970 imd->delay_alter_type = ALTER_NONE;
1972 if (!imd->pixbuf) return;
1974 x = imd->x_scroll + (imd->vis_width / 2);
1975 y = imd->y_scroll + (imd->vis_height / 2);
1979 case ALTER_ROTATE_90:
1980 new = pixbuf_copy_rotate_90(imd->pixbuf, FALSE);
1982 x = imd->height - y;
1985 case ALTER_ROTATE_90_CC:
1986 new = pixbuf_copy_rotate_90(imd->pixbuf, TRUE);
1991 case ALTER_ROTATE_180:
1992 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, TRUE);
1994 y = imd->height - y;
1997 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, FALSE);
2001 new = pixbuf_copy_mirror(imd->pixbuf, FALSE, TRUE);
2002 y = imd->height - y;
2014 image_set_pixbuf(imd, new, imd->zoom, TRUE);
2015 g_object_unref(new);
2017 if (imd->zoom != 0.0)
2019 image_scroll(imd, x - (imd->vis_width / 2), y - (imd->vis_height / 2));
2024 g_object_unref(imd->pixbuf);
2029 static void image_post_process(ImageWindow *imd, gint clamp)
2031 if (exif_rotate_enable && imd->pixbuf)
2036 ed = exif_read(imd->image_path);
2037 if (ed && exif_get_integer(ed, "Orientation", &orientation))
2039 /* see http://jpegclub.org/exif_orientation.html
2042 888888 888888 88 88 8888888888 88 88 8888888888
2043 88 88 88 88 88 88 88 88 88 88 88 88
2044 8888 8888 8888 8888 88 8888888888 8888888888 88
2049 switch (orientation)
2051 case EXIF_ORIENTATION_TOP_LEFT:
2052 /* normal -- nothing to do */
2054 case EXIF_ORIENTATION_TOP_RIGHT:
2056 imd->delay_alter_type = ALTER_MIRROR;
2058 case EXIF_ORIENTATION_BOTTOM_RIGHT:
2060 imd->delay_alter_type = ALTER_ROTATE_180;
2062 case EXIF_ORIENTATION_BOTTOM_LEFT:
2064 imd->delay_alter_type = ALTER_FLIP;
2066 case EXIF_ORIENTATION_LEFT_TOP:
2067 /* not implemented -- too wacky to fix in one step */
2069 case EXIF_ORIENTATION_RIGHT_TOP:
2070 /* rotated -90 (270) */
2071 imd->delay_alter_type = ALTER_ROTATE_90;
2073 case EXIF_ORIENTATION_RIGHT_BOTTOM:
2074 /* not implemented -- too wacky to fix in one step */
2076 case EXIF_ORIENTATION_LEFT_BOTTOM:
2078 imd->delay_alter_type = ALTER_ROTATE_90_CC;
2081 /* The other values are out of range */
2088 if (imd->delay_alter_type != ALTER_NONE)
2090 image_alter_real(imd, imd->delay_alter_type, clamp);
2095 *-------------------------------------------------------------------
2096 * read ahead (prebuffer)
2097 *-------------------------------------------------------------------
2100 static void image_read_ahead_cancel(ImageWindow *imd)
2102 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
2104 image_loader_free(imd->read_ahead_il);
2105 imd->read_ahead_il = NULL;
2107 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
2108 imd->read_ahead_pixbuf = NULL;
2110 g_free(imd->read_ahead_path);
2111 imd->read_ahead_path = NULL;
2114 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
2116 ImageWindow *imd = data;
2118 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
2120 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
2121 if (imd->read_ahead_pixbuf)
2123 g_object_ref(imd->read_ahead_pixbuf);
2127 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2129 image_loader_free(imd->read_ahead_il);
2130 imd->read_ahead_il = NULL;
2132 image_complete_util(imd, TRUE);
2135 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
2137 /* we even treat errors as success, maybe at least some of the file was ok */
2138 image_read_ahead_done_cb(il, data);
2141 static void image_read_ahead_start(ImageWindow *imd)
2143 /* already started ? */
2144 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
2146 /* still loading ?, do later */
2147 if (imd->il) return;
2149 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
2151 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
2153 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
2154 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
2156 image_read_ahead_cancel(imd);
2157 image_complete_util(imd, TRUE);
2161 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
2163 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
2165 image_read_ahead_cancel(imd);
2167 imd->read_ahead_path = g_strdup(path);
2169 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
2171 image_read_ahead_start(imd);
2175 *-------------------------------------------------------------------
2177 *-------------------------------------------------------------------
2180 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
2182 g_free(imd->prev_path);
2183 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2187 imd->prev_path = g_strdup(path);
2189 g_object_ref(pixbuf);
2190 imd->prev_pixbuf = pixbuf;
2194 imd->prev_path = NULL;
2195 imd->prev_pixbuf = NULL;
2198 if (debug) printf("post buffer set: %s\n", path);
2201 static gint image_post_buffer_get(ImageWindow *imd)
2205 if (imd->prev_pixbuf &&
2206 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
2208 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2209 imd->pixbuf = imd->prev_pixbuf;
2214 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2218 imd->prev_pixbuf = NULL;
2220 g_free(imd->prev_path);
2221 imd->prev_path = NULL;
2227 *-------------------------------------------------------------------
2229 *-------------------------------------------------------------------
2232 static void image_load_pixbuf_ready(ImageWindow *imd)
2234 if (imd->pixbuf || !imd->il) return;
2236 imd->pixbuf = image_loader_get_pixbuf(imd->il);
2238 if (imd->pixbuf) g_object_ref(imd->pixbuf);
2240 image_pixbuf_sync(imd, imd->zoom, TRUE, TRUE);
2243 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
2245 ImageWindow *imd = data;
2247 if (imd->delay_flip &&
2248 imd->pixbuf != image_loader_get_pixbuf(il))
2253 if (!imd->pixbuf) image_load_pixbuf_ready(imd);
2255 if (imd->scale != 1.0)
2257 x = (guint) floor((double)x * imd->scale);
2258 y = (guint) floor((double)y * imd->scale);
2259 w = (guint) ceil((double)w * imd->scale);
2260 h = (guint) ceil((double)h * imd->scale);
2265 if ((GdkInterpType)zoom_quality != GDK_INTERP_NEAREST)
2267 /* some scaling types use surrounding pixels to smooth the image,
2268 * this will expand the new area to cover up for faint black
2269 * lines caused by previous renders with non-complete image
2277 image_queue(imd, (gint) x, (gint) y, (gint) w, (gint) h, FALSE, TILE_RENDER_AREA, TRUE);
2280 static void image_load_done_cb(ImageLoader *il, gpointer data)
2282 ImageWindow *imd = data;
2284 if (debug) printf ("image done\n");
2286 if (imd->delay_flip &&
2287 imd->pixbuf != image_loader_get_pixbuf(imd->il))
2289 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2290 imd->pixbuf = image_loader_get_pixbuf(imd->il);
2291 if (imd->pixbuf) g_object_ref(imd->pixbuf);
2292 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2295 image_loader_free(imd->il);
2298 image_post_process(imd, TRUE);
2300 image_read_ahead_start(imd);
2303 static void image_load_error_cb(ImageLoader *il, gpointer data)
2305 if (debug) printf ("image error\n");
2307 /* even on error handle it like it was done,
2308 * since we have a pixbuf with _something_ */
2310 image_load_done_cb(il, data);
2313 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2314 static void image_load_buffer_throttle(ImageLoader *il)
2316 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
2318 /* Larger image files usually have larger chunks of data per pixel...
2319 * So increase the buffer read size so that the rendering chunks called
2323 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
2327 /* this read ahead is located here merely for the callbacks, above */
2329 static gint image_read_ahead_check(ImageWindow *imd)
2331 if (!imd->read_ahead_path) return FALSE;
2332 if (imd->il) return FALSE;
2334 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
2336 image_read_ahead_cancel(imd);
2340 if (imd->read_ahead_il)
2342 imd->il = imd->read_ahead_il;
2343 imd->read_ahead_il = NULL;
2345 /* override the old signals */
2346 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
2347 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
2348 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
2350 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2351 image_load_buffer_throttle(imd->il);
2354 /* do this one directly (probably should add a set func) */
2355 imd->il->func_done = image_load_done_cb;
2357 if (!imd->delay_flip)
2359 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2360 imd->pixbuf = image_loader_get_pixbuf(imd->il);
2361 if (imd->pixbuf) g_object_ref(imd->pixbuf);
2364 image_read_ahead_cancel(imd);
2367 else if (imd->read_ahead_pixbuf)
2369 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2370 imd->pixbuf = imd->read_ahead_pixbuf;
2371 imd->read_ahead_pixbuf = NULL;
2373 image_read_ahead_cancel(imd);
2375 image_post_process(imd, FALSE);
2379 image_read_ahead_cancel(imd);
2383 static gint image_load_begin(ImageWindow *imd, const gchar *path)
2385 if (debug) printf ("image begin \n");
2387 if (imd->il) return FALSE;
2389 imd->completed = FALSE;
2391 if (image_post_buffer_get(imd))
2393 if (debug) printf("from post buffer: %s\n", imd->image_path);
2395 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2399 if (image_read_ahead_check(imd))
2401 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
2403 if (!imd->delay_flip || !imd->il) image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
2407 if (!imd->delay_flip && imd->pixbuf)
2409 g_object_unref(imd->pixbuf);
2413 imd->il = image_loader_new(path);
2415 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
2416 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
2417 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
2419 if (!image_loader_start(imd->il, image_load_done_cb, imd))
2421 if (debug) printf("image start error\n");
2423 image_loader_free(imd->il);
2426 image_complete_util(imd, FALSE);
2431 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
2432 image_load_buffer_throttle(imd->il);
2435 if (!imd->delay_flip && !imd->pixbuf) image_load_pixbuf_ready(imd);
2440 static void image_reset(ImageWindow *imd)
2442 /* stops anything currently being done */
2444 if (debug) printf("image reset\n");
2446 image_loader_free(imd->il);
2449 image_queue_clear(imd);
2450 imd->delay_alter_type = ALTER_NONE;
2454 *-------------------------------------------------------------------
2456 *-------------------------------------------------------------------
2459 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
2463 image_source_tile_unset(imd);
2465 imd->zoom = zoom; /* store the zoom, needed by the loader */
2469 if (imd->image_path && isfile(imd->image_path))
2471 if (image_load_begin(imd, imd->image_path))
2473 imd->unknown = FALSE;
2478 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2479 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2480 imd->unknown = TRUE;
2482 imd->size = filesize(imd->image_path);
2483 imd->mtime = filetime(imd->image_path);
2487 if (imd->pixbuf) g_object_unref(imd->pixbuf);
2490 if (imd->image_path)
2492 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
2493 imd->mtime = filetime(imd->image_path);
2500 imd->unknown = TRUE;
2506 image_pixbuf_sync(imd, zoom, FALSE, new);
2510 image_update_util(imd);
2514 static void image_change_real(ImageWindow *imd, const gchar *path,
2515 CollectionData *cd, CollectInfo *info, gdouble zoom)
2517 GdkPixbuf *prev_pixbuf = NULL;
2518 gchar *prev_path = NULL;
2519 gint prev_clear = FALSE;
2521 imd->collection = cd;
2522 imd->collection_info = info;
2524 if (enable_read_ahead && imd->image_path && imd->pixbuf)
2528 /* current image is not finished */
2533 prev_path = g_strdup(imd->image_path);
2534 prev_pixbuf = imd->pixbuf;
2535 g_object_ref(prev_pixbuf);
2539 g_free(imd->image_path);
2540 imd->image_path = g_strdup(path);
2541 imd->image_name = filename_from_path(imd->image_path);
2543 image_change_complete(imd, zoom, TRUE);
2547 image_post_buffer_set(imd, prev_path, prev_pixbuf);
2549 g_object_unref(prev_pixbuf);
2551 else if (prev_clear)
2553 image_post_buffer_set(imd, NULL, NULL);
2556 image_update_title(imd);
2557 image_new_util(imd);
2561 *-------------------------------------------------------------------
2563 *-------------------------------------------------------------------
2566 static gint image_expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2570 ImageWindow *imd = data;
2572 image_border_draw(imd, event->area.x, event->area.y,
2573 event->area.width, event->area.height);
2576 x = MAX(0, (gint)event->area.x - imd->x_offset + imd->x_scroll);
2577 y = MAX(0, (gint)event->area.y - imd->y_offset + imd->y_scroll);
2579 image_queue(imd, x, y,
2580 MIN((gint)event->area.width, imd->width - x),
2581 MIN((gint)event->area.height, imd->height - y),
2582 FALSE, TILE_RENDER_ALL, FALSE);
2587 static void image_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
2589 ImageWindow *imd = data;
2591 image_size_sync(imd, allocation->width, allocation->height);
2595 *-------------------------------------------------------------------
2597 *-------------------------------------------------------------------
2600 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
2604 widget = imd->widget;
2605 if (!widget->window) return;
2609 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
2610 area, widget, "image_window",
2611 widget->allocation.x, widget->allocation.y,
2612 widget->allocation.width - 1, widget->allocation.height - 1);
2616 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
2617 area, widget, "image_window",
2618 widget->allocation.x, widget->allocation.y,
2619 widget->allocation.width - 1, widget->allocation.height - 1);
2623 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2625 ImageWindow *imd = data;
2627 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
2631 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2633 ImageWindow *imd = data;
2635 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2636 image_focus_paint(imd, TRUE, NULL);
2641 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2643 ImageWindow *imd = data;
2645 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2646 image_focus_paint(imd, FALSE, NULL);
2653 *-------------------------------------------------------------------
2655 *-------------------------------------------------------------------
2658 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
2662 work = imd->overlay_list;
2666 gint px, py, pw, ph;
2667 gint rx, ry, rw, rh;
2672 if (!od->visible) continue;
2674 pw = gdk_pixbuf_get_width(od->pixbuf);
2675 ph = gdk_pixbuf_get_height(od->pixbuf);
2681 if (px < 0) px = imd->window_width - pw + px;
2682 if (py < 0) py = imd->window_height - ph + py;
2685 if (util_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
2687 gdk_draw_pixbuf(imd->image->window,
2688 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
2692 (GdkRgbDither)dither_quality, rx, ry);
2697 static void image_overlay_queue_draw(ImageWindow *imd, OverlayData *od, gint hidden)
2702 w = gdk_pixbuf_get_width(od->pixbuf);
2703 h = gdk_pixbuf_get_height(od->pixbuf);
2709 if (x < 0) x = imd->window_width - w + x;
2710 if (y < 0) y = imd->window_height - h + y;
2713 image_queue(imd, imd->x_scroll - imd->x_offset + x,
2714 imd->y_scroll - imd->y_offset + y,
2716 FALSE, TILE_RENDER_ALL, FALSE);
2718 old_vis = od->visible;
2719 if (hidden) od->visible = FALSE;
2720 image_border_draw(imd, x, y, w, h);
2721 od->visible = old_vis;
2724 static void image_overlay_queue_all(ImageWindow *imd)
2728 work = imd->overlay_list;
2731 OverlayData *od = work->data;
2734 image_overlay_queue_draw(imd, od, FALSE);
2738 static OverlayData *image_overlay_find(ImageWindow *imd, gint id)
2742 work = imd->overlay_list;
2745 OverlayData *od = work->data;
2748 if (od->id == id) return od;
2754 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
2755 gint relative, gint always)
2760 if (!imd || !pixbuf) return -1;
2763 while (image_overlay_find(imd, id)) id++;
2765 od = g_new0(OverlayData, 1);
2767 od->pixbuf = pixbuf;
2768 g_object_ref(G_OBJECT(od->pixbuf));
2771 od->relative = relative;
2773 od->always = always;
2775 imd->overlay_list = g_list_append(imd->overlay_list, od);
2777 image_overlay_queue_draw(imd, od, FALSE);
2782 static void image_overlay_free(ImageWindow *imd, OverlayData *od)
2784 imd->overlay_list = g_list_remove(imd->overlay_list, od);
2786 if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
2790 static void image_overlay_list_clear(ImageWindow *imd)
2792 while (imd->overlay_list)
2796 od = imd->overlay_list->data;
2797 image_overlay_free(imd, od);
2801 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
2807 od = image_overlay_find(imd, id);
2812 image_overlay_queue_draw(imd, od, TRUE);
2814 g_object_ref(G_OBJECT(pixbuf));
2815 g_object_unref(G_OBJECT(od->pixbuf));
2816 od->pixbuf = pixbuf;
2821 image_overlay_queue_draw(imd, od, FALSE);
2825 image_overlay_queue_draw(imd, od, TRUE);
2826 image_overlay_free(imd, od);
2830 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
2834 if (!imd) return FALSE;
2836 od = image_overlay_find(imd, id);
2837 if (!od) return FALSE;
2839 if (pixbuf) *pixbuf = od->pixbuf;
2846 void image_overlay_remove(ImageWindow *imd, gint id)
2848 image_overlay_set(imd, id, NULL, 0, 0);
2852 *-------------------------------------------------------------------
2854 *-------------------------------------------------------------------
2857 #define SCROLLER_UPDATES_PER_SEC 30
2858 #define SCROLLER_DEAD_ZONE 6
2861 static gboolean image_scroller_update_cb(gpointer data)
2863 ImageWindow *imd = data;
2867 /* this was a simple scroll by difference between scroller and mouse position,
2868 * but all this math results in a smoother result and accounts for a dead zone.
2871 if (abs(imd->scroller_xpos - imd->scroller_x) < SCROLLER_DEAD_ZONE)
2877 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2878 x = (imd->scroller_xpos - imd->scroller_x) / 2 * SCROLLER_UPDATES_PER_SEC;
2879 x += (x > 0) ? -shift : shift;
2882 if (abs(imd->scroller_ypos - imd->scroller_y) < SCROLLER_DEAD_ZONE)
2888 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2889 y = (imd->scroller_ypos - imd->scroller_y) / 2 * SCROLLER_UPDATES_PER_SEC;
2890 y += (y > 0) ? -shift : shift;
2893 if (abs(x) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2899 xinc = imd->scroller_xinc;
2903 if (xinc < 0) xinc = 0;
2904 if (x < xinc) xinc = x;
2905 if (x > xinc) xinc = MIN(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2909 if (xinc > 0) xinc = 0;
2910 if (x > xinc) xinc = x;
2911 if (x < xinc) xinc = MAX(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2915 if (abs(y) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2921 yinc = imd->scroller_yinc;
2925 if (yinc < 0) yinc = 0;
2926 if (y < yinc) yinc = y;
2927 if (y > yinc) yinc = MIN(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2931 if (yinc > 0) yinc = 0;
2932 if (y > yinc) yinc = y;
2933 if (y < yinc) yinc = MAX(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2937 imd->scroller_xinc = xinc;
2938 imd->scroller_yinc = yinc;
2940 xinc = xinc / SCROLLER_UPDATES_PER_SEC;
2941 yinc = yinc / SCROLLER_UPDATES_PER_SEC;
2943 image_scroll(imd, xinc, yinc);
2948 static void image_scroller_timer_set(ImageWindow *imd, gint start)
2950 if (imd->scroller_id != -1)
2952 g_source_remove(imd->scroller_id);
2953 imd->scroller_id = -1;
2958 imd->scroller_id = g_timeout_add(1000 / SCROLLER_UPDATES_PER_SEC,
2959 image_scroller_update_cb, imd);
2963 static void image_scroller_start(ImageWindow *imd, gint x, gint y)
2965 if (imd->scroller_overlay == -1)
2970 pixbuf = pixbuf_inline(PIXBUF_INLINE_SCROLLER);
2971 w = gdk_pixbuf_get_width(pixbuf);
2972 h = gdk_pixbuf_get_height(pixbuf);
2974 imd->scroller_overlay = image_overlay_add(imd, pixbuf, x - w / 2, y - h / 2, FALSE, TRUE);
2977 imd->scroller_x = x;
2978 imd->scroller_y = y;
2979 imd->scroller_xpos = x;
2980 imd->scroller_ypos = y;
2982 image_scroller_timer_set(imd, TRUE);
2985 static void image_scroller_stop(ImageWindow *imd)
2987 if (imd->scroller_id == -1) return;
2989 image_overlay_remove(imd, imd->scroller_overlay);
2990 imd->scroller_overlay = -1;
2992 image_scroller_timer_set(imd, FALSE);
2996 *-------------------------------------------------------------------
2998 *-------------------------------------------------------------------
3001 static gint image_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3003 ImageWindow *imd = data;
3005 if (imd->scroller_id != -1)
3007 imd->scroller_xpos = bevent->x;
3008 imd->scroller_ypos = bevent->y;
3011 if (!imd->in_drag || !gdk_pointer_is_grabbed()) return FALSE;
3013 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
3019 widget_set_cursor (imd->image, GDK_FLEUR);
3023 image_scroll_real(imd, imd->drag_last_x - bevent->x, imd->drag_last_y - bevent->y);
3025 imd->drag_last_x = bevent->x;
3026 imd->drag_last_y = bevent->y;
3031 static gint image_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3033 ImageWindow *imd = data;
3035 if (imd->scroller_id != -1) return TRUE;
3037 switch (bevent->button)
3040 imd->in_drag = TRUE;
3041 imd->drag_last_x = bevent->x;
3042 imd->drag_last_y = bevent->y;
3043 imd->drag_moved = 0;
3044 gdk_pointer_grab(imd->image->window, FALSE,
3045 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3046 NULL, NULL, bevent->time);
3047 gtk_grab_add(imd->image);
3050 imd->drag_moved = 0;
3053 image_button_do(imd, bevent);
3059 gtk_widget_grab_focus(imd->widget);
3064 static gint image_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
3066 ImageWindow *imd = data;
3068 if (imd->scroller_id != -1)
3070 image_scroller_stop(imd);
3074 if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(imd->image))
3076 gtk_grab_remove(imd->image);
3077 gdk_pointer_ungrab(bevent->time);
3078 widget_set_cursor(imd->image, -1);
3081 if (bevent->button == 1 && (bevent->state & GDK_SHIFT_MASK))
3083 image_scroller_start(imd, bevent->x, bevent->y);
3085 else if (bevent->button == 1 || bevent->button == 2)
3087 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD) image_button_do(imd, bevent);
3090 imd->in_drag = FALSE;
3095 static gint image_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
3097 ImageWindow *imd = data;
3099 if (imd->scroller_id != -1)
3101 imd->scroller_xpos = imd->scroller_x;
3102 imd->scroller_ypos = imd->scroller_y;
3103 imd->scroller_xinc = 0;
3104 imd->scroller_yinc = 0;
3110 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
3112 ImageWindow *imd = data;
3114 if (imd->func_scroll &&
3115 event && event->type == GDK_SCROLL)
3117 imd->func_scroll(imd, event->direction, event->time,
3118 event->x, event->y, event->state, imd->data_scroll);
3125 static void image_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data)
3127 ImageWindow *imd = data;
3129 imd->drag_moved = IMAGE_DRAG_SCROLL_THRESHHOLD;
3133 *-------------------------------------------------------------------
3135 *-------------------------------------------------------------------
3139 *-------------------------------------------------------------------
3141 *-------------------------------------------------------------------
3144 void image_attach_window(ImageWindow *imd, GtkWidget *window,
3145 const gchar *title, const gchar *title_right, gint show_zoom)
3147 imd->top_window = window;
3149 imd->title = g_strdup(title);
3150 g_free(imd->title_right);
3151 imd->title_right = g_strdup(title_right);
3152 imd->title_show_zoom = show_zoom;
3154 image_update_title(imd);
3157 void image_set_update_func(ImageWindow *imd,
3158 void (*func)(ImageWindow *imd, gpointer data),
3161 imd->func_update = func;
3162 imd->data_update = data;
3165 void image_set_complete_func(ImageWindow *imd,
3166 void (*func)(ImageWindow *, gint preload, gpointer),
3169 imd->func_complete = func;
3170 imd->data_complete = data;
3173 void image_set_new_func(ImageWindow *imd,
3174 void (*func)(ImageWindow *, gpointer),
3177 imd->func_new = func;
3178 imd->data_new = data;
3182 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent)
3184 if (imd->func_button &&
3186 (bevent->type == GDK_BUTTON_PRESS || bevent->type == GDK_BUTTON_RELEASE))
3188 imd->func_button(imd, bevent->button, bevent->time,
3189 bevent->x, bevent->y, bevent->state, imd->data_button);
3193 void image_set_button_func(ImageWindow *imd,
3194 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
3197 imd->func_button = func;
3198 imd->data_button = data;
3201 void image_set_scroll_func(ImageWindow *imd,
3202 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
3205 imd->func_scroll = func;
3206 imd->data_scroll = data;
3209 void image_set_scroll_notify_func(ImageWindow *imd,
3210 void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
3213 imd->func_scroll_notify = func;
3214 imd->data_scroll_notify = data;
3219 const gchar *image_get_path(ImageWindow *imd)
3221 return imd->image_path;
3224 const gchar *image_get_name(ImageWindow *imd)
3226 return imd->image_name;
3229 /* merely changes path string, does not change the image! */
3230 void image_set_path(ImageWindow *imd, const gchar *newpath)
3232 g_free(imd->image_path);
3233 imd->image_path = g_strdup(newpath);
3234 imd->image_name = filename_from_path(imd->image_path);
3236 image_update_title(imd);
3237 image_new_util(imd);
3240 /* load a new image */
3242 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
3244 if (imd->image_path == path ||
3245 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
3247 image_source_tile_unset(imd);
3249 image_change_real(imd, path, NULL, NULL, zoom);
3252 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
3254 image_source_tile_unset(imd);
3256 image_set_pixbuf(imd, pixbuf, zoom, TRUE);
3257 image_new_util(imd);
3260 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
3262 if (!cd || !info || !g_list_find(cd->list, info)) return;
3264 image_source_tile_unset(imd);
3266 image_change_real(imd, info->path, cd, info, zoom);
3269 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
3271 if (collection_to_number(imd->collection) >= 0)
3273 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
3275 if (info) *info = imd->collection_info;
3279 if (info) *info = NULL;
3281 return imd->collection;
3284 if (info) *info = NULL;
3288 static void image_loader_sync_data(ImageLoader *il, gpointer data)
3290 /* change data for the callbacks directly */
3292 il->data_area_ready = data;
3293 il->data_error = data;
3294 il->data_done = data;
3295 il->data_percent = data;
3298 /* this is more like a move function
3299 * it moves most data from source to imd, source does keep a ref on the pixbuf
3301 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
3303 if (imd == source) return;
3305 imd->zoom_min = source->zoom_min;
3306 imd->zoom_max = source->zoom_max;
3308 imd->unknown = source->unknown;
3310 image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
3312 imd->collection = source->collection;
3313 imd->collection_info = source->collection_info;
3314 imd->size = source->size;
3315 imd->mtime = source->mtime;
3317 image_set_path(imd, image_get_path(source));
3319 image_loader_free(imd->il);
3322 if (imd->pixbuf && source->il)
3324 imd->il = source->il;
3327 image_loader_sync_data(imd->il, imd);
3329 imd->delay_alter_type = source->delay_alter_type;
3330 source->delay_alter_type = ALTER_NONE;
3333 image_loader_free(imd->read_ahead_il);
3334 imd->read_ahead_il = source->read_ahead_il;
3335 source->read_ahead_il = NULL;
3336 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
3338 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
3339 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
3340 source->read_ahead_pixbuf = NULL;
3342 g_free(imd->read_ahead_path);
3343 imd->read_ahead_path = source->read_ahead_path;
3344 source->read_ahead_path = NULL;
3346 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
3347 imd->prev_pixbuf = source->prev_pixbuf;
3348 source->prev_pixbuf = NULL;
3350 g_free(imd->prev_path);
3351 imd->prev_path = source->prev_path;
3352 source->prev_path = NULL;
3354 imd->completed = source->completed;
3356 imd->x_scroll = source->x_scroll;
3357 imd->y_scroll = source->y_scroll;
3359 if (imd->source_tiles_enabled)
3361 image_source_tile_unset(imd);
3364 if (source->source_tiles_enabled)
3366 imd->source_tiles_enabled = source->source_tiles_enabled;
3367 imd->source_tiles_cache_size = source->source_tiles_cache_size;
3368 imd->source_tiles = source->source_tiles;
3369 imd->source_tile_width = source->source_tile_width;
3370 imd->source_tile_height = source->source_tile_height;
3372 source->source_tiles_enabled = FALSE;
3373 source->source_tiles = NULL;
3375 imd->func_tile_request = source->func_tile_request;
3376 imd->func_tile_dispose = source->func_tile_dispose;
3377 imd->data_tile = source->data_tile;
3379 source->func_tile_request = NULL;
3380 source->func_tile_dispose = NULL;
3381 source->data_tile = NULL;
3383 imd->image_width = source->image_width;
3384 imd->image_height = source->image_height;
3386 if (image_zoom_clamp(imd, source->zoom, TRUE, TRUE))
3388 image_size_clamp(imd);
3389 image_scroll_clamp(imd);
3390 image_tile_sync(imd, imd->width, imd->height, FALSE);
3391 image_redraw(imd, FALSE);
3396 image_scroll_clamp(imd);
3401 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
3403 gint sx, sy, sw, sh;
3405 sx = (gint)floor((double)x * imd->scale);
3406 sy = (gint)floor((double)y * imd->scale);
3407 sw = (gint)ceil((double)width * imd->scale);
3408 sh = (gint)ceil((double)height * imd->scale);
3410 if (imd->source_tiles_enabled)
3412 source_tile_changed(imd, x, y, width, height);
3415 image_queue(imd, sx, sy, sw, sh, FALSE, TILE_RENDER_AREA, TRUE);
3418 void image_reload(ImageWindow *imd)
3420 if (imd->source_tiles_enabled) return;
3422 image_change_complete(imd, imd->zoom, FALSE);
3425 void image_scroll(ImageWindow *imd, gint x, gint y)
3427 image_scroll_real(imd, x, y);
3430 void image_scroll_to_point(ImageWindow *imd, gint x, gint y)
3434 px = (gdouble)x * imd->scale - imd->x_scroll;
3435 py = (gdouble)y * imd->scale - imd->y_scroll;
3437 image_scroll(imd, px, py);
3440 void image_alter(ImageWindow *imd, AlterType type)
3442 if (imd->source_tiles_enabled) return;
3446 /* still loading, wait till done */
3447 imd->delay_alter_type = type;
3451 image_alter_real(imd, type, TRUE);
3456 static void image_zoom_adjust_real(ImageWindow *imd, gdouble increment,
3457 gint center_point, gint x, gint y)
3459 gdouble zoom = imd->zoom;
3461 if (increment == 0.0) return; /* avoid possible div by zero, a no-op anyway... */
3465 if (imd->scale < 1.0)
3467 zoom = 0.0 - 1.0 / imd->scale;
3475 if (increment < 0.0)
3477 if (zoom >= 1.0 && zoom + increment < 1.0)
3479 zoom = zoom + increment - 2.0;
3483 zoom = zoom + increment;
3488 if (zoom <= -1.0 && zoom + increment > -1.0)
3490 zoom = zoom + increment + 2.0;
3494 zoom = zoom + increment;
3498 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, center_point, x, y);
3501 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
3503 image_zoom_adjust_real(imd, increment, FALSE, 0, 0);
3506 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
3508 image_zoom_adjust_real(imd, increment, TRUE, x, y);
3511 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
3513 if (min > 1.0 || max < 1.0) return;
3514 if (min < 1.0 && min > -1.0) return;
3515 if (min < -200.0 || max > 200.0) return;
3517 imd->zoom_min = min;
3518 imd->zoom_max = max;
3521 void image_zoom_set(ImageWindow *imd, gdouble zoom)
3523 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, FALSE, 0, 0);
3526 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
3530 if (!imd->pixbuf || imd->image_width < 1 || imd->image_height < 1) return;
3534 zoom = (gdouble)imd->window_height / imd->image_height;
3538 zoom = (gdouble)imd->window_width / imd->image_width;
3543 zoom = 0.0 - 1.0 / zoom;
3546 image_zoom_set(imd, zoom);
3549 gdouble image_zoom_get(ImageWindow *imd)
3554 gdouble image_zoom_get_real(ImageWindow *imd)
3559 gchar *image_zoom_get_as_text(ImageWindow *imd)
3565 gchar *approx = " ";
3567 if (imd->zoom > 0.0)
3571 else if (imd->zoom < 0.0)
3573 r = 0.0 - imd->zoom;
3575 else if (imd->zoom == 0.0 && imd->scale != 0.0)
3577 if (imd->scale >= 1.0)
3583 r = 1.0 / imd->scale;
3588 if (rint(l) != l) pl = 1;
3589 if (rint(r) != r) pr = 1;
3591 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
3594 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
3598 if (mode == ZOOM_RESET_ORIGINAL)
3602 else if (mode == ZOOM_RESET_FIT_WINDOW)
3610 zoom = image_zoom_get(imd);
3623 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
3625 if (imd->source_tiles_enabled) return;
3629 image_read_ahead_set(imd, path);
3633 image_read_ahead_cancel(imd);
3637 static gint image_auto_refresh_cb(gpointer data)
3639 ImageWindow *imd = data;
3642 if (!imd || !imd->pixbuf ||
3643 imd->il || !imd->image_path ||
3644 !update_on_time_change) return TRUE;
3646 newtime = filetime(imd->image_path);
3647 if (newtime > 0 && newtime != imd->mtime)
3649 imd->mtime = newtime;
3656 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
3658 void image_auto_refresh(ImageWindow *imd, gint interval)
3661 if (imd->source_tiles_enabled) return;
3663 if (imd->auto_refresh_id > -1)
3665 g_source_remove(imd->auto_refresh_id);
3666 imd->auto_refresh_id = -1;
3667 imd->auto_refresh_interval = -1;
3670 if (interval < 0) return;
3672 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
3674 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
3675 imd->auto_refresh_interval = interval;
3678 /* allow top window to be resized ? */
3680 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
3682 imd->top_window_sync = allow_sync;
3685 /* background colors */
3687 void image_background_set_black(ImageWindow *imd, gint black)
3693 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3694 g_object_ref(G_OBJECT(style));
3698 style->bg[GTK_STATE_NORMAL] = style->black;
3701 gtk_widget_set_style(imd->image, style);
3702 g_object_unref(G_OBJECT(style));
3704 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3707 void image_background_set_color(ImageWindow *imd, GdkColor *color)
3713 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3714 g_object_ref(G_OBJECT(style));
3720 slot = &style->bg[GTK_STATE_NORMAL];
3722 slot->red = color->red;
3723 slot->green = color->green;
3724 slot->blue = color->blue;
3727 gtk_widget_set_style(imd->image, style);
3728 g_object_unref(G_OBJECT(style));
3730 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3733 void image_set_delay_flip(ImageWindow *imd, gint delay)
3736 imd->delay_flip == delay) return;
3738 imd->delay_flip = delay;
3739 if (!imd->delay_flip && imd->il)
3741 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3743 image_load_pixbuf_ready(imd);
3745 image_queue_clear(imd);
3746 image_queue(imd, 0, 0, imd->width, imd->height, FALSE, TILE_RENDER_AREA, TRUE);
3750 /* wallpaper util */
3752 void image_to_root_window(ImageWindow *imd, gint scaled)
3755 GdkWindow *rootwindow;
3759 if (!imd || !imd->pixbuf) return;
3761 screen = gtk_widget_get_screen(imd->image);
3762 rootwindow = gdk_screen_get_root_window(screen);
3763 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
3767 pb = gdk_pixbuf_scale_simple(imd->pixbuf, gdk_screen_width(), gdk_screen_height(), (GdkInterpType)zoom_quality);
3771 pb = gdk_pixbuf_scale_simple(imd->pixbuf, imd->width, imd->height, (GdkInterpType)zoom_quality);
3774 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
3775 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
3776 gdk_window_clear(rootwindow);
3778 g_object_unref(pixmap);
3785 *-------------------------------------------------------------------
3787 *-------------------------------------------------------------------
3790 static void image_free(ImageWindow *imd)
3792 image_read_ahead_cancel(imd);
3793 image_post_buffer_set(imd, NULL, NULL);
3794 image_auto_refresh(imd, -1);
3796 g_free(imd->image_path);
3798 g_free(imd->title_right);
3801 image_tile_sync_count(imd, 0);
3802 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3804 image_scroller_timer_set(imd, FALSE);
3806 image_overlay_list_clear(imd);
3808 source_tile_free_all(imd);
3813 static void image_destroy_cb(GtkObject *widget, gpointer data)
3815 ImageWindow *imd = data;
3819 ImageWindow *image_new(gint frame)
3823 imd = g_new0(ImageWindow, 1);
3825 imd->zoom_min = IMAGE_ZOOM_MIN;
3826 imd->zoom_max = IMAGE_ZOOM_MAX;
3830 imd->draw_idle_id = -1;
3832 imd->tile_width = IMAGE_TILE_SIZE;
3833 imd->tile_height = IMAGE_TILE_SIZE;
3835 imd->top_window = NULL;
3837 imd->title_right = NULL;
3838 imd->title_show_zoom = FALSE;
3840 imd->unknown = TRUE;
3844 imd->has_frame = frame;
3845 imd->top_window_sync = FALSE;
3847 imd->tile_cache = NULL;
3848 imd->tile_cache_size = 0;
3850 imd->delay_alter_type = ALTER_NONE;
3852 imd->read_ahead_il = NULL;
3853 imd->read_ahead_pixbuf = NULL;
3854 imd->read_ahead_path = NULL;
3856 imd->completed = FALSE;
3858 imd->auto_refresh_id = -1;
3859 imd->auto_refresh_interval = -1;
3861 imd->delay_flip = FALSE;
3863 imd->func_update = NULL;
3864 imd->func_complete = NULL;
3865 imd->func_tile_request = NULL;
3866 imd->func_tile_dispose = NULL;
3868 imd->func_button = NULL;
3869 imd->func_scroll = NULL;
3871 imd->scroller_id = -1;
3872 imd->scroller_overlay = -1;
3874 imd->source_tiles_enabled = FALSE;
3875 imd->source_tiles = NULL;
3877 imd->image = gtk_drawing_area_new();
3878 gtk_widget_set_double_buffered(imd->image, FALSE);
3882 imd->widget = gtk_frame_new(NULL);
3883 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
3884 gtk_container_add(GTK_CONTAINER(imd->widget), imd->image);
3885 gtk_widget_show(imd->image);
3887 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
3888 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
3889 G_CALLBACK(image_focus_in_cb), imd);
3890 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
3891 G_CALLBACK(image_focus_out_cb), imd);
3893 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
3894 G_CALLBACK(image_focus_expose), imd);
3898 imd->widget = imd->image;
3901 g_signal_connect(G_OBJECT(imd->image), "motion_notify_event",
3902 G_CALLBACK(image_mouse_motion_cb), imd);
3903 g_signal_connect(G_OBJECT(imd->image), "button_press_event",
3904 G_CALLBACK(image_mouse_press_cb), imd);
3905 g_signal_connect(G_OBJECT(imd->image), "button_release_event",
3906 G_CALLBACK(image_mouse_release_cb), imd);
3907 g_signal_connect(G_OBJECT(imd->image), "leave_notify_event",
3908 G_CALLBACK(image_mouse_leave_cb), imd);
3909 gtk_widget_set_events(imd->image, GDK_POINTER_MOTION_MASK |
3910 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
3911 GDK_LEAVE_NOTIFY_MASK);
3913 g_signal_connect(G_OBJECT(imd->image), "expose_event",
3914 G_CALLBACK(image_expose_cb), imd);
3915 g_signal_connect_after(G_OBJECT(imd->image), "size_allocate",
3916 G_CALLBACK(image_size_cb), imd);
3918 g_signal_connect(G_OBJECT(imd->image), "drag_begin",
3919 G_CALLBACK(image_mouse_drag_cb), imd);
3920 g_signal_connect(G_OBJECT(imd->image), "scroll_event",
3921 G_CALLBACK(image_scroll_cb), imd);
3923 g_signal_connect(G_OBJECT(imd->widget), "destroy",
3924 G_CALLBACK(image_destroy_cb), imd);