4 * Copyright (C) 2008 - 2010 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
19 #include "pixbuf-renderer.h"
20 #include "renderer-tiles.h"
28 /* comment this out if not using this from within Geeqie
29 * defining GQ_BUILD does these things:
30 * - Sets the shift-click scroller pixbuf to a nice icon instead of a black box
36 #include "pixbuf_util.h"
40 EXIF_ORIENTATION_UNKNOWN = 0,
41 EXIF_ORIENTATION_TOP_LEFT = 1,
42 EXIF_ORIENTATION_TOP_RIGHT = 2,
43 EXIF_ORIENTATION_BOTTOM_RIGHT = 3,
44 EXIF_ORIENTATION_BOTTOM_LEFT = 4,
45 EXIF_ORIENTATION_LEFT_TOP = 5,
46 EXIF_ORIENTATION_RIGHT_TOP = 6,
47 EXIF_ORIENTATION_RIGHT_BOTTOM = 7,
48 EXIF_ORIENTATION_LEFT_BOTTOM = 8
49 } ExifOrientationType;
53 /* size to use when breaking up image pane for rendering */
54 #define PR_TILE_SIZE 128
56 typedef struct _ImageTile ImageTile;
57 typedef struct _QueueData QueueData;
61 GdkPixmap *pixmap; /* off screen buffer */
62 GdkPixbuf *pixbuf; /* pixbuf area for zooming */
63 gint x; /* x offset into image */
64 gint y; /* y offset into image */
65 gint w; /* width that is visible (may be less if at edge of image) */
66 gint h; /* height '' */
70 /* render_todo: (explanation)
72 AREA render area of tile, usually only used when loading an image
73 note: will jump to an ALL if render_done is not ALL.
74 ALL render entire tile, if never done before w/ ALL, for expose events *only*
77 ImageRenderType render_todo; /* what to do (see above) */
78 ImageRenderType render_done; /* highest that has been done before on tile */
83 guint size; /* est. memory used by pixmap and pixbuf */
96 typedef struct _OverlayData OverlayData;
107 OverlayRendererFlags flags;
110 typedef struct _RendererTiles RendererTiles;
112 struct _RendererTiles
117 gint tile_cache_max; /* max mb to use for offscreen buffer */
121 gint tile_cols; /* count of tile columns */
122 GList *tiles; /* list of buffer tiles */
123 gint tile_cache_size; /* allocated size of pixmaps/pixbufs */
124 GList *draw_queue; /* list of areas to redraw */
125 GList *draw_queue_2pass;/* list when 2 pass is enabled */
128 GdkPixmap *overlay_buffer;
130 guint draw_idle_id; /* event source id */
132 GdkPixbuf *spare_tile;
138 gint x_scroll; /* allow local adjustment and mirroring */
144 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h);
145 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, ImageTile *it);
148 static void rt_tile_free_all(RendererTiles *rt);
149 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h);
150 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it);
151 static void rt_queue_clear(RendererTiles *rt);
152 static void rt_queue_merge(QueueData *parent, QueueData *qd);
153 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
154 gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing);
156 static void rt_redraw(RendererTiles *rt, gboolean new_data);
159 static void rt_signals_connect(RendererTiles *rt);
160 static void rt_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data);
161 static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data);
162 static void pixbuf_renderer_paint(RendererTiles *rt, GdkRectangle *area);
163 static gint rt_queue_draw_idle_cb(gpointer data);
166 static void rt_sync_scroll(RendererTiles *rt)
168 PixbufRenderer *pr = rt->pr;
170 rt->x_scroll = (rt->stereo_mode & PR_STEREO_MIRROR) ?
171 pr->width - pr->vis_width - pr->x_scroll
174 rt->y_scroll = (rt->stereo_mode & PR_STEREO_FLIP) ?
175 pr->height - pr->vis_height - pr->y_scroll
180 *-------------------------------------------------------------------
182 *-------------------------------------------------------------------
185 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h)
187 PixbufRenderer *pr = rt->pr;
191 box = GTK_WIDGET(pr);
193 if (!box->window) return;
195 if (!pr->pixbuf && !pr->source_tiles_enabled)
197 if (pr_clip_region(x, y, w, h,
199 pr->viewport_width, pr->viewport_height,
202 gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
203 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
208 if (pr->vis_width < pr->viewport_width)
210 if (pr->x_offset > 0 &&
211 pr_clip_region(x, y, w, h,
213 pr->x_offset, pr->viewport_height,
216 gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
217 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
219 if (pr->viewport_width - pr->vis_width - pr->x_offset > 0 &&
220 pr_clip_region(x, y, w, h,
221 pr->x_offset + pr->vis_width, 0,
222 pr->viewport_width - pr->vis_width - pr->x_offset, pr->viewport_height,
225 gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
226 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
229 if (pr->vis_height < pr->viewport_height)
231 if (pr->y_offset > 0 &&
232 pr_clip_region(x, y, w, h,
234 pr->vis_width, pr->y_offset,
237 gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
238 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
240 if (pr->viewport_height - pr->vis_height - pr->y_offset > 0 &&
241 pr_clip_region(x, y, w, h,
242 pr->x_offset, pr->y_offset + pr->vis_height,
243 pr->vis_width, pr->viewport_height - pr->vis_height - pr->y_offset,
246 gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
247 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
252 static void rt_border_clear(RendererTiles *rt)
254 PixbufRenderer *pr = rt->pr;
255 rt_border_draw(rt, 0, 0, pr->viewport_width, pr->viewport_height);
260 *-------------------------------------------------------------------
262 *-------------------------------------------------------------------
265 static ImageTile *rt_tile_new(gint x, gint y, gint width, gint height)
269 it = g_new0(ImageTile, 1);
276 it->render_done = TILE_RENDER_NONE;
281 static void rt_tile_free(ImageTile *it)
285 if (it->pixbuf) g_object_unref(it->pixbuf);
286 if (it->pixmap) g_object_unref(it->pixmap);
291 static void rt_tile_free_all(RendererTiles *rt)
306 g_list_free(rt->tiles);
308 rt->tile_cache_size = 0;
311 static ImageTile *rt_tile_add(RendererTiles *rt, gint x, gint y)
313 PixbufRenderer *pr = rt->pr;
316 it = rt_tile_new(x, y, rt->tile_width, rt->tile_height);
318 if (it->x + it->w > pr->width) it->w = pr->width - it->x;
319 if (it->y + it->h > pr->height) it->h = pr->height - it->y;
321 rt->tiles = g_list_prepend(rt->tiles, it);
322 rt->tile_cache_size += it->size;
327 static void rt_tile_remove(RendererTiles *rt, ImageTile *it)
331 QueueData *qd = it->qd;
334 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
340 QueueData *qd = it->qd2;
343 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
347 rt->tiles = g_list_remove(rt->tiles, it);
348 rt->tile_cache_size -= it->size;
353 static void rt_tile_free_space(RendererTiles *rt, guint space, ImageTile *it)
355 PixbufRenderer *pr = rt->pr;
359 work = g_list_last(rt->tiles);
361 if (pr->source_tiles_enabled && pr->scale < 1.0)
365 tiles = (pr->vis_width / rt->tile_width + 1) * (pr->vis_height / rt->tile_height + 1);
366 tile_max = MAX(tiles * rt->tile_width * rt->tile_height * 3,
367 (gint)((gdouble)rt->tile_cache_max * 1048576.0 * pr->scale));
371 tile_max = rt->tile_cache_max * 1048576;
374 while (work && rt->tile_cache_size + space > tile_max)
381 ((!needle->qd && !needle->qd2) || !rt_tile_is_visible(rt, needle))) rt_tile_remove(rt, needle);
385 static void rt_tile_invalidate_all(RendererTiles *rt)
387 PixbufRenderer *pr = rt->pr;
398 it->render_done = TILE_RENDER_NONE;
399 it->render_todo = TILE_RENDER_ALL;
402 it->w = MIN(rt->tile_width, pr->width - it->x);
403 it->h = MIN(rt->tile_height, pr->height - it->y);
407 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h)
413 x1 = ROUND_DOWN(x, rt->tile_width);
414 x2 = ROUND_UP(x + w, rt->tile_width);
416 y1 = ROUND_DOWN(y, rt->tile_height);
417 y2 = ROUND_UP(y + h, rt->tile_height);
427 if (it->x < x2 && it->x + it->w > x1 &&
428 it->y < y2 && it->y + it->h > y1)
430 it->render_done = TILE_RENDER_NONE;
431 it->render_todo = TILE_RENDER_ALL;
436 static ImageTile *rt_tile_get(RendererTiles *rt, gint x, gint y, gboolean only_existing)
446 if (it->x == x && it->y == y)
448 rt->tiles = g_list_delete_link(rt->tiles, work);
449 rt->tiles = g_list_prepend(rt->tiles, it);
456 if (only_existing) return NULL;
458 return rt_tile_add(rt, x, y);
461 static gint pixmap_calc_size(GdkPixmap *pixmap)
465 d = gdk_drawable_get_depth(pixmap);
466 gdk_drawable_get_size(pixmap, &w, &h);
467 return w * h * (d / 8);
470 static void rt_tile_prepare(RendererTiles *rt, ImageTile *it)
472 PixbufRenderer *pr = rt->pr;
478 pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1);
480 size = pixmap_calc_size(pixmap);
481 rt_tile_free_space(rt, size, it);
485 rt->tile_cache_size += size;
488 if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) ||
489 pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf)
494 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height);
497 size = gdk_pixbuf_get_rowstride(pixbuf) * rt->tile_height;
498 rt_tile_free_space(rt, size, it);
502 rt->tile_cache_size += size;
507 *-------------------------------------------------------------------
509 *-------------------------------------------------------------------
512 static void rt_overlay_get_position(RendererTiles *rt, OverlayData *od,
513 gint *x, gint *y, gint *w, gint *h)
515 PixbufRenderer *pr = rt->pr;
518 pw = gdk_pixbuf_get_width(od->pixbuf);
519 ph = gdk_pixbuf_get_height(od->pixbuf);
523 if (od->flags & OVL_RELATIVE)
525 if (px < 0) px = pr->viewport_width - pw + px;
526 if (py < 0) py = pr->viewport_height - ph + py;
535 static void rt_overlay_init_window(RendererTiles *rt, OverlayData *od)
537 PixbufRenderer *pr = rt->pr;
539 GdkWindowAttr attributes;
540 gint attributes_mask;
542 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
544 attributes.window_type = GDK_WINDOW_CHILD;
545 attributes.wclass = GDK_INPUT_OUTPUT;
546 attributes.width = pw;
547 attributes.height = ph;
548 attributes.event_mask = GDK_EXPOSURE_MASK;
551 od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask);
552 gdk_window_set_user_data(od->window, pr);
553 gdk_window_move(od->window, px + rt->stereo_off_x, py + rt->stereo_off_y);
554 gdk_window_show(od->window);
557 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h,
560 PixbufRenderer *pr = rt->pr;
564 box = GTK_WIDGET(pr);
566 work = rt->overlay_list;
576 if (!od->window) rt_overlay_init_window(rt, od);
578 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
579 if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
581 if (!rt->overlay_buffer)
583 rt->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1);
588 #if GTK_CHECK_VERSION(2,20,0)
589 gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)],
591 gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)],
594 rx - (pr->x_offset + (it->x - rt->x_scroll)),
595 ry - (pr->y_offset + (it->y - rt->y_scroll)),
597 gdk_draw_pixbuf(rt->overlay_buffer,
598 #if GTK_CHECK_VERSION(2,20,0)
599 box->style->fg_gc[gtk_widget_get_state(box)],
601 box->style->fg_gc[GTK_WIDGET_STATE(box)],
606 pr->dither_quality, rx, ry);
607 #if GTK_CHECK_VERSION(2,20,0)
608 gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)],
610 gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
614 rx - px, ry - py, rw, rh);
618 /* no ImageTile means region may be larger than our scratch buffer */
621 for (sx = rx; sx < rx + rw; sx += rt->tile_width)
622 for (sy = ry; sy < ry + rh; sy += rt->tile_height)
626 sw = MIN(rx + rw - sx, rt->tile_width);
627 sh = MIN(ry + rh - sy, rt->tile_height);
629 gdk_draw_rectangle(rt->overlay_buffer,
630 #if GTK_CHECK_VERSION(2,20,0)
631 box->style->bg_gc[gtk_widget_get_state(box)], TRUE,
633 box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE,
636 gdk_draw_pixbuf(rt->overlay_buffer,
637 #if GTK_CHECK_VERSION(2,20,0)
638 box->style->fg_gc[gtk_widget_get_state(box)],
640 box->style->fg_gc[GTK_WIDGET_STATE(box)],
645 pr->dither_quality, sx, sy);
646 #if GTK_CHECK_VERSION(2,20,0)
647 gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)],
649 gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
653 sx - px, sy - py, sw, sh);
660 static void rt_overlay_queue_draw(RendererTiles *rt, OverlayData *od, gint x1, gint y1, gint x2, gint y2)
662 PixbufRenderer *pr = rt->pr;
665 rt_overlay_get_position(rt, od, &x, &y, &w, &h);
673 rt_queue(rt, rt->x_scroll - pr->x_offset + x,
674 rt->y_scroll - pr->y_offset + y,
676 FALSE, TILE_RENDER_ALL, FALSE, FALSE);
678 rt_border_draw(rt, x, y, w, h);
681 static void rt_overlay_queue_all(RendererTiles *rt, gint x1, gint y1, gint x2, gint y2)
685 work = rt->overlay_list;
688 OverlayData *od = work->data;
691 rt_overlay_queue_draw(rt, od, x1, y1, x2, y2);
695 static void rt_overlay_update_sizes(RendererTiles *rt)
699 work = rt->overlay_list;
702 OverlayData *od = work->data;
705 if (!od->window) rt_overlay_init_window(rt, od);
707 if (od->flags & OVL_RELATIVE)
711 rt_overlay_get_position(rt, od, &x, &y, &w, &h);
712 gdk_window_move_resize(od->window, x + rt->stereo_off_x, y + rt->stereo_off_y, w, h);
717 static OverlayData *rt_overlay_find(RendererTiles *rt, gint id)
721 work = rt->overlay_list;
724 OverlayData *od = work->data;
727 if (od->id == id) return od;
734 gint renderer_tiles_overlay_add(RendererTiles *rt, GdkPixbuf *pixbuf, gint x, gint y,
735 OverlayRendererFlags flags)
737 PixbufRenderer *pr = rt->pr;
741 g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1);
742 g_return_val_if_fail(pixbuf != NULL, -1);
745 while (rt_overlay_find(rt, id)) id++;
747 od = g_new0(OverlayData, 1);
750 g_object_ref(G_OBJECT(od->pixbuf));
755 rt_overlay_init_window(rt, od);
757 rt->overlay_list = g_list_append(rt->overlay_list, od);
759 rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
764 static void rt_overlay_free(RendererTiles *rt, OverlayData *od)
766 rt->overlay_list = g_list_remove(rt->overlay_list, od);
768 if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
769 if (od->window) gdk_window_destroy(od->window);
772 if (!rt->overlay_list && rt->overlay_buffer)
774 g_object_unref(rt->overlay_buffer);
775 rt->overlay_buffer = NULL;
779 static void rt_overlay_list_clear(RendererTiles *rt)
781 while (rt->overlay_list)
785 od = rt->overlay_list->data;
786 rt_overlay_free(rt, od);
790 static void rt_overlay_list_reset_window(RendererTiles *rt)
794 if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
795 rt->overlay_buffer = NULL;
797 work = rt->overlay_list;
800 OverlayData *od = work->data;
802 if (od->window) gdk_window_destroy(od->window);
807 void renderer_tiles_overlay_set(RendererTiles *rt, gint id, GdkPixbuf *pixbuf, gint x, gint y)
809 PixbufRenderer *pr = rt->pr;
812 g_return_if_fail(IS_PIXBUF_RENDERER(pr));
814 od = rt_overlay_find(rt, id);
821 g_object_ref(G_OBJECT(pixbuf));
822 g_object_unref(G_OBJECT(od->pixbuf));
828 if (!od->window) rt_overlay_init_window(rt, od);
830 rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
831 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
832 gdk_window_move_resize(od->window, px + rt->stereo_off_x, py + rt->stereo_off_y, pw, ph);
836 rt_overlay_queue_draw(rt, od, 0, 0, 0, 0);
837 rt_overlay_free(rt, od);
841 gboolean renderer_tiles_overlay_get(RendererTiles *rt, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
843 PixbufRenderer *pr = rt->pr;
846 g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE);
848 od = rt_overlay_find(rt, id);
849 if (!od) return FALSE;
851 if (pixbuf) *pixbuf = od->pixbuf;
858 static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data)
860 RendererTiles *rt = data;
861 rt_overlay_list_reset_window(rt);
865 *-------------------------------------------------------------------
867 *-------------------------------------------------------------------
870 static GdkPixbuf *rt_get_spare_tile(RendererTiles *rt)
872 if (!rt->spare_tile) rt->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height);
873 return rt->spare_tile;
876 #define COLOR_BYTES 3 /* rgb */
878 static void rt_tile_rotate_90_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
880 GdkPixbuf *src = *tile;
883 guchar *s_pix, *d_pix;
885 guchar *ip, *spi, *dpi;
887 gint tw = rt->tile_width;
889 srs = gdk_pixbuf_get_rowstride(src);
890 s_pix = gdk_pixbuf_get_pixels(src);
891 spi = s_pix + (x * COLOR_BYTES);
893 dest = rt_get_spare_tile(rt);
894 drs = gdk_pixbuf_get_rowstride(dest);
895 d_pix = gdk_pixbuf_get_pixels(dest);
896 dpi = d_pix + (tw - 1) * COLOR_BYTES;
898 for (i = y; i < y + h; i++)
900 sp = spi + (i * srs);
901 ip = dpi - (i * COLOR_BYTES);
902 for (j = x; j < x + w; j++)
905 memcpy(dp, sp, COLOR_BYTES);
910 rt->spare_tile = src;
914 static void rt_tile_rotate_90_counter_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
916 GdkPixbuf *src = *tile;
919 guchar *s_pix, *d_pix;
921 guchar *ip, *spi, *dpi;
923 gint th = rt->tile_height;
925 srs = gdk_pixbuf_get_rowstride(src);
926 s_pix = gdk_pixbuf_get_pixels(src);
927 spi = s_pix + (x * COLOR_BYTES);
929 dest = rt_get_spare_tile(rt);
930 drs = gdk_pixbuf_get_rowstride(dest);
931 d_pix = gdk_pixbuf_get_pixels(dest);
932 dpi = d_pix + (th - 1) * drs;
934 for (i = y; i < y + h; i++)
936 sp = spi + (i * srs);
937 ip = dpi + (i * COLOR_BYTES);
938 for (j = x; j < x + w; j++)
941 memcpy(dp, sp, COLOR_BYTES);
946 rt->spare_tile = src;
950 static void rt_tile_mirror_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
952 GdkPixbuf *src = *tile;
955 guchar *s_pix, *d_pix;
960 gint tw = rt->tile_width;
962 srs = gdk_pixbuf_get_rowstride(src);
963 s_pix = gdk_pixbuf_get_pixels(src);
964 spi = s_pix + (x * COLOR_BYTES);
966 dest = rt_get_spare_tile(rt);
967 drs = gdk_pixbuf_get_rowstride(dest);
968 d_pix = gdk_pixbuf_get_pixels(dest);
969 dpi = d_pix + (tw - x - 1) * COLOR_BYTES;
971 for (i = y; i < y + h; i++)
973 sp = spi + (i * srs);
974 dp = dpi + (i * drs);
975 for (j = 0; j < w; j++)
977 memcpy(dp, sp, COLOR_BYTES);
983 rt->spare_tile = src;
987 static void rt_tile_mirror_and_flip(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
989 GdkPixbuf *src = *tile;
992 guchar *s_pix, *d_pix;
996 gint tw = rt->tile_width;
997 gint th = rt->tile_height;
999 srs = gdk_pixbuf_get_rowstride(src);
1000 s_pix = gdk_pixbuf_get_pixels(src);
1001 spi = s_pix + (x * COLOR_BYTES);
1003 dest = rt_get_spare_tile(rt);
1004 drs = gdk_pixbuf_get_rowstride(dest);
1005 d_pix = gdk_pixbuf_get_pixels(dest);
1006 dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES;
1008 for (i = y; i < y + h; i++)
1010 sp = s_pix + (i * srs) + (x * COLOR_BYTES);
1011 dp = dpi - (i * drs) - (x * COLOR_BYTES);
1012 for (j = 0; j < w; j++)
1014 memcpy(dp, sp, COLOR_BYTES);
1020 rt->spare_tile = src;
1024 static void rt_tile_flip_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
1026 GdkPixbuf *src = *tile;
1029 guchar *s_pix, *d_pix;
1033 gint th = rt->tile_height;
1035 srs = gdk_pixbuf_get_rowstride(src);
1036 s_pix = gdk_pixbuf_get_pixels(src);
1037 spi = s_pix + (x * COLOR_BYTES);
1039 dest = rt_get_spare_tile(rt);
1040 drs = gdk_pixbuf_get_rowstride(dest);
1041 d_pix = gdk_pixbuf_get_pixels(dest);
1042 dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES);
1044 for (i = y; i < y + h; i++)
1046 sp = spi + (i * srs);
1047 dp = dpi - (i * drs);
1048 memcpy(dp, sp, w * COLOR_BYTES);
1051 rt->spare_tile = src;
1055 static void rt_tile_apply_orientation(RendererTiles *rt, gint orientation, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h)
1057 switch (orientation)
1059 case EXIF_ORIENTATION_TOP_LEFT:
1060 /* normal -- nothing to do */
1062 case EXIF_ORIENTATION_TOP_RIGHT:
1065 rt_tile_mirror_only(rt, pixbuf, x, y, w, h);
1068 case EXIF_ORIENTATION_BOTTOM_RIGHT:
1071 rt_tile_mirror_and_flip(rt, pixbuf, x, y, w, h);
1074 case EXIF_ORIENTATION_BOTTOM_LEFT:
1077 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1080 case EXIF_ORIENTATION_LEFT_TOP:
1082 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1083 rt_tile_rotate_90_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1086 case EXIF_ORIENTATION_RIGHT_TOP:
1087 /* rotated -90 (270) */
1089 rt_tile_rotate_90_clockwise(rt, pixbuf, x, y, w, h);
1092 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1094 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1095 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1098 case EXIF_ORIENTATION_LEFT_BOTTOM:
1101 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, y, w, h);
1105 /* The other values are out of range */
1110 static gboolean rt_source_tile_render(RendererTiles *rt, ImageTile *it,
1111 gint x, gint y, gint w, gint h,
1112 gboolean new_data, gboolean fast)
1114 PixbufRenderer *pr = rt->pr;
1118 gboolean draw = FALSE;
1120 box = GTK_WIDGET(pr);
1122 if (pr->zoom == 1.0 || pr->scale == 1.0)
1124 list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE);
1129 gint rx, ry, rw, rh;
1134 if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height,
1135 it->x + x, it->y + y, w, h,
1136 &rx, &ry, &rw, &rh))
1140 gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1141 rx - st->x, ry - st->y, rw, rh);
1143 else /* (pr->zoom == 1.0 || pr->scale == 1.0) */
1145 gdk_draw_pixbuf(it->pixmap,
1146 #if GTK_CHECK_VERSION(2,20,0)
1147 box->style->fg_gc[gtk_widget_get_state(box)],
1149 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1152 rx - st->x, ry - st->y,
1153 rx - it->x, ry - it->y,
1155 pr->dither_quality, rx, ry);
1162 gdouble scale_x, scale_y;
1163 gint sx, sy, sw, sh;
1165 if (pr->image_width == 0 || pr->image_height == 0) return FALSE;
1166 scale_x = (gdouble)pr->width / pr->image_width;
1167 scale_y = (gdouble)pr->height / pr->image_height;
1169 sx = (gdouble)(it->x + x) / scale_x;
1170 sy = (gdouble)(it->y + y) / scale_y;
1171 sw = (gdouble)w / scale_x;
1172 sh = (gdouble)h / scale_y;
1174 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1177 /* draws red over draw region, to check for leaks (regions not filled) */
1178 pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255);
1181 list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE);
1186 gint rx, ry, rw, rh;
1187 gint stx, sty, stw, sth;
1192 stx = floor((gdouble)st->x * scale_x);
1193 sty = floor((gdouble)st->y * scale_y);
1194 stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx;
1195 sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty;
1197 if (pr_clip_region(stx, sty, stw, sth,
1198 it->x + x, it->y + y, w, h,
1199 &rx, &ry, &rw, &rh))
1203 gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1204 rx - st->x, ry - st->y, rw, rh);
1211 /* may need to use unfloored stx,sty values here */
1212 offset_x = (gdouble)(stx - it->x);
1213 offset_y = (gdouble)(sty - it->y);
1215 gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh,
1216 (gdouble) 0.0 + offset_x,
1217 (gdouble) 0.0 + offset_y,
1219 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality);
1231 static void rt_tile_get_region(gboolean has_alpha,
1232 const GdkPixbuf *src, GdkPixbuf *dest,
1233 int pb_x, int pb_y, int pb_w, int pb_h,
1234 double offset_x, double offset_y, double scale_x, double scale_y,
1235 GdkInterpType interp_type,
1236 int check_x, int check_y)
1240 if (scale_x == 1.0 && scale_y == 1.0)
1242 gdk_pixbuf_copy_area(src,
1243 -offset_x + pb_x, -offset_y + pb_y,
1250 gdk_pixbuf_scale(src, dest,
1251 pb_x, pb_y, pb_w, pb_h,
1260 gdk_pixbuf_composite_color(src, dest,
1261 pb_x, pb_y, pb_w, pb_h,
1266 255, check_x, check_y,
1267 PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
1274 static void rt_tile_render(RendererTiles *rt, ImageTile *it,
1275 gint x, gint y, gint w, gint h,
1276 gboolean new_data, gboolean fast)
1278 PixbufRenderer *pr = rt->pr;
1281 gboolean draw = FALSE;
1282 gint stereo_right_pixbuf_off;
1283 gint orientation = pr->orientation;
1284 static const gint mirror[] = {1, 2, 1, 4, 3, 6, 5, 8, 7};
1285 static const gint flip[] = {1, 4, 3, 2, 1, 8, 7, 6, 5};
1287 if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return;
1289 if (it->render_done != TILE_RENDER_ALL)
1295 if (!fast) it->render_done = TILE_RENDER_ALL;
1297 else if (it->render_todo != TILE_RENDER_AREA)
1299 if (!fast) it->render_todo = TILE_RENDER_NONE;
1303 if (!fast) it->render_todo = TILE_RENDER_NONE;
1305 if (new_data) it->blank = FALSE;
1307 rt_tile_prepare(rt, it);
1308 has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf));
1310 stereo_right_pixbuf_off = (rt->stereo_mode & PR_STEREO_RIGHT) ? pr->stereo_pixbuf_off : 0;
1311 if (rt->stereo_mode & PR_STEREO_MIRROR) orientation = mirror[orientation];
1312 if (rt->stereo_mode & PR_STEREO_FLIP) orientation = flip[orientation];
1313 box = GTK_WIDGET(pr);
1315 /* FIXME checker colors for alpha should be configurable,
1316 * also should be drawn for blank = TRUE
1321 /* no data, do fast rect fill */
1322 gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE,
1323 0, 0, it->w, it->h);
1325 else if (pr->source_tiles_enabled)
1327 draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast);
1329 else if ((pr->zoom == 1.0 || pr->scale == 1.0) &&
1330 pr->aspect_ratio == 1.0 &&
1332 orientation == EXIF_ORIENTATION_TOP_LEFT &&
1333 !(pr->func_post_process && !(pr->post_process_slow && fast)) &&
1334 !(rt->stereo_mode & PR_STEREO_ANAGLYPH))
1336 /* special case: faster, simple, scale 1.0, base orientation, no postprocessing */
1337 gdk_draw_pixbuf(it->pixmap,
1338 #if GTK_CHECK_VERSION(2,20,0)
1339 box->style->fg_gc[gtk_widget_get_state(box)],
1341 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1344 it->x + x + stereo_right_pixbuf_off, it->y + y,
1347 pr->dither_quality, it->x + x, it->y + y);
1351 gdouble scale_x, scale_y;
1352 gdouble src_x, src_y;
1356 if (pr->image_width == 0 || pr->image_height == 0) return;
1358 scale_x = (gdouble)pr->width / pr->image_width;
1359 scale_y = (gdouble)pr->height / pr->image_height;
1361 pr_tile_coords_map_orientation(orientation, it->x, it->y,
1362 pr->width, pr->height,
1363 rt->tile_width, rt->tile_height,
1365 pr_tile_region_map_orientation(orientation, x, y,
1366 rt->tile_width, rt->tile_height,
1370 //printf("%d %d\n", GET_X_SCROLL(rt), src_x);
1371 switch (orientation)
1374 case EXIF_ORIENTATION_LEFT_TOP:
1375 case EXIF_ORIENTATION_RIGHT_TOP:
1376 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1377 case EXIF_ORIENTATION_LEFT_BOTTOM:
1387 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
1388 * small sizes for anything but GDK_INTERP_NEAREST
1390 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1392 rt_tile_get_region(has_alpha,
1393 pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1394 (gdouble) 0.0 - src_x - stereo_right_pixbuf_off * scale_x,
1395 (gdouble) 0.0 - src_y,
1397 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1398 it->x + pb_x, it->y + pb_y);
1399 if (rt->stereo_mode & PR_STEREO_ANAGLYPH && pr->stereo_pixbuf_off > 0)
1401 GdkPixbuf *right_pb = rt_get_spare_tile(rt);
1402 rt_tile_get_region(has_alpha,
1403 pr->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h,
1404 (gdouble) 0.0 - src_x - pr->stereo_pixbuf_off * scale_x,
1405 (gdouble) 0.0 - src_y,
1407 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1408 it->x + pb_x, it->y + pb_y);
1409 pr_create_anaglyph(it->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h);
1410 /* do not care about freeing spare_tile, it will be reused */
1412 rt_tile_apply_orientation(rt, orientation, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1416 if (draw && it->pixbuf && !it->blank)
1419 if (pr->func_post_process && !(pr->post_process_slow && fast))
1420 pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data);
1422 gdk_draw_pixbuf(it->pixmap,
1423 #if GTK_CHECK_VERSION(2,20,0)
1424 box->style->fg_gc[gtk_widget_get_state(box)],
1426 box->style->fg_gc[GTK_WIDGET_STATE(box)],
1432 pr->dither_quality, it->x + x, it->y + y);
1436 /* enable this line for debugging the edges of tiles */
1437 gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1438 FALSE, 0, 0, it->w, it->h);
1439 gdk_draw_rectangle(it->pixmap, box->style->white_gc,
1445 static void rt_tile_expose(RendererTiles *rt, ImageTile *it,
1446 gint x, gint y, gint w, gint h,
1447 gboolean new_data, gboolean fast)
1449 PixbufRenderer *pr = rt->pr;
1452 /* clamp to visible */
1453 if (it->x + x < rt->x_scroll)
1455 w -= rt->x_scroll - it->x - x;
1456 x = rt->x_scroll - it->x;
1458 if (it->x + x + w > rt->x_scroll + pr->vis_width)
1460 w = rt->x_scroll + pr->vis_width - it->x - x;
1463 if (it->y + y < rt->y_scroll)
1465 h -= rt->y_scroll - it->y - y;
1466 y = rt->y_scroll - it->y;
1468 if (it->y + y + h > rt->y_scroll + pr->vis_height)
1470 h = rt->y_scroll + pr->vis_height - it->y - y;
1474 rt_tile_render(rt, it, x, y, w, h, new_data, fast);
1476 box = GTK_WIDGET(pr);
1478 #if GTK_CHECK_VERSION(2,20,0)
1479 gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)],
1481 gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)],
1484 pr->x_offset + (it->x - rt->x_scroll) + x + rt->stereo_off_x, pr->y_offset + (it->y - rt->y_scroll) + y + rt->stereo_off_y, w, h);
1486 if (rt->overlay_list)
1488 rt_overlay_draw(rt, pr->x_offset + (it->x - rt->x_scroll) + x,
1489 pr->y_offset + (it->y - rt->y_scroll) + y,
1496 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it)
1498 PixbufRenderer *pr = rt->pr;
1499 return (it->x + it->w >= rt->x_scroll && it->x < rt->x_scroll + pr->vis_width &&
1500 it->y + it->h >= rt->y_scroll && it->y < rt->y_scroll + pr->vis_height);
1504 *-------------------------------------------------------------------
1506 *-------------------------------------------------------------------
1509 static gint rt_get_queued_area(GList *work)
1515 QueueData *qd = work->data;
1516 area += qd->w * qd->h;
1523 static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set)
1525 PixbufRenderer *pr = rt->pr;
1527 gint visible_area = pr->vis_width * pr->vis_height;
1532 DEBUG_2("redraw priority: 2pass");
1533 rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL);
1537 if (visible_area == 0)
1544 percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area;
1549 /* we have enough data for starting intensive redrawing */
1550 DEBUG_2("redraw priority: high %.2f %%", percent);
1551 rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL);
1555 if (percent < 1.0 || force_set)
1557 /* queue is (almost) empty, wait 50 ms*/
1558 DEBUG_2("redraw priority: wait %.2f %%", percent);
1559 rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL);
1563 /* keep the same priority as before */
1564 DEBUG_2("redraw priority: no change %.2f %%", percent);
1569 static gboolean rt_queue_draw_idle_cb(gpointer data)
1571 RendererTiles *rt = data;
1572 PixbufRenderer *pr = rt->pr;
1577 if ((!pr->pixbuf && !pr->source_tiles_enabled) ||
1578 (!rt->draw_queue && !rt->draw_queue_2pass) ||
1581 pr_render_complete_signal(pr);
1583 rt->draw_idle_id = 0;
1589 qd = rt->draw_queue->data;
1590 fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow));
1596 /* still loading, wait till done (also drops the higher priority) */
1598 return rt_queue_schedule_next_draw(rt, FALSE);
1601 qd = rt->draw_queue_2pass->data;
1605 #if GTK_CHECK_VERSION(2,20,0)
1606 if (gtk_widget_get_realized(pr))
1608 if (GTK_WIDGET_REALIZED(pr))
1611 if (rt_tile_is_visible(rt, qd->it))
1613 rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1615 else if (qd->new_data)
1617 /* if new pixel data, and we already have a pixmap, update the tile */
1618 qd->it->blank = FALSE;
1619 if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
1621 rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1629 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
1634 rt_queue_merge(qd->it->qd2, qd);
1640 rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd);
1651 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
1655 if (!rt->draw_queue && !rt->draw_queue_2pass)
1657 pr_render_complete_signal(pr);
1659 rt->draw_idle_id = 0;
1663 return rt_queue_schedule_next_draw(rt, FALSE);
1666 static void rt_queue_list_free(GList *list)
1686 static void rt_queue_clear(RendererTiles *rt)
1688 rt_queue_list_free(rt->draw_queue);
1689 rt->draw_queue = NULL;
1691 rt_queue_list_free(rt->draw_queue_2pass);
1692 rt->draw_queue_2pass = NULL;
1694 if (rt->draw_idle_id)
1696 g_source_remove(rt->draw_idle_id);
1697 rt->draw_idle_id = 0;
1702 static void rt_queue_merge(QueueData *parent, QueueData *qd)
1704 if (parent->x + parent->w < qd->x + qd->w)
1706 parent->w += (qd->x + qd->w) - (parent->x + parent->w);
1708 if (parent->x > qd->x)
1710 parent->w += parent->x - qd->x;
1714 if (parent->y + parent->h < qd->y + qd->h)
1716 parent->h += (qd->y + qd->h) - (parent->y + parent->h);
1718 if (parent->y > qd->y)
1720 parent->h += parent->y - qd->y;
1724 parent->new_data |= qd->new_data;
1727 static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h)
1729 PixbufRenderer *pr = rt->pr;
1736 vh = pr->vis_height;
1741 if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1744 nx = CLAMP(*x, vx, vx + vw);
1745 nw = CLAMP(*w - (nx - *x), 1, vw);
1747 ny = CLAMP(*y, vy, vy + vh);
1748 nh = CLAMP(*h - (ny - *y), 1, vh);
1758 static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h,
1759 gboolean clamp, ImageRenderType render,
1760 gboolean new_data, gboolean only_existing)
1762 PixbufRenderer *pr = rt->pr;
1767 if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE;
1769 x1 = ROUND_DOWN(x, rt->tile_width);
1770 x2 = ROUND_UP(x + w, rt->tile_width);
1772 y1 = ROUND_DOWN(y, rt->tile_height);
1773 y2 = ROUND_UP(y + h, rt->tile_height);
1775 for (j = y1; j <= y2; j += rt->tile_height)
1777 for (i = x1; i <= x2; i += rt->tile_width)
1781 it = rt_tile_get(rt, i, j,
1783 (i + rt->tile_width < rt->x_scroll ||
1784 i > rt->x_scroll + pr->vis_width ||
1785 j + rt->tile_height < rt->y_scroll ||
1786 j > rt->y_scroll + pr->vis_height)));
1791 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1792 (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1794 it->render_todo = render;
1797 qd = g_new(QueueData, 1);
1799 qd->new_data = new_data;
1809 qd->w = x + w - i - qd->x;
1810 if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x;
1820 qd->h = y + h - j - qd->y;
1821 if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y;
1823 if (qd->w < 1 || qd->h < 1)
1829 rt_queue_merge(it->qd, qd);
1835 rt->draw_queue = g_list_append(rt->draw_queue, qd);
1844 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
1845 gboolean clamp, ImageRenderType render,
1846 gboolean new_data, gboolean only_existing)
1848 PixbufRenderer *pr = rt->pr;
1853 nx = CLAMP(x, 0, pr->width - 1);
1854 ny = CLAMP(y, 0, pr->height - 1);
1857 w = CLAMP(w, 0, pr->width - nx);
1858 h = CLAMP(h, 0, pr->height - ny);
1859 if (w < 1 || h < 1) return;
1861 if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) &&
1862 ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id))
1864 if (rt->draw_idle_id)
1866 g_source_remove(rt->draw_idle_id);
1867 rt->draw_idle_id = 0;
1869 rt_queue_schedule_next_draw(rt, TRUE);
1873 static void rt_redraw(RendererTiles *rt, gboolean new_data)
1875 PixbufRenderer *pr = rt->pr;
1877 rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE);
1881 static void rt_scroll(RendererTiles *rt, gint x_off, gint y_off)
1883 PixbufRenderer *pr = rt->pr;
1886 if (rt->stereo_mode & PR_STEREO_MIRROR) x_off = -x_off;
1887 if (rt->stereo_mode & PR_STEREO_FLIP) y_off = -y_off;
1889 gint w = pr->vis_width - abs(x_off);
1890 gint h = pr->vis_height - abs(y_off);
1894 /* scrolled completely to new material */
1895 rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1928 box = GTK_WIDGET(pr);
1930 gc = gdk_gc_new(box->window);
1931 gdk_gc_set_exposures(gc, TRUE);
1932 gdk_draw_drawable(box->window, gc,
1934 x2 + pr->x_offset + rt->stereo_off_x, y2 + pr->y_offset + rt->stereo_off_y,
1935 x1 + pr->x_offset + rt->stereo_off_x, y1 + pr->y_offset + rt->stereo_off_y, w, h);
1938 rt_overlay_queue_all(rt, x2, y2, x1, y1);
1940 w = pr->vis_width - w;
1941 h = pr->vis_height - h;
1946 x_off > 0 ? rt->x_scroll + (pr->vis_width - w) : rt->x_scroll, rt->y_scroll,
1947 w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1951 /* FIXME, to optimize this, remove overlap */
1953 rt->x_scroll, y_off > 0 ? rt->y_scroll + (pr->vis_height - h) : rt->y_scroll,
1954 pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1957 /* process exposures here, "expose_event" seems to miss a few with obstructed windows */
1958 #if ! GTK_CHECK_VERSION(2,18,0)
1959 while ((event = gdk_event_get_graphics_expose(box->window)) != NULL)
1961 pixbuf_renderer_paint(pr, &event->expose.area);
1963 if (event->expose.count == 0)
1965 gdk_event_free(event);
1968 gdk_event_free(event);
1974 static void renderer_queue(void *renderer, gint x, gint y, gint w, gint h,
1975 gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing)
1977 RendererTiles *rt = (RendererTiles *)renderer;
1978 PixbufRenderer *pr = rt->pr;
1980 if (rt->stereo_mode & PR_STEREO_MIRROR) x = pr->width - w - x;
1981 if (rt->stereo_mode & PR_STEREO_FLIP) y = pr->height - h - y;
1983 rt_queue((RendererTiles *)renderer, x, y, w, h, clamp, render, new_data, only_existing);
1986 static void renderer_queue_clear(void *renderer)
1988 rt_queue_clear((RendererTiles *)renderer);
1991 static void renderer_border_draw(void *renderer, gint x, gint y, gint w, gint h)
1993 rt_border_draw((RendererTiles *)renderer, x, y, w, h);
1997 static void renderer_invalidate_all(void *renderer)
1999 rt_tile_invalidate_all((RendererTiles *)renderer);
2002 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
2004 rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h);
2007 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
2009 rt_overlay_draw((RendererTiles *)renderer, x, y, w, h, NULL);
2012 static void renderer_update_sizes(void *renderer)
2014 RendererTiles *rt = (RendererTiles *)renderer;
2016 rt->stereo_off_x = 0;
2017 rt->stereo_off_y = 0;
2019 if (rt->stereo_mode & PR_STEREO_RIGHT)
2021 if (rt->stereo_mode & PR_STEREO_HORIZ)
2023 rt->stereo_off_x = rt->pr->viewport_width;
2025 else if (rt->stereo_mode & PR_STEREO_VERT)
2027 rt->stereo_off_y = rt->pr->viewport_height;
2031 rt_overlay_update_sizes(rt);
2035 static void renderer_free(void *renderer)
2037 RendererTiles *rt = (RendererTiles *)renderer;
2039 rt_tile_free_all(rt);
2040 if (rt->spare_tile) g_object_unref(rt->spare_tile);
2041 if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
2042 rt_overlay_list_clear(rt);
2043 /* disconnect "hierarchy-changed" */
2044 g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA,
2049 RendererFuncs *renderer_tiles_new(PixbufRenderer *pr, gint stereo_mode)
2051 RendererTiles *rt = g_new0(RendererTiles, 1);
2055 rt->f.queue = renderer_queue;
2056 rt->f.queue_clear = renderer_queue_clear;
2057 rt->f.border_draw = renderer_border_draw;
2058 rt->f.free = renderer_free;
2059 rt->f.invalidate_all = renderer_invalidate_all;
2060 rt->f.invalidate_region = renderer_invalidate_region;
2061 rt->f.scroll = rt_scroll;
2062 rt->f.update_sizes = renderer_update_sizes;
2065 rt->f.overlay_add = renderer_tiles_overlay_add;
2066 rt->f.overlay_set = renderer_tiles_overlay_set;
2067 rt->f.overlay_get = renderer_tiles_overlay_get;
2068 rt->f.overlay_draw = renderer_overlay_draw;
2070 rt->tile_width = PR_TILE_SIZE;
2071 rt->tile_height = PR_TILE_SIZE;
2074 rt->tile_cache_size = 0;
2076 rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT;
2078 rt->draw_idle_id = 0;
2080 rt->stereo_mode = stereo_mode;
2081 rt->stereo_off_x = 0;
2082 rt->stereo_off_y = 0;
2084 g_signal_connect(G_OBJECT(pr), "hierarchy-changed",
2085 G_CALLBACK(rt_hierarchy_changed_cb), rt);
2087 return (RendererFuncs *) rt;
2091 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */