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 128
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 */
123 static void image_queue_clear(ImageWindow *imd);
125 static void image_update_title(ImageWindow *imd);
126 static void image_update_util(ImageWindow *imd);
127 static void image_complete_util(ImageWindow *imd, gint preload);
129 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent);
131 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h);
132 static void image_overlay_queue_all(ImageWindow *imd);
134 static void image_scroller_timer_set(ImageWindow *imd, gint start);
137 static gint util_clip_region(gint x, gint y, gint w, gint h,
138 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
139 gint *rx, gint *ry, gint *rw, gint *rh)
141 if (clip_x + clip_w <= x ||
143 clip_y + clip_h <= y ||
149 *rx = MAX(x, clip_x);
150 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
152 *ry = MAX(y, clip_y);
153 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
160 *-------------------------------------------------------------------
162 *-------------------------------------------------------------------
165 static gint pixmap_calc_size(GdkPixmap *pixmap)
169 d = gdk_drawable_get_depth(pixmap);
170 gdk_drawable_get_size(pixmap, &w, &h);
171 return w * h * (d / 8);
174 static void image_tile_cache_remove(ImageWindow *imd, ImageTile *it)
178 work = imd->tile_cache;
181 CacheData *cd = work->data;
186 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
187 imd->tile_cache_size -= cd->size;
193 static void image_tile_cache_free(ImageWindow *imd, CacheData *cd)
195 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
198 g_object_unref(cd->it->pixmap);
199 cd->it->pixmap = NULL;
200 cd->it->render_done = TILE_RENDER_NONE;
204 gdk_pixbuf_unref(cd->it->pixbuf);
205 cd->it->pixbuf = NULL;
207 imd->tile_cache_size -= cd->size;
211 static void image_tile_cache_free_space(ImageWindow *imd, gint space, ImageTile *it)
213 GList *work = g_list_last(imd->tile_cache);
215 while (work && imd->tile_cache_size > 0 && imd->tile_cache_size + space > tile_cache_max * 1048576)
217 CacheData *cd = work->data;
219 if (cd->it != it) image_tile_cache_free(imd, cd);
223 static void image_tile_cache_add(ImageWindow *imd, ImageTile *it,
224 GdkPixmap *pixmap, GdkPixbuf *pixbuf, guint size)
228 cd = g_new(CacheData, 1);
234 imd->tile_cache = g_list_prepend(imd->tile_cache, cd);
236 imd->tile_cache_size += cd->size;
239 static void image_tile_prepare(ImageWindow *imd, ImageTile *it)
246 pixmap = gdk_pixmap_new(imd->image->window, imd->tile_width, imd->tile_height, -1);
248 size = pixmap_calc_size(pixmap);
249 image_tile_cache_free_space(imd, size, it);
252 image_tile_cache_add(imd, it, pixmap, NULL, size);
255 if ((imd->zoom != 1.0 || gdk_pixbuf_get_has_alpha(imd->pixbuf)) &&
261 pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(imd->pixbuf),
262 gdk_pixbuf_get_has_alpha(imd->pixbuf),
263 gdk_pixbuf_get_bits_per_sample(imd->pixbuf),
264 imd->tile_width, imd->tile_height);
266 size = gdk_pixbuf_get_rowstride(pixbuf) * imd->tile_height;
267 image_tile_cache_free_space(imd, size, it);
270 image_tile_cache_add(imd, it, NULL, pixbuf, size);
275 *-------------------------------------------------------------------
277 *-------------------------------------------------------------------
280 static ImageTile *image_tile_new(gint w, gint h)
284 it = g_new0(ImageTile, 1);
290 it->render_todo = TILE_RENDER_NONE;
291 it->render_done = TILE_RENDER_NONE;
296 static void image_tile_free(ImageTile *it)
300 if (it->pixbuf) gdk_pixbuf_unref(it->pixbuf);
301 if (it->pixmap) g_object_unref(it->pixmap);
306 static void image_tile_sync_count(ImageWindow *imd, gint n)
310 l = g_list_length(imd->tiles);
318 imd->tiles = g_list_prepend(imd->tiles, image_tile_new(imd->tile_width, imd->tile_height));
324 /* This should remove from the tail of the GList, but with large images there are many tiles,
325 * making this significantly faster for those cases.
327 while (l > n && imd->tiles)
329 ImageTile *it = imd->tiles->data;
330 imd->tiles = g_list_remove(imd->tiles, it);
331 image_tile_cache_remove(imd, it);
338 static void image_tile_sync(ImageWindow *imd, gint width, gint height, gint blank)
344 imd->tile_cols = (width + imd->tile_width - 1) / imd->tile_width;
346 rows = (height + imd->tile_height - 1) / imd->tile_height;
348 image_tile_sync_count(imd, imd->tile_cols * rows);
354 ImageTile *it = work->data;
359 if (x + imd->tile_width > width)
365 it->w = imd->tile_width;
367 if (y + imd->tile_height > height)
373 it->h = imd->tile_height;
377 it->render_todo = TILE_RENDER_NONE;
378 it->render_done = TILE_RENDER_NONE;
380 x += imd->tile_width;
384 y += imd->tile_height;
388 /* all it's are now useless in queue */
389 image_queue_clear(imd);
392 static void image_tile_render(ImageWindow *imd, ImageTile *it,
393 gint x, gint y, gint w, gint h,
394 gint new_data, gint fast)
399 if (it->render_todo == TILE_RENDER_NONE && it->pixmap) return;
401 if (it->render_done != TILE_RENDER_ALL)
407 if (!fast) it->render_done = TILE_RENDER_ALL;
409 else if (it->render_todo != TILE_RENDER_AREA)
411 if (!fast) it->render_todo = TILE_RENDER_NONE;
415 if (!fast) it->render_todo = TILE_RENDER_NONE;
417 if (new_data) it->blank = FALSE;
419 image_tile_prepare(imd, it);
420 has_alpha = gdk_pixbuf_get_has_alpha(imd->pixbuf);
422 /* FIXME checker colors for alpha should be configurable,
423 * also should be drawn for blank = TRUE
428 /* no data, do fast rect fill */
429 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
432 else if (imd->zoom == 1.0 || imd->scale == 1.0)
436 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
437 (double) 0.0 - it->x,
438 (double) 0.0 - it->y,
439 1.0, 1.0, GDK_INTERP_NEAREST,
440 255, it->x + x, it->y + y,
441 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
447 gdk_draw_pixbuf(it->pixmap,
448 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
450 it->x + x, it->y + y,
453 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
458 double scale_x, scale_y;
460 if (imd->image_width == 0 || imd->image_height == 0) return;
461 scale_x = (double)imd->width / imd->image_width;
462 scale_y = (double)imd->height / imd->image_height;
464 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
465 * small sizes for anything but GDK_INTERP_NEAREST
467 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
471 gdk_pixbuf_scale(imd->pixbuf, it->pixbuf, x, y, w, h,
472 (double) 0.0 - it->x,
473 (double) 0.0 - it->y,
475 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
479 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
480 (double) 0.0 - it->x,
481 (double) 0.0 - it->y,
483 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality,
484 255, it->x + x, it->y + y,
485 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
490 if (draw && it->pixbuf && !it->blank)
492 gdk_draw_pixbuf(it->pixmap,
493 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
498 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
502 static void image_tile_expose(ImageWindow *imd, ImageTile *it,
503 gint x, gint y, gint w, gint h,
504 gint new_data, gint fast)
506 image_tile_render(imd, it, x, y, w, h, new_data, fast);
508 gdk_draw_drawable(imd->image->window, imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
510 imd->x_offset + (it->x - imd->x_scroll) + x, imd->y_offset + (it->y - imd->y_scroll) + y, w, h);
512 if (imd->overlay_list)
514 image_overlay_draw(imd, imd->x_offset + (it->x - imd->x_scroll) + x,
515 imd->y_offset + (it->y - imd->y_scroll) + y,
520 static gint image_tile_is_visible(ImageWindow *imd, ImageTile *it)
522 return (it->x + it->w >= imd->x_scroll && it->x <= imd->x_scroll + imd->window_width - imd->x_offset * 2 &&
523 it->y + it->h >= imd->y_scroll && it->y <= imd->y_scroll + imd->window_height - imd->y_offset * 2);
527 *-------------------------------------------------------------------
529 *-------------------------------------------------------------------
533 static gint image_queue_draw_idle_cb(gpointer data)
535 ImageWindow *imd = data;
539 if (!imd->pixbuf || (!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1)
541 if (!imd->completed) image_complete_util(imd, FALSE);
543 imd->draw_idle_id = -1;
549 qd = imd->draw_queue->data;
550 fast = (two_pass_zoom && (GdkInterpType)zoom_quality != GDK_INTERP_NEAREST && imd->scale != 1.0);
556 /* still loading, wait till done (also drops the higher priority) */
558 imd->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
559 image_queue_draw_idle_cb, imd, NULL);
560 imd->draw_idle_high = FALSE;
563 qd = imd->draw_queue_2pass->data;
567 if (GTK_WIDGET_REALIZED(imd->image))
569 if (image_tile_is_visible(imd, qd->it))
571 image_tile_expose(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
573 else if (qd->new_data)
575 /* if new pixel data, and we already have a pixmap, update the tile */
576 qd->it->blank = FALSE;
577 if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
579 image_tile_render(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
586 imd->draw_queue = g_list_remove(imd->draw_queue, qd);
589 imd->draw_queue_2pass = g_list_append(imd->draw_queue_2pass, qd);
598 imd->draw_queue_2pass = g_list_remove(imd->draw_queue_2pass, qd);
602 if (!imd->draw_queue && !imd->draw_queue_2pass)
604 if (!imd->completed) image_complete_util(imd, FALSE);
606 imd->draw_idle_id = -1;
613 static QueueData *image_queue_combine(ImageWindow *imd, QueueData *qd)
615 QueueData *found = NULL;
618 work = imd->draw_queue;
619 while (work && !found)
624 if (found->it != qd->it) found = NULL;
629 if (found->x + found->w < qd->x + qd->w) found->w += (qd->x + qd->w) - (found->x + found->w);
630 if (found->x > qd->x)
632 found->w += found->x - qd->x;
636 if (found->y + found->h < qd->y + qd->h) found->h += (qd->y + qd->h) - (found->y + found->h);
637 if (found->y > qd->y)
639 found->h += found->y - qd->y;
642 found->new_data |= qd->new_data;
648 static gint image_clamp_to_visible(ImageWindow *imd, gint *x, gint *y, gint *w, gint *h)
656 vh = imd->vis_height;
661 if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
664 nx = CLAMP(*x, vx, vx + vw);
665 nw = CLAMP(*w - (nx - *x), 1, vw);
667 ny = CLAMP(*y, vy, vy + vh);
668 nh = CLAMP(*h - (ny - *y), 1, vh);
678 static gint image_queue_to_tiles(ImageWindow *imd, gint x, gint y, gint w, gint h,
679 gint clamp, TileRenderType render, gint new_data)
686 if (clamp && !image_clamp_to_visible(imd, &x, &y, &w, &h)) return FALSE;
688 x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
689 x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
691 y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
692 y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
694 work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
695 for (j = y1; j <= y2; j += imd->tile_height)
699 for (i = x1; i <= x2; i += imd->tile_width)
703 ImageTile *it = tmp->data;
706 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
707 (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
709 it->render_todo = render;
712 qd = g_new(QueueData, 1);
714 qd->new_data = new_data;
724 qd->w = x + w - i - qd->x;
725 if (qd->x + qd->w > imd->tile_width) qd->w = imd->tile_width - qd->x;
736 qd->h = y + h - j - qd->y;
737 if (qd->y + qd->h > imd->tile_height) qd->h = imd->tile_height - qd->y;
739 if (qd->w < 1 || qd->h < 1 || /* <--- sanity checks, rare cases cause this */
740 image_queue_combine(imd, qd))
746 imd->draw_queue = g_list_append(imd->draw_queue, qd);
752 work = g_list_nth(work, imd->tile_cols); /* step 1 row */
758 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
759 gint clamp, TileRenderType render, gint new_data)
763 nx = CLAMP(x, 0, imd->width - 1);
764 ny = CLAMP(y, 0, imd->height - 1);
767 w = CLAMP(w, 0, imd->width - nx);
768 h = CLAMP(h, 0, imd->height - ny);
769 if (w < 1 || h < 1) return;
771 if (image_queue_to_tiles(imd, nx, ny, w, h, clamp, render, new_data) &&
772 ((!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1 || !imd->draw_idle_high))
774 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
775 imd->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW,
776 image_queue_draw_idle_cb, imd, NULL);
777 imd->draw_idle_high = TRUE;
781 static void image_queue_list_free(GList *list)
798 static void image_queue_clear(ImageWindow *imd)
800 image_queue_list_free(imd->draw_queue);
801 imd->draw_queue = NULL;
803 image_queue_list_free(imd->draw_queue_2pass);
804 imd->draw_queue_2pass = NULL;
806 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
807 imd->draw_idle_id = -1;
811 *-------------------------------------------------------------------
813 *-------------------------------------------------------------------
816 static gint image_top_window_sizable(ImageWindow *imd)
818 if (!imd->top_window) return FALSE;
819 if (!fit_window) return FALSE;
820 if (!imd->top_window_sync) return FALSE;
821 if (!imd->image->window) return FALSE;
822 if (window_maximized(imd->top_window)) return FALSE;
827 static gint image_size_top_window(ImageWindow *imd, gint w, gint h)
831 if (!image_top_window_sizable(imd)) return FALSE;
833 if (limit_window_size)
835 gint sw = gdk_screen_width() * max_window_size / 100;
836 gint sh = gdk_screen_height() * max_window_size / 100;
842 w += (imd->top_window->allocation.width - imd->image->allocation.width);
843 h += (imd->top_window->allocation.height - imd->image->allocation.height);
845 gdk_drawable_get_size(imd->top_window->window, &ww, &wh);
846 if (w == ww && h == wh) return FALSE;
848 gdk_window_resize(imd->top_window->window, w, h);
853 static void image_redraw(ImageWindow *imd, gint new_data)
855 image_queue_clear(imd);
856 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, new_data);
859 static void image_border_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
863 if (!imd->image->window) return;
867 if (util_clip_region(x, y, w, h,
869 imd->window_width, imd->window_height,
872 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
873 image_overlay_draw(imd, rx, ry, rw, rh);
878 if (imd->vis_width < imd->window_width)
880 if (imd->x_offset > 0 &&
881 util_clip_region(x, y, w, h,
883 imd->x_offset, imd->window_height,
886 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
887 image_overlay_draw(imd, rx, ry, rw, rh);
889 if (imd->window_width - imd->vis_width - imd->x_offset > 0 &&
890 util_clip_region(x, y, w, h,
891 imd->x_offset + imd->vis_width, 0,
892 imd->window_width - imd->vis_width - imd->x_offset, imd->window_height,
895 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
896 image_overlay_draw(imd, rx, ry, rw, rh);
899 if (imd->vis_height < imd->window_height)
901 if (imd->y_offset > 0 &&
902 util_clip_region(x, y, w, h,
904 imd->vis_width, imd->y_offset,
907 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
908 image_overlay_draw(imd, rx, ry, rw, rh);
910 if (imd->window_height - imd->vis_height - imd->y_offset > 0 &&
911 util_clip_region(x, y, w, h,
912 imd->x_offset, imd->y_offset + imd->vis_height,
913 imd->vis_width, imd->window_height - imd->vis_height - imd->y_offset,
916 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
917 image_overlay_draw(imd, rx, ry, rw, rh);
922 static void image_border_clear(ImageWindow *imd)
924 image_border_draw(imd, 0, 0, imd->window_width, imd->window_height);
927 static gint image_scroll_clamp(ImageWindow *imd)
932 if (imd->zoom == 0.0)
939 old_xs = imd->x_scroll;
940 old_ys = imd->y_scroll;
942 if (imd->x_offset > 0)
948 imd->x_scroll = CLAMP(imd->x_scroll, 0, imd->width - imd->vis_width);
951 if (imd->y_offset > 0)
957 imd->y_scroll = CLAMP(imd->y_scroll, 0, imd->height - imd->vis_height);
960 return (old_xs != imd->x_scroll || old_ys != imd->y_scroll);
963 static gint image_zoom_clamp(ImageWindow *imd, gdouble zoom, gint force, gint new)
968 zoom = CLAMP(zoom, IMAGE_ZOOM_MIN, IMAGE_ZOOM_MAX);
970 if (imd->zoom == zoom && !force) return FALSE;
972 w = imd->image_width;
973 h = imd->image_height;
975 if (zoom == 0.0 && !imd->pixbuf)
979 else if (zoom == 0.0)
985 sizeable = (new && image_top_window_sizable(imd));
989 max_w = gdk_screen_width();
990 max_h = gdk_screen_height();
992 if (limit_window_size)
994 max_w = max_w * max_window_size / 100;
995 max_h = max_h * max_window_size / 100;
1000 max_w = imd->window_width;
1001 max_h = imd->window_height;
1004 if ((zoom_to_fit_expands && !sizeable) || w > max_w || h > max_h)
1006 if ((gdouble)max_w / w > (gdouble)max_h / h)
1008 scale = (gdouble)max_h / h;
1010 w = w * scale + 0.5;
1011 if (w > max_w) w = max_w;
1015 scale = (gdouble)max_w / w;
1017 h = h * scale + 0.5;
1018 if (h > max_h) h = max_h;
1028 else if (zoom > 0.0) /* zoom orig, in */
1036 scale = 1.0 / (0.0 - zoom);
1049 static gint image_size_clamp(ImageWindow *imd)
1051 gint old_vw, old_vh;
1053 old_vw = imd->vis_width;
1054 old_vh = imd->vis_height;
1056 if (imd->width < imd->window_width)
1058 imd->vis_width = imd->width;
1059 imd->x_offset = (imd->window_width - imd->width) / 2;
1063 imd->vis_width = imd->window_width;
1067 if (imd->height < imd->window_height)
1069 imd->vis_height = imd->height;
1070 imd->y_offset = (imd->window_height - imd->height) / 2;
1074 imd->vis_height = imd->window_height;
1078 return (old_vw != imd->vis_width || old_vh != imd->vis_height);
1081 static void image_size_sync(ImageWindow *imd, gint new_width, gint new_height)
1083 if (imd->window_width == new_width && imd->window_height == new_height) return;
1085 imd->window_width = new_width;
1086 imd->window_height = new_height;
1088 if (imd->zoom == 0.0) image_zoom_clamp(imd, 0.0, TRUE, FALSE);
1090 image_size_clamp(imd);
1091 image_scroll_clamp(imd);
1093 gtk_widget_set_size_request(imd->image, imd->window_width, imd->window_height);
1095 /* ensure scroller remains visible */
1096 if (imd->scroller_overlay != -1)
1098 gint update = FALSE;
1100 if (imd->scroller_x > new_width)
1102 imd->scroller_x = new_width;
1103 imd->scroller_xpos = new_width;
1106 if (imd->scroller_y > new_height)
1108 imd->scroller_y = new_height;
1109 imd->scroller_ypos = new_height;
1117 if (image_overlay_get(imd, imd->scroller_overlay, &pixbuf, NULL, NULL))
1121 w = gdk_pixbuf_get_width(pixbuf);
1122 h = gdk_pixbuf_get_height(pixbuf);
1123 image_overlay_set(imd, imd->scroller_overlay, pixbuf,
1124 imd->scroller_x - w / 2, imd->scroller_y - h / 2);
1129 /* clear any borders */
1130 image_border_clear(imd);
1132 image_tile_sync(imd, imd->width, imd->height, FALSE);
1134 /* no longer needed? (expose event should be doing this for us) */
1135 image_redraw(imd, FALSE);
1138 if (imd->title_show_zoom) image_update_title(imd);
1139 image_update_util(imd);
1143 *-------------------------------------------------------------------
1145 *-------------------------------------------------------------------
1148 static void image_update_title(ImageWindow *imd)
1150 gchar *title = NULL;
1152 gchar *collection = NULL;
1154 if (!imd->top_window) return;
1156 if (imd->collection && collection_to_number(imd->collection) >= 0)
1159 name = imd->collection->name;
1160 if (!name) name = _("Untitled");
1161 collection = g_strdup_printf(" (Collection %s)", name);
1164 if (imd->title_show_zoom)
1166 gchar *buf = image_zoom_get_as_text(imd);
1167 zoom = g_strconcat(" [", buf, "]", NULL);
1171 title = g_strdup_printf("%s%s%s%s%s%s",
1172 imd->title ? imd->title : "",
1173 imd->image_name ? imd->image_name : "",
1175 collection ? collection : "",
1176 imd->image_name ? " - " : "",
1177 imd->title_right ? imd->title_right : "");
1179 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
1186 static void image_update_util(ImageWindow *imd)
1188 if (imd->func_update) imd->func_update(imd, imd->data_update);
1191 static void image_complete_util(ImageWindow *imd, gint preload)
1193 if (imd->il && imd->pixbuf != image_loader_get_pixbuf(imd->il)) return;
1195 if (debug) printf("image load completed \"%s\" (%s)\n",
1196 (preload) ? imd->read_ahead_path : imd->image_path,
1197 (preload) ? "preload" : "current");
1199 if (!preload) imd->completed = TRUE;
1200 if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
1203 static void image_new_util(ImageWindow *imd)
1205 if (imd->func_new) imd->func_new(imd, imd->data_new);
1208 static void image_scroll_real(ImageWindow *imd, gint x, gint y)
1214 if (!imd->pixbuf) return;
1216 old_x = imd->x_scroll;
1217 old_y = imd->y_scroll;
1222 image_scroll_clamp(imd);
1223 if (imd->x_scroll == old_x && imd->y_scroll == old_y) return;
1225 if (imd->overlay_list)
1229 new_x = imd->x_scroll;
1230 new_y = imd->y_scroll;
1231 imd->x_scroll = old_x;
1232 imd->y_scroll = old_y;
1233 image_overlay_queue_all(imd);
1234 imd->x_scroll = new_x;
1235 imd->y_scroll = new_y;
1238 x_off = imd->x_scroll - old_x;
1239 y_off = imd->y_scroll - old_y;
1241 w = imd->vis_width - abs(x_off);
1242 h = imd->vis_height - abs(y_off);
1246 /* scrolled completely to new material */
1247 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, FALSE);
1278 gc = gdk_gc_new(imd->image->window);
1279 gdk_gc_set_exposures(gc, TRUE);
1280 gdk_draw_drawable(imd->image->window, gc,
1282 x2 + imd->x_offset, y2 + imd->y_offset,
1283 x1 + imd->x_offset, y1 + imd->y_offset, w, h);
1286 if (imd->overlay_list)
1288 image_overlay_queue_all(imd);
1291 w = imd->vis_width - w;
1292 h = imd->vis_height - h;
1297 x_off > 0 ? imd->x_scroll + (imd->vis_width - w) : imd->x_scroll, imd->y_scroll,
1298 w, imd->vis_height, TRUE, TILE_RENDER_ALL, FALSE);
1302 /* FIXME, to optimize this, remove overlap */
1304 imd->x_scroll, y_off > 0 ? imd->y_scroll + (imd->vis_height - h) : imd->y_scroll,
1305 imd->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE);
1310 static void widget_set_cursor(GtkWidget *widget, gint icon)
1314 if (!widget->window) return;
1322 cursor = gdk_cursor_new (icon);
1325 gdk_window_set_cursor(widget->window, cursor);
1327 if (cursor) gdk_cursor_unref(cursor);
1331 *-------------------------------------------------------------------
1332 * image pixbuf handling
1333 *-------------------------------------------------------------------
1336 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
1337 gint force, gint blank, gint new,
1338 gint center_point, gint px, gint py)
1341 gint old_cx, old_cy;
1345 old_scale = imd->scale;
1348 px = CLAMP(px, 0, imd->width);
1349 py = CLAMP(py, 0, imd->height);
1350 old_cx = imd->x_scroll + (px - imd->x_offset);
1351 old_cy = imd->y_scroll + (py - imd->y_offset);
1356 old_cx = imd->x_scroll + imd->vis_width / 2;
1357 old_cy = imd->y_scroll + imd->vis_height / 2;
1360 if (!image_zoom_clamp(imd, zoom, force, new)) return;
1362 clamped = image_size_clamp(imd);
1363 sized = image_size_top_window(imd, imd->width, imd->height);
1367 /* force means new image, so update scroll offset per options */
1368 switch (scroll_reset_method)
1370 case SCROLL_RESET_NOCHANGE:
1371 /* maintain old scroll position, do nothing */
1373 case SCROLL_RESET_CENTER:
1374 /* center new image */
1375 imd->x_scroll = ((double)imd->image_width / 2.0 * imd->scale) - imd->vis_width / 2;
1376 imd->y_scroll = ((double)imd->image_height / 2.0 * imd->scale) - imd->vis_height / 2;
1378 case SCROLL_RESET_TOPLEFT:
1380 /* reset to upper left */
1388 /* user zoom does not force, so keep visible center point */
1391 imd->x_scroll = old_cx / old_scale * imd->scale - (px - imd->x_offset);
1392 imd->y_scroll = old_cy / old_scale * imd->scale - (py - imd->y_offset);
1396 imd->x_scroll = old_cx / old_scale * imd->scale - (imd->vis_width / 2);
1397 imd->y_scroll = old_cy / old_scale * imd->scale - (imd->vis_height / 2);
1400 image_scroll_clamp(imd);
1402 image_tile_sync(imd, imd->width, imd->height, blank);
1404 /* If the window was not sized, redraw the image - we know there will be no size/expose signal.
1405 * But even if a size is claimed, there is no guarantee that the window manager will allow it,
1406 * so redraw the window anyway :/
1408 if (sized || clamped) image_border_clear(imd);
1409 image_redraw(imd, FALSE);
1411 if (imd->title_show_zoom) image_update_title(imd);
1412 image_update_util(imd);
1415 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new)
1419 /* no pixbuf so just clear the window */
1420 imd->image_width = 0;
1421 imd->image_height = 0;
1424 if (imd->image->window)
1426 gdk_window_clear(imd->image->window);
1427 image_overlay_draw(imd, 0, 0, imd->window_width, imd->window_height);
1430 image_update_util(imd);
1435 imd->image_width = gdk_pixbuf_get_width(imd->pixbuf);
1436 imd->image_height = gdk_pixbuf_get_height(imd->pixbuf);
1439 /* reset scrolling */
1444 image_zoom_sync(imd, zoom, TRUE, blank, new, FALSE, 0, 0);
1447 static void image_set_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gint new)
1449 if (pixbuf) g_object_ref(pixbuf);
1450 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1451 imd->pixbuf = pixbuf;
1453 image_pixbuf_sync(imd, zoom, FALSE, new);
1456 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
1458 GdkPixbuf *new = NULL;
1462 imd->delay_alter_type = ALTER_NONE;
1464 if (!imd->pixbuf) return;
1466 x = imd->x_scroll + (imd->vis_width / 2);
1467 y = imd->y_scroll + (imd->vis_height / 2);
1471 case ALTER_ROTATE_90:
1472 new = pixbuf_copy_rotate_90(imd->pixbuf, FALSE);
1474 x = imd->height - y;
1477 case ALTER_ROTATE_90_CC:
1478 new = pixbuf_copy_rotate_90(imd->pixbuf, TRUE);
1483 case ALTER_ROTATE_180:
1484 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, TRUE);
1486 y = imd->height - y;
1489 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, FALSE);
1493 new = pixbuf_copy_mirror(imd->pixbuf, FALSE, TRUE);
1494 y = imd->height - y;
1506 image_set_pixbuf(imd, new, imd->zoom, TRUE);
1507 g_object_unref(new);
1509 if (imd->zoom != 0.0)
1511 image_scroll(imd, x - (imd->vis_width / 2), y - (imd->vis_height / 2));
1516 g_object_unref(imd->pixbuf);
1521 static void image_post_process(ImageWindow *imd, gint clamp)
1523 if (exif_rotate_enable && imd->pixbuf)
1528 ed = exif_read(imd->image_path);
1529 if (ed && exif_get_integer(ed, "Orientation", &orientation))
1531 /* see http://jpegclub.org/exif_orientation.html
1534 888888 888888 88 88 8888888888 88 88 8888888888
1535 88 88 88 88 88 88 88 88 88 88 88 88
1536 8888 8888 8888 8888 88 8888888888 8888888888 88
1541 switch (orientation)
1543 case EXIF_ORIENTATION_TOP_LEFT:
1544 /* normal -- nothing to do */
1546 case EXIF_ORIENTATION_TOP_RIGHT:
1548 imd->delay_alter_type = ALTER_MIRROR;
1550 case EXIF_ORIENTATION_BOTTOM_RIGHT:
1552 imd->delay_alter_type = ALTER_ROTATE_180;
1554 case EXIF_ORIENTATION_BOTTOM_LEFT:
1556 imd->delay_alter_type = ALTER_FLIP;
1558 case EXIF_ORIENTATION_LEFT_TOP:
1559 /* not implemented -- too wacky to fix in one step */
1561 case EXIF_ORIENTATION_RIGHT_TOP:
1562 /* rotated -90 (270) */
1563 imd->delay_alter_type = ALTER_ROTATE_90;
1565 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1566 /* not implemented -- too wacky to fix in one step */
1568 case EXIF_ORIENTATION_LEFT_BOTTOM:
1570 imd->delay_alter_type = ALTER_ROTATE_90_CC;
1573 /* The other values are out of range */
1580 if (imd->delay_alter_type != ALTER_NONE)
1582 image_alter_real(imd, imd->delay_alter_type, clamp);
1587 *-------------------------------------------------------------------
1588 * read ahead (prebuffer)
1589 *-------------------------------------------------------------------
1592 static void image_read_ahead_cancel(ImageWindow *imd)
1594 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
1596 image_loader_free(imd->read_ahead_il);
1597 imd->read_ahead_il = NULL;
1599 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1600 imd->read_ahead_pixbuf = NULL;
1602 g_free(imd->read_ahead_path);
1603 imd->read_ahead_path = NULL;
1606 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
1608 ImageWindow *imd = data;
1610 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
1612 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
1613 if (imd->read_ahead_pixbuf)
1615 g_object_ref(imd->read_ahead_pixbuf);
1619 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1621 image_loader_free(imd->read_ahead_il);
1622 imd->read_ahead_il = NULL;
1624 image_complete_util(imd, TRUE);
1627 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
1629 /* we even treat errors as success, maybe at least some of the file was ok */
1630 image_read_ahead_done_cb(il, data);
1633 static void image_read_ahead_start(ImageWindow *imd)
1635 /* already started ? */
1636 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
1638 /* still loading ?, do later */
1639 if (imd->il) return;
1641 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
1643 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
1645 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
1646 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
1648 image_read_ahead_cancel(imd);
1649 image_complete_util(imd, TRUE);
1653 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
1655 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
1657 image_read_ahead_cancel(imd);
1659 imd->read_ahead_path = g_strdup(path);
1661 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
1663 image_read_ahead_start(imd);
1667 *-------------------------------------------------------------------
1669 *-------------------------------------------------------------------
1672 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
1674 g_free(imd->prev_path);
1675 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1679 imd->prev_path = g_strdup(path);
1681 g_object_ref(pixbuf);
1682 imd->prev_pixbuf = pixbuf;
1686 imd->prev_path = NULL;
1687 imd->prev_pixbuf = NULL;
1690 if (debug) printf("post buffer set: %s\n", path);
1693 static gint image_post_buffer_get(ImageWindow *imd)
1697 if (imd->prev_pixbuf &&
1698 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
1700 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1701 imd->pixbuf = imd->prev_pixbuf;
1706 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1710 imd->prev_pixbuf = NULL;
1712 g_free(imd->prev_path);
1713 imd->prev_path = NULL;
1719 *-------------------------------------------------------------------
1721 *-------------------------------------------------------------------
1724 static void image_load_pixbuf_ready(ImageWindow *imd)
1726 if (imd->pixbuf || !imd->il) return;
1728 imd->pixbuf = image_loader_get_pixbuf(imd->il);
1730 if (imd->pixbuf) g_object_ref(imd->pixbuf);
1732 image_pixbuf_sync(imd, imd->zoom, TRUE, TRUE);
1735 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
1737 ImageWindow *imd = data;
1739 if (imd->delay_flip &&
1740 imd->pixbuf != image_loader_get_pixbuf(il))
1745 if (!imd->pixbuf) image_load_pixbuf_ready(imd);
1747 if (imd->scale != 1.0)
1749 x = (guint) floor((double)x * imd->scale);
1750 y = (guint) floor((double)y * imd->scale);
1751 w = (guint) ceil((double)w * imd->scale);
1752 h = (guint) ceil((double)h * imd->scale);
1757 if ((GdkInterpType)zoom_quality != GDK_INTERP_NEAREST)
1759 /* some scaling types use surrounding pixels to smooth the image,
1760 * this will expand the new area to cover up for faint black
1761 * lines caused by previous renders with non-complete image
1769 image_queue(imd, (gint) x, (gint) y, (gint) w, (gint) h, FALSE, TILE_RENDER_AREA, TRUE);
1772 static void image_load_done_cb(ImageLoader *il, gpointer data)
1774 ImageWindow *imd = data;
1776 if (debug) printf ("image done\n");
1778 if (imd->delay_flip &&
1779 imd->pixbuf != image_loader_get_pixbuf(imd->il))
1781 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1782 imd->pixbuf = image_loader_get_pixbuf(imd->il);
1783 if (imd->pixbuf) g_object_ref(imd->pixbuf);
1784 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1787 image_loader_free(imd->il);
1790 image_post_process(imd, TRUE);
1792 image_read_ahead_start(imd);
1795 static void image_load_error_cb(ImageLoader *il, gpointer data)
1797 if (debug) printf ("image error\n");
1799 /* even on error handle it like it was done,
1800 * since we have a pixbuf with _something_ */
1802 image_load_done_cb(il, data);
1805 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1806 static void image_load_buffer_throttle(ImageLoader *il)
1808 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
1810 /* Larger image files usually have larger chunks of data per pixel...
1811 * So increase the buffer read size so that the rendering chunks called
1815 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
1819 /* this read ahead is located here merely for the callbacks, above */
1821 static gint image_read_ahead_check(ImageWindow *imd)
1823 if (!imd->read_ahead_path) return FALSE;
1824 if (imd->il) return FALSE;
1826 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
1828 image_read_ahead_cancel(imd);
1832 if (imd->read_ahead_il)
1834 imd->il = imd->read_ahead_il;
1835 imd->read_ahead_il = NULL;
1837 /* override the old signals */
1838 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
1839 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
1840 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
1842 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1843 image_load_buffer_throttle(imd->il);
1846 /* do this one directly (probably should add a set func) */
1847 imd->il->func_done = image_load_done_cb;
1849 if (!imd->delay_flip)
1851 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1852 imd->pixbuf = image_loader_get_pixbuf(imd->il);
1853 if (imd->pixbuf) g_object_ref(imd->pixbuf);
1856 image_read_ahead_cancel(imd);
1859 else if (imd->read_ahead_pixbuf)
1861 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1862 imd->pixbuf = imd->read_ahead_pixbuf;
1863 imd->read_ahead_pixbuf = NULL;
1865 image_read_ahead_cancel(imd);
1867 image_post_process(imd, FALSE);
1871 image_read_ahead_cancel(imd);
1875 static gint image_load_begin(ImageWindow *imd, const gchar *path)
1877 if (debug) printf ("image begin \n");
1879 if (imd->il) return FALSE;
1881 imd->completed = FALSE;
1883 if (image_post_buffer_get(imd))
1885 if (debug) printf("from post buffer: %s\n", imd->image_path);
1887 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1891 if (image_read_ahead_check(imd))
1893 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
1895 if (!imd->delay_flip || !imd->il) image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
1899 if (!imd->delay_flip && imd->pixbuf)
1901 g_object_unref(imd->pixbuf);
1905 imd->il = image_loader_new(path);
1907 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
1908 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
1909 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
1911 if (!image_loader_start(imd->il, image_load_done_cb, imd))
1913 if (debug) printf("image start error\n");
1915 image_loader_free(imd->il);
1918 image_complete_util(imd, FALSE);
1923 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
1924 image_load_buffer_throttle(imd->il);
1927 if (!imd->delay_flip && !imd->pixbuf) image_load_pixbuf_ready(imd);
1932 static void image_reset(ImageWindow *imd)
1934 /* stops anything currently being done */
1936 if (debug) printf("image reset\n");
1938 image_loader_free(imd->il);
1941 image_queue_clear(imd);
1942 imd->delay_alter_type = ALTER_NONE;
1946 *-------------------------------------------------------------------
1948 *-------------------------------------------------------------------
1951 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
1955 imd->zoom = zoom; /* store the zoom, needed by the loader */
1959 if (imd->image_path && isfile(imd->image_path))
1961 if (image_load_begin(imd, imd->image_path))
1963 imd->unknown = FALSE;
1968 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1969 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1970 imd->unknown = TRUE;
1972 imd->size = filesize(imd->image_path);
1973 imd->mtime = filetime(imd->image_path);
1977 if (imd->pixbuf) g_object_unref(imd->pixbuf);
1980 if (imd->image_path)
1982 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
1983 imd->mtime = filetime(imd->image_path);
1990 imd->unknown = TRUE;
1996 image_pixbuf_sync(imd, zoom, FALSE, new);
2000 image_update_util(imd);
2004 static void image_change_real(ImageWindow *imd, const gchar *path,
2005 CollectionData *cd, CollectInfo *info, gdouble zoom)
2007 GdkPixbuf *prev_pixbuf = NULL;
2008 gchar *prev_path = NULL;
2009 gint prev_clear = FALSE;
2011 imd->collection = cd;
2012 imd->collection_info = info;
2014 if (enable_read_ahead && imd->image_path && imd->pixbuf)
2018 /* current image is not finished */
2023 prev_path = g_strdup(imd->image_path);
2024 prev_pixbuf = imd->pixbuf;
2025 g_object_ref(prev_pixbuf);
2029 g_free(imd->image_path);
2030 imd->image_path = g_strdup(path);
2031 imd->image_name = filename_from_path(imd->image_path);
2033 image_change_complete(imd, zoom, TRUE);
2037 image_post_buffer_set(imd, prev_path, prev_pixbuf);
2039 g_object_unref(prev_pixbuf);
2041 else if (prev_clear)
2043 image_post_buffer_set(imd, NULL, NULL);
2046 image_update_title(imd);
2047 image_new_util(imd);
2051 *-------------------------------------------------------------------
2053 *-------------------------------------------------------------------
2056 static gint image_expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2060 ImageWindow *imd = data;
2062 image_border_draw(imd, event->area.x, event->area.y,
2063 event->area.width, event->area.height);
2066 x = MAX(0, (gint)event->area.x - imd->x_offset + imd->x_scroll);
2067 y = MAX(0, (gint)event->area.y - imd->y_offset + imd->y_scroll);
2069 image_queue(imd, x, y,
2070 MIN((gint)event->area.width, imd->width - x),
2071 MIN((gint)event->area.height, imd->height - y),
2072 FALSE, TILE_RENDER_ALL, FALSE);
2077 static void image_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
2079 ImageWindow *imd = data;
2081 image_size_sync(imd, allocation->width, allocation->height);
2085 *-------------------------------------------------------------------
2087 *-------------------------------------------------------------------
2090 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
2094 widget = imd->widget;
2095 if (!widget->window) return;
2099 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
2100 area, widget, "image_window",
2101 widget->allocation.x, widget->allocation.y,
2102 widget->allocation.width - 1, widget->allocation.height - 1);
2106 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
2107 area, widget, "image_window",
2108 widget->allocation.x, widget->allocation.y,
2109 widget->allocation.width - 1, widget->allocation.height - 1);
2113 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
2115 ImageWindow *imd = data;
2117 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
2121 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2123 ImageWindow *imd = data;
2125 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2126 image_focus_paint(imd, TRUE, NULL);
2131 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
2133 ImageWindow *imd = data;
2135 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
2136 image_focus_paint(imd, FALSE, NULL);
2143 *-------------------------------------------------------------------
2145 *-------------------------------------------------------------------
2148 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
2152 work = imd->overlay_list;
2156 gint px, py, pw, ph;
2157 gint rx, ry, rw, rh;
2162 if (!od->visible) continue;
2164 pw = gdk_pixbuf_get_width(od->pixbuf);
2165 ph = gdk_pixbuf_get_height(od->pixbuf);
2171 if (px < 0) px = imd->window_width - pw + px;
2172 if (py < 0) py = imd->window_height - ph + py;
2175 if (util_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
2177 gdk_draw_pixbuf(imd->image->window,
2178 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
2182 (GdkRgbDither)dither_quality, rx, ry);
2187 static void image_overlay_queue_draw(ImageWindow *imd, OverlayData *od, gint hidden)
2192 w = gdk_pixbuf_get_width(od->pixbuf);
2193 h = gdk_pixbuf_get_height(od->pixbuf);
2199 if (x < 0) x = imd->window_width - w + x;
2200 if (y < 0) y = imd->window_height - h + y;
2203 image_queue(imd, imd->x_scroll - imd->x_offset + x,
2204 imd->y_scroll - imd->y_offset + y,
2206 FALSE, TILE_RENDER_ALL, FALSE);
2208 old_vis = od->visible;
2209 if (hidden) od->visible = FALSE;
2210 image_border_draw(imd, x, y, w, h);
2211 od->visible = old_vis;
2214 static void image_overlay_queue_all(ImageWindow *imd)
2218 work = imd->overlay_list;
2221 OverlayData *od = work->data;
2224 image_overlay_queue_draw(imd, od, FALSE);
2228 static OverlayData *image_overlay_find(ImageWindow *imd, gint id)
2232 work = imd->overlay_list;
2235 OverlayData *od = work->data;
2238 if (od->id == id) return od;
2244 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
2245 gint relative, gint always)
2250 if (!imd || !pixbuf) return -1;
2253 while (image_overlay_find(imd, id)) id++;
2255 od = g_new0(OverlayData, 1);
2257 od->pixbuf = pixbuf;
2258 g_object_ref(G_OBJECT(od->pixbuf));
2261 od->relative = relative;
2263 od->always = always;
2265 imd->overlay_list = g_list_append(imd->overlay_list, od);
2267 image_overlay_queue_draw(imd, od, FALSE);
2272 static void image_overlay_free(ImageWindow *imd, OverlayData *od)
2274 imd->overlay_list = g_list_remove(imd->overlay_list, od);
2276 if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
2280 static void image_overlay_list_clear(ImageWindow *imd)
2282 while (imd->overlay_list)
2286 od = imd->overlay_list->data;
2287 image_overlay_free(imd, od);
2291 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
2297 od = image_overlay_find(imd, id);
2302 image_overlay_queue_draw(imd, od, TRUE);
2304 g_object_ref(G_OBJECT(pixbuf));
2305 g_object_unref(G_OBJECT(od->pixbuf));
2306 od->pixbuf = pixbuf;
2311 image_overlay_queue_draw(imd, od, FALSE);
2315 image_overlay_queue_draw(imd, od, TRUE);
2316 image_overlay_free(imd, od);
2320 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
2324 if (!imd) return FALSE;
2326 od = image_overlay_find(imd, id);
2327 if (!od) return FALSE;
2329 if (pixbuf) *pixbuf = od->pixbuf;
2336 void image_overlay_remove(ImageWindow *imd, gint id)
2338 image_overlay_set(imd, id, NULL, 0, 0);
2342 *-------------------------------------------------------------------
2344 *-------------------------------------------------------------------
2347 #define SCROLLER_UPDATES_PER_SEC 30
2348 #define SCROLLER_DEAD_ZONE 6
2351 static gboolean image_scroller_update_cb(gpointer data)
2353 ImageWindow *imd = data;
2357 /* this was a simple scroll by difference between scroller and mouse position,
2358 * but all this math results in a smoother result and accounts for a dead zone.
2361 if (abs(imd->scroller_xpos - imd->scroller_x) < SCROLLER_DEAD_ZONE)
2367 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2368 x = (imd->scroller_xpos - imd->scroller_x) / 2 * SCROLLER_UPDATES_PER_SEC;
2369 x += (x > 0) ? -shift : shift;
2372 if (abs(imd->scroller_ypos - imd->scroller_y) < SCROLLER_DEAD_ZONE)
2378 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
2379 y = (imd->scroller_ypos - imd->scroller_y) / 2 * SCROLLER_UPDATES_PER_SEC;
2380 y += (y > 0) ? -shift : shift;
2383 if (abs(x) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2389 xinc = imd->scroller_xinc;
2393 if (xinc < 0) xinc = 0;
2394 if (x < xinc) xinc = x;
2395 if (x > xinc) xinc = MIN(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2399 if (xinc > 0) xinc = 0;
2400 if (x > xinc) xinc = x;
2401 if (x < xinc) xinc = MAX(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
2405 if (abs(y) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
2411 yinc = imd->scroller_yinc;
2415 if (yinc < 0) yinc = 0;
2416 if (y < yinc) yinc = y;
2417 if (y > yinc) yinc = MIN(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2421 if (yinc > 0) yinc = 0;
2422 if (y > yinc) yinc = y;
2423 if (y < yinc) yinc = MAX(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
2427 imd->scroller_xinc = xinc;
2428 imd->scroller_yinc = yinc;
2430 xinc = xinc / SCROLLER_UPDATES_PER_SEC;
2431 yinc = yinc / SCROLLER_UPDATES_PER_SEC;
2433 image_scroll(imd, xinc, yinc);
2438 static void image_scroller_timer_set(ImageWindow *imd, gint start)
2440 if (imd->scroller_id != -1)
2442 g_source_remove(imd->scroller_id);
2443 imd->scroller_id = -1;
2448 imd->scroller_id = g_timeout_add(1000 / SCROLLER_UPDATES_PER_SEC,
2449 image_scroller_update_cb, imd);
2453 static void image_scroller_start(ImageWindow *imd, gint x, gint y)
2455 if (imd->scroller_overlay == -1)
2460 pixbuf = pixbuf_inline(PIXBUF_INLINE_SCROLLER);
2461 w = gdk_pixbuf_get_width(pixbuf);
2462 h = gdk_pixbuf_get_height(pixbuf);
2464 imd->scroller_overlay = image_overlay_add(imd, pixbuf, x - w / 2, y - h / 2, FALSE, TRUE);
2467 imd->scroller_x = x;
2468 imd->scroller_y = y;
2469 imd->scroller_xpos = x;
2470 imd->scroller_ypos = y;
2472 image_scroller_timer_set(imd, TRUE);
2475 static void image_scroller_stop(ImageWindow *imd)
2477 if (imd->scroller_id == -1) return;
2479 image_overlay_remove(imd, imd->scroller_overlay);
2480 imd->scroller_overlay = -1;
2482 image_scroller_timer_set(imd, FALSE);
2486 *-------------------------------------------------------------------
2488 *-------------------------------------------------------------------
2491 static gint image_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2493 ImageWindow *imd = data;
2495 if (imd->scroller_id != -1)
2497 imd->scroller_xpos = bevent->x;
2498 imd->scroller_ypos = bevent->y;
2501 if (!imd->in_drag || !gdk_pointer_is_grabbed()) return FALSE;
2503 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
2509 widget_set_cursor (imd->image, GDK_FLEUR);
2513 image_scroll_real(imd, imd->drag_last_x - bevent->x, imd->drag_last_y - bevent->y);
2515 imd->drag_last_x = bevent->x;
2516 imd->drag_last_y = bevent->y;
2521 static gint image_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2523 ImageWindow *imd = data;
2525 if (imd->scroller_id != -1) return TRUE;
2527 switch (bevent->button)
2530 imd->in_drag = TRUE;
2531 imd->drag_last_x = bevent->x;
2532 imd->drag_last_y = bevent->y;
2533 imd->drag_moved = 0;
2534 gdk_pointer_grab(imd->image->window, FALSE,
2535 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2536 NULL, NULL, bevent->time);
2537 gtk_grab_add(imd->image);
2540 imd->drag_moved = 0;
2543 image_button_do(imd, bevent);
2549 gtk_widget_grab_focus(imd->widget);
2554 static gint image_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
2556 ImageWindow *imd = data;
2558 if (imd->scroller_id != -1)
2560 image_scroller_stop(imd);
2564 if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(imd->image))
2566 gtk_grab_remove(imd->image);
2567 gdk_pointer_ungrab(bevent->time);
2568 widget_set_cursor(imd->image, -1);
2571 if (bevent->button == 1 && (bevent->state & GDK_SHIFT_MASK))
2573 image_scroller_start(imd, bevent->x, bevent->y);
2575 else if (bevent->button == 1 || bevent->button == 2)
2577 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD) image_button_do(imd, bevent);
2580 imd->in_drag = FALSE;
2585 static gint image_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
2587 ImageWindow *imd = data;
2589 if (imd->scroller_id != -1)
2591 imd->scroller_xpos = imd->scroller_x;
2592 imd->scroller_ypos = imd->scroller_y;
2593 imd->scroller_xinc = 0;
2594 imd->scroller_yinc = 0;
2600 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
2602 ImageWindow *imd = data;
2604 if (imd->func_scroll &&
2605 event && event->type == GDK_SCROLL)
2607 imd->func_scroll(imd, event->direction, event->time,
2608 event->x, event->y, event->state, imd->data_scroll);
2615 static void image_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data)
2617 ImageWindow *imd = data;
2619 imd->drag_moved = IMAGE_DRAG_SCROLL_THRESHHOLD;
2623 *-------------------------------------------------------------------
2625 *-------------------------------------------------------------------
2629 *-------------------------------------------------------------------
2631 *-------------------------------------------------------------------
2634 void image_attach_window(ImageWindow *imd, GtkWidget *window,
2635 const gchar *title, const gchar *title_right, gint show_zoom)
2637 imd->top_window = window;
2639 imd->title = g_strdup(title);
2640 g_free(imd->title_right);
2641 imd->title_right = g_strdup(title_right);
2642 imd->title_show_zoom = show_zoom;
2644 image_update_title(imd);
2647 void image_set_update_func(ImageWindow *imd,
2648 void (*func)(ImageWindow *imd, gpointer data),
2651 imd->func_update = func;
2652 imd->data_update = data;
2655 void image_set_complete_func(ImageWindow *imd,
2656 void (*func)(ImageWindow *, gint preload, gpointer),
2659 imd->func_complete = func;
2660 imd->data_complete = data;
2663 void image_set_new_func(ImageWindow *imd,
2664 void (*func)(ImageWindow *, gpointer),
2667 imd->func_new = func;
2668 imd->data_new = data;
2672 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent)
2674 if (imd->func_button &&
2676 (bevent->type == GDK_BUTTON_PRESS || bevent->type == GDK_BUTTON_RELEASE))
2678 imd->func_button(imd, bevent->button, bevent->time,
2679 bevent->x, bevent->y, bevent->state, imd->data_button);
2683 void image_set_button_func(ImageWindow *imd,
2684 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
2687 imd->func_button = func;
2688 imd->data_button = data;
2691 void image_set_scroll_func(ImageWindow *imd,
2692 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
2695 imd->func_scroll = func;
2696 imd->data_scroll = data;
2701 const gchar *image_get_path(ImageWindow *imd)
2703 return imd->image_path;
2706 const gchar *image_get_name(ImageWindow *imd)
2708 return imd->image_name;
2711 /* merely changes path string, does not change the image! */
2712 void image_set_path(ImageWindow *imd, const gchar *newpath)
2714 g_free(imd->image_path);
2715 imd->image_path = g_strdup(newpath);
2716 imd->image_name = filename_from_path(imd->image_path);
2718 image_update_title(imd);
2719 image_new_util(imd);
2722 /* load a new image */
2724 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
2726 if (imd->image_path == path ||
2727 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
2729 image_change_real(imd, path, NULL, NULL, zoom);
2732 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
2734 image_set_pixbuf(imd, pixbuf, zoom, TRUE);
2735 image_new_util(imd);
2738 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
2740 if (!cd || !info || !g_list_find(cd->list, info)) return;
2742 image_change_real(imd, info->path, cd, info, zoom);
2745 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
2747 if (collection_to_number(imd->collection) >= 0)
2749 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
2751 if (info) *info = imd->collection_info;
2755 if (info) *info = NULL;
2757 return imd->collection;
2760 if (info) *info = NULL;
2764 static void image_loader_sync_data(ImageLoader *il, gpointer data)
2766 /* change data for the callbacks directly */
2768 il->data_area_ready = data;
2769 il->data_error = data;
2770 il->data_done = data;
2771 il->data_percent = data;
2774 /* this is more like a move function
2775 * it moves most data from source to imd, source does keep a ref on the pixbuf
2777 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
2779 if (imd == source) return;
2781 imd->unknown = source->unknown;
2783 image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
2785 imd->collection = source->collection;
2786 imd->collection_info = source->collection_info;
2787 imd->size = source->size;
2788 imd->mtime = source->mtime;
2790 image_set_path(imd, image_get_path(source));
2792 image_loader_free(imd->il);
2795 if (imd->pixbuf && source->il)
2797 imd->il = source->il;
2800 image_loader_sync_data(imd->il, imd);
2802 imd->delay_alter_type = source->delay_alter_type;
2803 source->delay_alter_type = ALTER_NONE;
2806 image_loader_free(imd->read_ahead_il);
2807 imd->read_ahead_il = source->read_ahead_il;
2808 source->read_ahead_il = NULL;
2809 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
2811 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
2812 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
2813 source->read_ahead_pixbuf = NULL;
2815 g_free(imd->read_ahead_path);
2816 imd->read_ahead_path = source->read_ahead_path;
2817 source->read_ahead_path = NULL;
2819 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
2820 imd->prev_pixbuf = source->prev_pixbuf;
2821 source->prev_pixbuf = NULL;
2823 g_free(imd->prev_path);
2824 imd->prev_path = source->prev_path;
2825 source->prev_path = NULL;
2827 imd->completed = source->completed;
2829 imd->x_scroll = source->x_scroll;
2830 imd->y_scroll = source->y_scroll;
2832 image_scroll_clamp(imd);
2837 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
2839 gint sx, sy, sw, sh;
2841 sx = (gint)floor((double)x * imd->scale);
2842 sy = (gint)floor((double)y * imd->scale);
2843 sw = (gint)ceil((double)width * imd->scale);
2844 sh = (gint)ceil((double)height * imd->scale);
2846 image_queue(imd, sx, sy, sw, sh, FALSE, TILE_RENDER_AREA, TRUE);
2849 void image_reload(ImageWindow *imd)
2851 image_change_complete(imd, imd->zoom, FALSE);
2854 void image_scroll(ImageWindow *imd, gint x, gint y)
2856 image_scroll_real(imd, x, y);
2859 void image_alter(ImageWindow *imd, AlterType type)
2863 /* still loading, wait till done */
2864 imd->delay_alter_type = type;
2868 image_alter_real(imd, type, TRUE);
2873 static void image_zoom_adjust_real(ImageWindow *imd, gdouble increment,
2874 gint center_point, gint x, gint y)
2876 gdouble zoom = imd->zoom;
2878 if (increment == 0.0) return; /* avoid possible div by zero, a no-op anyway... */
2882 if (imd->scale < 1.0)
2884 zoom = 0.0 - 1.0 / imd->scale;
2892 if (increment < 0.0)
2894 if (zoom >= 1.0 && zoom + increment < 1.0)
2896 zoom = zoom + increment - 2.0;
2900 zoom = zoom + increment;
2905 if (zoom <= -1.0 && zoom + increment > -1.0)
2907 zoom = zoom + increment + 2.0;
2911 zoom = zoom + increment;
2915 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, center_point, x, y);
2918 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
2920 image_zoom_adjust_real(imd, increment, FALSE, 0, 0);
2923 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
2925 image_zoom_adjust_real(imd, increment, TRUE, x, y);
2928 void image_zoom_set(ImageWindow *imd, gdouble zoom)
2930 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, FALSE, 0, 0);
2933 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
2937 if (!imd->pixbuf || imd->image_width < 1 || imd->image_height < 1) return;
2941 zoom = (gdouble)imd->window_height / imd->image_height;
2945 zoom = (gdouble)imd->window_width / imd->image_width;
2950 zoom = 0.0 - 1.0 / zoom;
2953 image_zoom_set(imd, zoom);
2956 gdouble image_zoom_get(ImageWindow *imd)
2961 gdouble image_zoom_get_real(ImageWindow *imd)
2966 gchar *image_zoom_get_as_text(ImageWindow *imd)
2972 gchar *approx = " ";
2974 if (imd->zoom > 0.0)
2978 else if (imd->zoom < 0.0)
2980 r = 0.0 - imd->zoom;
2982 else if (imd->zoom == 0.0 && imd->scale != 0.0)
2984 if (imd->scale >= 1.0)
2990 r = 1.0 / imd->scale;
2995 if (rint(l) != l) pl = 1;
2996 if (rint(r) != r) pr = 1;
2998 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
3001 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
3005 if (mode == ZOOM_RESET_ORIGINAL)
3009 else if (mode == ZOOM_RESET_FIT_WINDOW)
3017 zoom = image_zoom_get(imd);
3030 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
3034 image_read_ahead_set(imd, path);
3038 image_read_ahead_cancel(imd);
3042 static gint image_auto_refresh_cb(gpointer data)
3044 ImageWindow *imd = data;
3047 if (!imd || !imd->pixbuf ||
3048 imd->il || !imd->image_path ||
3049 !update_on_time_change) return TRUE;
3051 newtime = filetime(imd->image_path);
3052 if (newtime > 0 && newtime != imd->mtime)
3054 imd->mtime = newtime;
3061 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
3063 void image_auto_refresh(ImageWindow *imd, gint interval)
3067 if (imd->auto_refresh_id > -1)
3069 g_source_remove(imd->auto_refresh_id);
3070 imd->auto_refresh_id = -1;
3071 imd->auto_refresh_interval = -1;
3074 if (interval < 0) return;
3076 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
3078 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
3079 imd->auto_refresh_interval = interval;
3082 /* allow top window to be resized ? */
3084 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
3086 imd->top_window_sync = allow_sync;
3089 /* background colors */
3091 void image_background_set_black(ImageWindow *imd, gint black)
3097 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3098 g_object_ref(G_OBJECT(style));
3102 style->bg[GTK_STATE_NORMAL] = style->black;
3105 gtk_widget_set_style(imd->image, style);
3106 g_object_unref(G_OBJECT(style));
3108 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3111 void image_background_set_color(ImageWindow *imd, GdkColor *color)
3117 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
3118 g_object_ref(G_OBJECT(style));
3124 slot = &style->bg[GTK_STATE_NORMAL];
3126 slot->red = color->red;
3127 slot->green = color->green;
3128 slot->blue = color->blue;
3131 gtk_widget_set_style(imd->image, style);
3132 g_object_unref(G_OBJECT(style));
3134 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
3137 void image_set_delay_flip(ImageWindow *imd, gint delay)
3140 imd->delay_flip == delay) return;
3142 imd->delay_flip = delay;
3143 if (!imd->delay_flip && imd->il)
3145 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3147 image_load_pixbuf_ready(imd);
3149 image_queue_clear(imd);
3150 image_queue(imd, 0, 0, imd->width, imd->height, FALSE, TILE_RENDER_AREA, TRUE);
3154 /* wallpaper util */
3156 void image_to_root_window(ImageWindow *imd, gint scaled)
3159 GdkWindow *rootwindow;
3163 if (!imd || !imd->pixbuf) return;
3166 screen = gtk_widget_get_screen(imd->image);
3167 rootwindow = gdk_screen_get_root_window(screen);
3168 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
3172 pb = gdk_pixbuf_scale_simple(imd->pixbuf, gdk_screen_width(), gdk_screen_height(), (GdkInterpType)zoom_quality);
3176 pb = gdk_pixbuf_scale_simple(imd->pixbuf, imd->width, imd->height, (GdkInterpType)zoom_quality);
3179 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
3180 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
3181 gdk_window_clear(rootwindow);
3183 g_object_unref(pixmap);
3190 *-------------------------------------------------------------------
3192 *-------------------------------------------------------------------
3195 static void image_free(ImageWindow *imd)
3197 image_read_ahead_cancel(imd);
3198 image_post_buffer_set(imd, NULL, NULL);
3199 image_auto_refresh(imd, -1);
3201 g_free(imd->image_path);
3203 g_free(imd->title_right);
3206 image_tile_sync_count(imd, 0);
3207 if (imd->pixbuf) g_object_unref(imd->pixbuf);
3209 image_scroller_timer_set(imd, FALSE);
3211 image_overlay_list_clear(imd);
3216 static void image_destroy_cb(GtkObject *widget, gpointer data)
3218 ImageWindow *imd = data;
3222 ImageWindow *image_new(gint frame)
3226 imd = g_new0(ImageWindow, 1);
3230 imd->draw_idle_id = -1;
3232 imd->tile_width = IMAGE_TILE_SIZE;
3233 imd->tile_height = IMAGE_TILE_SIZE;
3235 imd->top_window = NULL;
3237 imd->title_right = NULL;
3238 imd->title_show_zoom = FALSE;
3240 imd->unknown = TRUE;
3244 imd->has_frame = frame;
3245 imd->top_window_sync = FALSE;
3247 imd->tile_cache = NULL;
3248 imd->tile_cache_size = 0;
3250 imd->delay_alter_type = ALTER_NONE;
3252 imd->read_ahead_il = NULL;
3253 imd->read_ahead_pixbuf = NULL;
3254 imd->read_ahead_path = NULL;
3256 imd->completed = FALSE;
3258 imd->auto_refresh_id = -1;
3259 imd->auto_refresh_interval = -1;
3261 imd->delay_flip = FALSE;
3263 imd->func_update = NULL;
3264 imd->func_complete = NULL;
3266 imd->func_button = NULL;
3267 imd->func_scroll = NULL;
3269 imd->scroller_id = -1;
3270 imd->scroller_overlay = -1;
3272 imd->image = gtk_drawing_area_new();
3273 gtk_widget_set_double_buffered(imd->image, FALSE);
3277 imd->widget = gtk_frame_new(NULL);
3278 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
3279 gtk_container_add(GTK_CONTAINER(imd->widget), imd->image);
3280 gtk_widget_show(imd->image);
3282 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
3283 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
3284 G_CALLBACK(image_focus_in_cb), imd);
3285 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
3286 G_CALLBACK(image_focus_out_cb), imd);
3288 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
3289 G_CALLBACK(image_focus_expose), imd);
3293 imd->widget = imd->image;
3296 g_signal_connect(G_OBJECT(imd->image), "motion_notify_event",
3297 G_CALLBACK(image_mouse_motion_cb), imd);
3298 g_signal_connect(G_OBJECT(imd->image), "button_press_event",
3299 G_CALLBACK(image_mouse_press_cb), imd);
3300 g_signal_connect(G_OBJECT(imd->image), "button_release_event",
3301 G_CALLBACK(image_mouse_release_cb), imd);
3302 g_signal_connect(G_OBJECT(imd->image), "leave_notify_event",
3303 G_CALLBACK(image_mouse_leave_cb), imd);
3304 gtk_widget_set_events(imd->image, GDK_POINTER_MOTION_MASK |
3305 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
3306 GDK_LEAVE_NOTIFY_MASK);
3308 g_signal_connect(G_OBJECT(imd->image), "expose_event",
3309 G_CALLBACK(image_expose_cb), imd);
3310 g_signal_connect_after(G_OBJECT(imd->image), "size_allocate",
3311 G_CALLBACK(image_size_cb), imd);
3313 g_signal_connect(G_OBJECT(imd->image), "drag_begin",
3314 G_CALLBACK(image_mouse_drag_cb), imd);
3315 g_signal_connect(G_OBJECT(imd->image), "scroll_event",
3316 G_CALLBACK(image_scroll_cb), imd);
3318 g_signal_connect(G_OBJECT(imd->widget), "destroy",
3319 G_CALLBACK(image_destroy_cb), imd);