2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2021 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "pixbuf-renderer.h"
29 #include "renderer-tiles.h"
37 /* comment this out if not using this from within Geeqie
38 * defining GQ_BUILD does these things:
39 * - Sets the shift-click scroller pixbuf to a nice icon instead of a black box
45 #include "pixbuf_util.h"
49 EXIF_ORIENTATION_UNKNOWN = 0,
50 EXIF_ORIENTATION_TOP_LEFT = 1,
51 EXIF_ORIENTATION_TOP_RIGHT = 2,
52 EXIF_ORIENTATION_BOTTOM_RIGHT = 3,
53 EXIF_ORIENTATION_BOTTOM_LEFT = 4,
54 EXIF_ORIENTATION_LEFT_TOP = 5,
55 EXIF_ORIENTATION_RIGHT_TOP = 6,
56 EXIF_ORIENTATION_RIGHT_BOTTOM = 7,
57 EXIF_ORIENTATION_LEFT_BOTTOM = 8
58 } ExifOrientationType;
61 typedef struct _ImageTile ImageTile;
62 typedef struct _QueueData QueueData;
66 cairo_surface_t *surface; /* off screen buffer */
67 GdkPixbuf *pixbuf; /* pixbuf area for zooming */
68 gint x; /* x offset into image */
69 gint y; /* y offset into image */
70 gint w; /* width that is visible (may be less if at edge of image) */
71 gint h; /* height '' */
75 /* render_todo: (explanation)
77 AREA render area of tile, usually only used when loading an image
78 note: will jump to an ALL if render_done is not ALL.
79 ALL render entire tile, if never done before w/ ALL, for expose events *only*
82 ImageRenderType render_todo; /* what to do (see above) */
83 ImageRenderType render_done; /* highest that has been done before on tile */
88 guint size; /* est. memory used by pixmap and pixbuf */
101 typedef struct _OverlayData OverlayData;
112 OverlayRendererFlags flags;
115 typedef struct _RendererTiles RendererTiles;
117 struct _RendererTiles
122 gint tile_cache_max; /* max MiB to use for offscreen buffer */
126 gint tile_cols; /* count of tile columns */
127 GList *tiles; /* list of buffer tiles */
128 gint tile_cache_size; /* allocated size of pixmaps/pixbufs */
129 GList *draw_queue; /* list of areas to redraw */
130 GList *draw_queue_2pass;/* list when 2 pass is enabled */
133 cairo_surface_t *overlay_buffer;
134 cairo_surface_t *surface;
136 guint draw_idle_id; /* event source id */
138 GdkPixbuf *spare_tile;
144 gint x_scroll; /* allow local adjustment and mirroring */
152 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h);
153 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, ImageTile *it);
156 static void rt_tile_free_all(RendererTiles *rt);
157 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h);
158 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it);
159 static void rt_queue_clear(RendererTiles *rt);
160 static void rt_queue_merge(QueueData *parent, QueueData *qd);
161 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
162 gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing);
164 static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data);
165 static gint rt_queue_draw_idle_cb(gpointer data);
167 #define GET_RIGHT_PIXBUF_OFFSET(rt) \
168 (( (rt->stereo_mode & PR_STEREO_RIGHT) && !(rt->stereo_mode & PR_STEREO_SWAP)) || \
169 (!(rt->stereo_mode & PR_STEREO_RIGHT) && (rt->stereo_mode & PR_STEREO_SWAP)) ? \
170 rt->pr->stereo_pixbuf_offset_right : rt->pr->stereo_pixbuf_offset_left )
172 #define GET_LEFT_PIXBUF_OFFSET(rt) \
173 ((!(rt->stereo_mode & PR_STEREO_RIGHT) && !(rt->stereo_mode & PR_STEREO_SWAP)) || \
174 ( (rt->stereo_mode & PR_STEREO_RIGHT) && (rt->stereo_mode & PR_STEREO_SWAP)) ? \
175 rt->pr->stereo_pixbuf_offset_right : rt->pr->stereo_pixbuf_offset_left )
178 static void rt_sync_scroll(RendererTiles *rt)
180 PixbufRenderer *pr = rt->pr;
182 rt->x_scroll = (rt->stereo_mode & PR_STEREO_MIRROR) ?
183 pr->width - pr->vis_width - pr->x_scroll
186 rt->y_scroll = (rt->stereo_mode & PR_STEREO_FLIP) ?
187 pr->height - pr->vis_height - pr->y_scroll
192 *-------------------------------------------------------------------
194 *-------------------------------------------------------------------
197 static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h)
199 PixbufRenderer *pr = rt->pr;
205 box = GTK_WIDGET(pr);
206 window = gtk_widget_get_window(box);
210 cr = cairo_create(rt->surface);
212 if (!pr->pixbuf && !pr->source_tiles_enabled)
214 if (pr_clip_region(x, y, w, h,
216 pr->viewport_width, pr->viewport_height,
219 cairo_set_source_rgb(cr, (double)pr->color.red/65535, (double)pr->color.green/65535, (double)pr->color.blue/65535);
220 cairo_rectangle(cr, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
222 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
228 if (pr->vis_width < pr->viewport_width)
230 if (pr->x_offset > 0 &&
231 pr_clip_region(x, y, w, h,
233 pr->x_offset, pr->viewport_height,
236 cairo_set_source_rgb(cr, (double)pr->color.red/65535, (double)pr->color.green/65535, (double)pr->color.blue/65535);
237 cairo_rectangle(cr, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
239 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
241 if (pr->viewport_width - pr->vis_width - pr->x_offset > 0 &&
242 pr_clip_region(x, y, w, h,
243 pr->x_offset + pr->vis_width, 0,
244 pr->viewport_width - pr->vis_width - pr->x_offset, pr->viewport_height,
247 cairo_set_source_rgb(cr, (double)pr->color.red/65535, (double)pr->color.green/65535, (double)pr->color.blue/65535);
248 cairo_rectangle(cr, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
250 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
253 if (pr->vis_height < pr->viewport_height)
255 if (pr->y_offset > 0 &&
256 pr_clip_region(x, y, w, h,
258 pr->vis_width, pr->y_offset,
261 cairo_set_source_rgb(cr, (double)pr->color.red/65535, (double)pr->color.green/65535, (double)pr->color.blue/65535);
262 cairo_rectangle(cr, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
264 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
266 if (pr->viewport_height - pr->vis_height - pr->y_offset > 0 &&
267 pr_clip_region(x, y, w, h,
268 pr->x_offset, pr->y_offset + pr->vis_height,
269 pr->vis_width, pr->viewport_height - pr->vis_height - pr->y_offset,
272 cairo_set_source_rgb(cr, (double)pr->color.red/65535, (double)pr->color.green/65535, (double)pr->color.blue/65535);
273 cairo_rectangle(cr, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh);
275 rt_overlay_draw(rt, rx, ry, rw, rh, NULL);
281 static void rt_border_clear(RendererTiles *rt)
283 PixbufRenderer *pr = rt->pr;
284 rt_border_draw(rt, 0, 0, pr->viewport_width, pr->viewport_height);
289 *-------------------------------------------------------------------
291 *-------------------------------------------------------------------
294 static ImageTile *rt_tile_new(gint x, gint y, gint width, gint height)
298 it = g_new0(ImageTile, 1);
305 it->render_done = TILE_RENDER_NONE;
310 static void rt_tile_free(ImageTile *it)
314 if (it->pixbuf) g_object_unref(it->pixbuf);
315 if (it->surface) cairo_surface_destroy(it->surface);
320 static void rt_tile_free_all(RendererTiles *rt)
335 g_list_free(rt->tiles);
337 rt->tile_cache_size = 0;
340 static ImageTile *rt_tile_add(RendererTiles *rt, gint x, gint y)
342 PixbufRenderer *pr = rt->pr;
345 it = rt_tile_new(x, y, rt->tile_width, rt->tile_height);
347 if (it->x + it->w > pr->width) it->w = pr->width - it->x;
348 if (it->y + it->h > pr->height) it->h = pr->height - it->y;
350 rt->tiles = g_list_prepend(rt->tiles, it);
351 rt->tile_cache_size += it->size;
356 static void rt_tile_remove(RendererTiles *rt, ImageTile *it)
360 QueueData *qd = it->qd;
363 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
369 QueueData *qd = it->qd2;
372 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
376 rt->tiles = g_list_remove(rt->tiles, it);
377 rt->tile_cache_size -= it->size;
382 static void rt_tile_free_space(RendererTiles *rt, guint space, ImageTile *it)
384 PixbufRenderer *pr = rt->pr;
388 work = g_list_last(rt->tiles);
390 if (pr->source_tiles_enabled && pr->scale < 1.0)
394 tiles = (pr->vis_width / rt->tile_width + 1) * (pr->vis_height / rt->tile_height + 1);
395 tile_max = MAX(tiles * rt->tile_width * rt->tile_height * 3,
396 (gint)((gdouble)rt->tile_cache_max * 1048576.0 * pr->scale));
400 tile_max = rt->tile_cache_max * 1048576;
403 while (work && rt->tile_cache_size + space > tile_max)
410 ((!needle->qd && !needle->qd2) || !rt_tile_is_visible(rt, needle))) rt_tile_remove(rt, needle);
414 static void rt_tile_invalidate_all(RendererTiles *rt)
416 PixbufRenderer *pr = rt->pr;
427 it->render_done = TILE_RENDER_NONE;
428 it->render_todo = TILE_RENDER_ALL;
431 it->w = MIN(rt->tile_width, pr->width - it->x);
432 it->h = MIN(rt->tile_height, pr->height - it->y);
436 static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h)
442 x1 = ROUND_DOWN(x, rt->tile_width);
443 x2 = ROUND_UP(x + w, rt->tile_width);
445 y1 = ROUND_DOWN(y, rt->tile_height);
446 y2 = ROUND_UP(y + h, rt->tile_height);
456 if (it->x < x2 && it->x + it->w > x1 &&
457 it->y < y2 && it->y + it->h > y1)
459 it->render_done = TILE_RENDER_NONE;
460 it->render_todo = TILE_RENDER_ALL;
465 static ImageTile *rt_tile_get(RendererTiles *rt, gint x, gint y, gboolean only_existing)
475 if (it->x == x && it->y == y)
477 rt->tiles = g_list_delete_link(rt->tiles, work);
478 rt->tiles = g_list_prepend(rt->tiles, it);
485 if (only_existing) return NULL;
487 return rt_tile_add(rt, x, y);
490 static gint pixmap_calc_size(cairo_surface_t *UNUSED(surface))
494 // d = gdk_drawable_get_depth(pixmap);
495 // gdk_drawable_get_size(pixmap, &w, &h);
496 return options->image.tile_size * options->image.tile_size * 4 / 8;
499 static void rt_hidpi_aware_draw(
506 cairo_surface_t *surface;
507 surface = gdk_cairo_surface_create_from_pixbuf(pixbuf, rt->hidpi_scale, NULL);
508 cairo_set_source_surface(cr, surface, x, y);
510 cairo_surface_destroy(surface);
513 static void rt_tile_prepare(RendererTiles *rt, ImageTile *it)
515 PixbufRenderer *pr = rt->pr;
518 cairo_surface_t *surface;
521 surface = gdk_window_create_similar_surface(gtk_widget_get_window((GtkWidget *)pr),
523 rt->tile_width, rt->tile_height);
525 size = pixmap_calc_size(surface) * rt->hidpi_scale * rt->hidpi_scale;
526 rt_tile_free_space(rt, size, it);
528 it->surface = surface;
530 rt->tile_cache_size += size;
537 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->hidpi_scale * rt->tile_width, rt->hidpi_scale * rt->tile_height);
539 size = gdk_pixbuf_get_rowstride(pixbuf) * rt->tile_height * rt->hidpi_scale;
540 rt_tile_free_space(rt, size, it);
544 rt->tile_cache_size += size;
549 *-------------------------------------------------------------------
551 *-------------------------------------------------------------------
554 static void rt_overlay_get_position(RendererTiles *rt, OverlayData *od,
555 gint *x, gint *y, gint *w, gint *h)
557 PixbufRenderer *pr = rt->pr;
560 pw = gdk_pixbuf_get_width(od->pixbuf);
561 ph = gdk_pixbuf_get_height(od->pixbuf);
565 if (od->flags & OVL_RELATIVE)
567 if (px < 0) px = pr->viewport_width - pw + px;
568 if (py < 0) py = pr->viewport_height - ph + py;
577 static void rt_overlay_init_window(RendererTiles *rt, OverlayData *od)
579 PixbufRenderer *pr = rt->pr;
581 GdkWindowAttr attributes;
582 gint attributes_mask;
584 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
586 attributes.window_type = GDK_WINDOW_CHILD;
587 attributes.wclass = GDK_INPUT_OUTPUT;
588 attributes.width = pw;
589 attributes.height = ph;
590 attributes.event_mask = GDK_EXPOSURE_MASK;
593 od->window = gdk_window_new(gtk_widget_get_window(GTK_WIDGET(pr)), &attributes, attributes_mask);
594 gdk_window_set_user_data(od->window, pr);
595 gdk_window_move(od->window, px + rt->stereo_off_x, py + rt->stereo_off_y);
596 gdk_window_show(od->window);
599 static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h,
602 PixbufRenderer *pr = rt->pr;
605 work = rt->overlay_list;
615 if (!od->window) rt_overlay_init_window(rt, od);
617 rt_overlay_get_position(rt, od, &px, &py, &pw, &ph);
618 if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
620 if (!rt->overlay_buffer)
622 rt->overlay_buffer = gdk_window_create_similar_surface(gtk_widget_get_window((GtkWidget *)pr),
624 rt->tile_width, rt->tile_height);
631 cr = cairo_create(rt->overlay_buffer);
632 cairo_set_source_surface(cr, it->surface, (pr->x_offset + (it->x - rt->x_scroll)) - rx, (pr->y_offset + (it->y - rt->y_scroll)) - ry);
633 cairo_rectangle(cr, 0, 0, rw, rh);
634 cairo_fill_preserve(cr);
636 gdk_cairo_set_source_pixbuf(cr, od->pixbuf, px - rx, py - ry);
640 cr = gdk_cairo_create(od->window);
641 cairo_set_source_surface(cr, rt->overlay_buffer, rx - px, ry - py);
642 cairo_rectangle (cr, rx - px, ry - py, rw, rh);
648 /* no ImageTile means region may be larger than our scratch buffer */
651 for (sx = rx; sx < rx + rw; sx += rt->tile_width)
652 for (sy = ry; sy < ry + rh; sy += rt->tile_height)
657 sw = MIN(rx + rw - sx, rt->tile_width);
658 sh = MIN(ry + rh - sy, rt->tile_height);
660 cr = cairo_create(rt->overlay_buffer);
661 cairo_set_source_rgb(cr, 0, 0, 0);
662 cairo_rectangle(cr, 0, 0, sw, sh);
663 cairo_fill_preserve(cr);
665 gdk_cairo_set_source_pixbuf(cr, od->pixbuf, px - sx, py - sy);
669 cr = gdk_cairo_create(od->window);
670 cairo_set_source_surface(cr, rt->overlay_buffer, sx - px, sy - py);
671 cairo_rectangle (cr, sx - px, sy - py, sw, sh);
680 static void rt_overlay_queue_draw(RendererTiles *rt, OverlayData *od, gint x1, gint y1, gint x2, gint y2)
682 PixbufRenderer *pr = rt->pr;
685 rt_overlay_get_position(rt, od, &x, &y, &w, &h);
693 rt_queue(rt, rt->x_scroll - pr->x_offset + x,
694 rt->y_scroll - pr->y_offset + y,
696 FALSE, TILE_RENDER_ALL, FALSE, FALSE);
698 rt_border_draw(rt, x, y, w, h);
701 static void rt_overlay_queue_all(RendererTiles *rt, gint x1, gint y1, gint x2, gint y2)
705 work = rt->overlay_list;
708 OverlayData *od = work->data;
711 rt_overlay_queue_draw(rt, od, x1, y1, x2, y2);
715 static void rt_overlay_update_sizes(RendererTiles *rt)
719 work = rt->overlay_list;
722 OverlayData *od = work->data;
725 if (!od->window) rt_overlay_init_window(rt, od);
727 if (od->flags & OVL_RELATIVE)
731 rt_overlay_get_position(rt, od, &x, &y, &w, &h);
732 gdk_window_move_resize(od->window, x + rt->stereo_off_x, y + rt->stereo_off_y, w, h);
737 static OverlayData *rt_overlay_find(RendererTiles *rt, gint id)
741 work = rt->overlay_list;
744 OverlayData *od = work->data;
747 if (od->id == id) return od;
754 gint renderer_tiles_overlay_add(void *renderer, GdkPixbuf *pixbuf, gint x, gint y,
755 OverlayRendererFlags flags)
757 RendererTiles *rt = (RendererTiles *) renderer;
758 PixbufRenderer *pr = rt->pr;
762 g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1);
763 g_return_val_if_fail(pixbuf != NULL, -1);
766 while (rt_overlay_find(rt, id)) id++;
768 od = g_new0(OverlayData, 1);
771 g_object_ref(G_OBJECT(od->pixbuf));
776 rt_overlay_init_window(rt, od);
778 rt->overlay_list = g_list_append(rt->overlay_list, od);
780 gtk_widget_queue_draw(GTK_WIDGET(rt->pr));
785 static void rt_overlay_free(RendererTiles *rt, OverlayData *od)
787 rt->overlay_list = g_list_remove(rt->overlay_list, od);
789 if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
790 if (od->window) gdk_window_destroy(od->window);
793 if (!rt->overlay_list && rt->overlay_buffer)
795 cairo_surface_destroy(rt->overlay_buffer);
796 rt->overlay_buffer = NULL;
800 static void rt_overlay_list_clear(RendererTiles *rt)
802 while (rt->overlay_list)
806 od = rt->overlay_list->data;
807 rt_overlay_free(rt, od);
811 static void rt_overlay_list_reset_window(RendererTiles *rt)
815 if (rt->overlay_buffer) cairo_surface_destroy(rt->overlay_buffer);
816 rt->overlay_buffer = NULL;
818 work = rt->overlay_list;
821 OverlayData *od = work->data;
823 if (od->window) gdk_window_destroy(od->window);
828 void renderer_tiles_overlay_set(void *renderer, gint id, GdkPixbuf *pixbuf, gint UNUSED(x), gint UNUSED(y))
830 RendererTiles *rc = (RendererTiles *)renderer;
831 PixbufRenderer *pr = rc->pr;
834 g_return_if_fail(IS_PIXBUF_RENDERER(pr));
836 od = rt_overlay_find(rc, id);
841 g_object_ref(G_OBJECT(pixbuf));
842 g_object_unref(G_OBJECT(od->pixbuf));
847 rt_overlay_free(rc, od);
850 gtk_widget_queue_draw(GTK_WIDGET(rc->pr));
853 gboolean renderer_tiles_overlay_get(void *renderer, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
855 RendererTiles *rt = (RendererTiles *) renderer;
856 PixbufRenderer *pr = rt->pr;
859 g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE);
861 od = rt_overlay_find(rt, id);
862 if (!od) return FALSE;
864 if (pixbuf) *pixbuf = od->pixbuf;
871 static void rt_hierarchy_changed_cb(GtkWidget *UNUSED(widget), GtkWidget *UNUSED(previous_toplevel), gpointer data)
873 RendererTiles *rt = data;
874 rt_overlay_list_reset_window(rt);
878 *-------------------------------------------------------------------
880 *-------------------------------------------------------------------
883 static GdkPixbuf *rt_get_spare_tile(RendererTiles *rt)
885 if (!rt->spare_tile) rt->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width * rt->hidpi_scale, rt->tile_height * rt->hidpi_scale);
886 return rt->spare_tile;
889 #define COLOR_BYTES 3 /* rgb */
891 static void rt_tile_rotate_90_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
893 GdkPixbuf *src = *tile;
896 guchar *s_pix, *d_pix;
898 guchar *ip, *spi, *dpi;
900 gint tw = rt->tile_width * rt->hidpi_scale;
902 srs = gdk_pixbuf_get_rowstride(src);
903 s_pix = gdk_pixbuf_get_pixels(src);
904 spi = s_pix + (x * COLOR_BYTES);
906 dest = rt_get_spare_tile(rt);
907 drs = gdk_pixbuf_get_rowstride(dest);
908 d_pix = gdk_pixbuf_get_pixels(dest);
909 dpi = d_pix + (tw - 1) * COLOR_BYTES;
911 for (i = y; i < y + h; i++)
913 sp = spi + (i * srs);
914 ip = dpi - (i * COLOR_BYTES);
915 for (j = x; j < x + w; j++)
918 memcpy(dp, sp, COLOR_BYTES);
923 rt->spare_tile = src;
927 static void rt_tile_rotate_90_counter_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
929 GdkPixbuf *src = *tile;
932 guchar *s_pix, *d_pix;
934 guchar *ip, *spi, *dpi;
936 gint th = rt->tile_height * rt->hidpi_scale;
938 srs = gdk_pixbuf_get_rowstride(src);
939 s_pix = gdk_pixbuf_get_pixels(src);
940 spi = s_pix + (x * COLOR_BYTES);
942 dest = rt_get_spare_tile(rt);
943 drs = gdk_pixbuf_get_rowstride(dest);
944 d_pix = gdk_pixbuf_get_pixels(dest);
945 dpi = d_pix + (th - 1) * drs;
947 for (i = y; i < y + h; i++)
949 sp = spi + (i * srs);
950 ip = dpi + (i * COLOR_BYTES);
951 for (j = x; j < x + w; j++)
954 memcpy(dp, sp, COLOR_BYTES);
959 rt->spare_tile = src;
963 static void rt_tile_mirror_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
965 GdkPixbuf *src = *tile;
968 guchar *s_pix, *d_pix;
973 gint tw = rt->tile_width * rt->hidpi_scale;
975 srs = gdk_pixbuf_get_rowstride(src);
976 s_pix = gdk_pixbuf_get_pixels(src);
977 spi = s_pix + (x * COLOR_BYTES);
979 dest = rt_get_spare_tile(rt);
980 drs = gdk_pixbuf_get_rowstride(dest);
981 d_pix = gdk_pixbuf_get_pixels(dest);
982 dpi = d_pix + (tw - x - 1) * COLOR_BYTES;
984 for (i = y; i < y + h; i++)
986 sp = spi + (i * srs);
987 dp = dpi + (i * drs);
988 for (j = 0; j < w; j++)
990 memcpy(dp, sp, COLOR_BYTES);
996 rt->spare_tile = src;
1000 static void rt_tile_mirror_and_flip(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
1002 GdkPixbuf *src = *tile;
1005 guchar *s_pix, *d_pix;
1009 gint tw = rt->tile_width * rt->hidpi_scale;
1010 gint th = rt->tile_height * rt->hidpi_scale;
1012 srs = gdk_pixbuf_get_rowstride(src);
1013 s_pix = gdk_pixbuf_get_pixels(src);
1015 dest = rt_get_spare_tile(rt);
1016 drs = gdk_pixbuf_get_rowstride(dest);
1017 d_pix = gdk_pixbuf_get_pixels(dest);
1018 dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES;
1020 for (i = y; i < y + h; i++)
1022 sp = s_pix + (i * srs) + (x * COLOR_BYTES);
1023 dp = dpi - (i * drs) - (x * COLOR_BYTES);
1024 for (j = 0; j < w; j++)
1026 memcpy(dp, sp, COLOR_BYTES);
1032 rt->spare_tile = src;
1036 static void rt_tile_flip_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h)
1038 GdkPixbuf *src = *tile;
1041 guchar *s_pix, *d_pix;
1045 gint th = rt->tile_height * rt->hidpi_scale;
1047 srs = gdk_pixbuf_get_rowstride(src);
1048 s_pix = gdk_pixbuf_get_pixels(src);
1049 spi = s_pix + (x * COLOR_BYTES);
1051 dest = rt_get_spare_tile(rt);
1052 drs = gdk_pixbuf_get_rowstride(dest);
1053 d_pix = gdk_pixbuf_get_pixels(dest);
1054 dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES);
1056 for (i = y; i < y + h; i++)
1058 sp = spi + (i * srs);
1059 dp = dpi - (i * drs);
1060 memcpy(dp, sp, w * COLOR_BYTES);
1063 rt->spare_tile = src;
1067 static void rt_tile_apply_orientation(RendererTiles *rt, gint orientation, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h)
1069 switch (orientation)
1071 case EXIF_ORIENTATION_TOP_LEFT:
1072 /* normal -- nothing to do */
1074 case EXIF_ORIENTATION_TOP_RIGHT:
1077 rt_tile_mirror_only(rt, pixbuf, x, y, w, h);
1080 case EXIF_ORIENTATION_BOTTOM_RIGHT:
1083 rt_tile_mirror_and_flip(rt, pixbuf, x, y, w, h);
1086 case EXIF_ORIENTATION_BOTTOM_LEFT:
1089 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1092 case EXIF_ORIENTATION_LEFT_TOP:
1094 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1095 rt_tile_rotate_90_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1098 case EXIF_ORIENTATION_RIGHT_TOP:
1099 /* rotated -90 (270) */
1101 rt_tile_rotate_90_clockwise(rt, pixbuf, x, y, w, h);
1104 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1106 rt_tile_flip_only(rt, pixbuf, x, y, w, h);
1107 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h);
1110 case EXIF_ORIENTATION_LEFT_BOTTOM:
1113 rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, y, w, h);
1117 /* The other values are out of range */
1122 static gboolean rt_source_tile_render(RendererTiles *rt, ImageTile *it,
1123 gint x, gint y, gint w, gint h,
1124 gboolean UNUSED(new_data), gboolean fast)
1126 PixbufRenderer *pr = rt->pr;
1129 gboolean draw = FALSE;
1131 if (pr->zoom == 1.0 || pr->scale == 1.0)
1133 list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE);
1138 gint rx, ry, rw, rh;
1143 if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height,
1144 it->x + x, it->y + y, w, h,
1145 &rx, &ry, &rw, &rh))
1148 cr = cairo_create(it->surface);
1149 cairo_rectangle (cr, rx - it->x, ry - it->y, rw, rh);
1153 cairo_set_source_rgb(cr, 0, 0, 0);
1156 else /* (pr->zoom == 1.0 || pr->scale == 1.0) */
1158 rt_hidpi_aware_draw(rt, cr, st->pixbuf, -it->x + st->x, -it->y + st->y);
1166 gdouble scale_x, scale_y;
1167 gint sx, sy, sw, sh;
1169 if (pr->image_width == 0 || pr->image_height == 0) return FALSE;
1170 scale_x = (gdouble)pr->width / pr->image_width;
1171 scale_y = (gdouble)pr->height / pr->image_height;
1173 sx = (gdouble)(it->x + x) / scale_x;
1174 sy = (gdouble)(it->y + y) / scale_y;
1175 sw = (gdouble)w / scale_x;
1176 sh = (gdouble)h / scale_y;
1178 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1181 /* draws red over draw region, to check for leaks (regions not filled) */
1182 pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255);
1185 list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE);
1190 gint rx, ry, rw, rh;
1191 gint stx, sty, stw, sth;
1196 stx = floor((gdouble)st->x * scale_x);
1197 sty = floor((gdouble)st->y * scale_y);
1198 stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx;
1199 sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty;
1201 if (pr_clip_region(stx, sty, stw, sth,
1202 it->x + x, it->y + y, w, h,
1203 &rx, &ry, &rw, &rh))
1209 cr = cairo_create(it->surface);
1210 cairo_rectangle (cr, rx - st->x, ry - st->y, rw, rh);
1211 cairo_set_source_rgb(cr, 0, 0, 0);
1220 /* may need to use unfloored stx,sty values here */
1221 offset_x = (gdouble)(stx - it->x);
1222 offset_y = (gdouble)(sty - it->y);
1224 gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh,
1225 (gdouble) 0.0 + offset_x,
1226 (gdouble) 0.0 + offset_y,
1228 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality);
1243 * @param ignore_alpha
1254 * @param interp_type
1257 * @param wide_image Used as a work-around for a GdkPixbuf problem. Set when image width is > 32767. Problem exhibited with gdk_pixbuf_copy_area() and GDK_INTERP_NEAREST. See #772 on GitHub Geeqie
1261 static void rt_tile_get_region(gboolean has_alpha, gboolean ignore_alpha,
1262 const GdkPixbuf *src, GdkPixbuf *dest,
1263 int pb_x, int pb_y, int pb_w, int pb_h,
1264 double offset_x, double offset_y, double scale_x, double scale_y,
1265 GdkInterpType interp_type,
1266 int check_x, int check_y, gboolean wide_image)
1268 GdkPixbuf* tmppixbuf;
1279 if (scale_x == 1.0 && scale_y == 1.0)
1283 srs = gdk_pixbuf_get_rowstride(src);
1284 drs = gdk_pixbuf_get_rowstride(dest);
1285 psrc = gdk_pixbuf_get_pixels(src);
1286 pdst = gdk_pixbuf_get_pixels(dest);
1287 for (y = 0; y < pb_h; y++)
1289 for (x = 0; x < pb_w; x++)
1291 for (c = 0; c < 3; c++)
1293 pdst[(y * drs) + x*3 + c] = psrc[(-(int)offset_y + pb_y + y) * srs + (-(int)offset_x + pb_x+x)*3 + c];
1300 gdk_pixbuf_copy_area(src,
1301 -offset_x + pb_x, -offset_y + pb_y,
1309 gdk_pixbuf_scale(src, dest,
1310 pb_x, pb_y, pb_w, pb_h,
1314 (wide_image && interp_type == GDK_INTERP_NEAREST) ? GDK_INTERP_TILES : interp_type);
1321 tmppixbuf = gdk_pixbuf_add_alpha(src, FALSE, 0, 0, 0);
1323 pixbuf_ignore_alpha_rect(tmppixbuf, 0, 0, gdk_pixbuf_get_width(src), gdk_pixbuf_get_height(src));
1325 gdk_pixbuf_composite_color(tmppixbuf, dest,
1326 pb_x, pb_y, pb_w, pb_h,
1330 (scale_x == 1.0 && scale_y == 1.0) ? GDK_INTERP_NEAREST : interp_type,
1331 255, check_x, check_y,
1332 PR_ALPHA_CHECK_SIZE,
1333 ((options->image.alpha_color_1.red << 8 & 0x00FF0000) +
1334 (options->image.alpha_color_1.green & 0x00FF00) +
1335 (options->image.alpha_color_1.blue >> 8 & 0x00FF)),
1336 ((options->image.alpha_color_2.red << 8 & 0x00FF0000) +
1337 (options->image.alpha_color_2.green & 0x00FF00) +
1338 (options->image.alpha_color_2.blue >> 8 & 0x00FF)));
1339 g_object_unref(tmppixbuf);
1343 gdk_pixbuf_composite_color(src, dest,
1344 pb_x, pb_y, pb_w, pb_h,
1348 (scale_x == 1.0 && scale_y == 1.0) ? GDK_INTERP_NEAREST : interp_type,
1349 255, check_x, check_y,
1350 PR_ALPHA_CHECK_SIZE,
1351 ((options->image.alpha_color_1.red << 8 & 0x00FF0000) +
1352 (options->image.alpha_color_1.green & 0x00FF00) +
1353 (options->image.alpha_color_1.blue >> 8 & 0x00FF)),
1354 ((options->image.alpha_color_2.red << 8 & 0x00FF0000) +
1355 (options->image.alpha_color_2.green & 0x00FF00) +
1356 (options->image.alpha_color_2.blue >> 8 & 0x00FF)));
1362 static gint rt_get_orientation(RendererTiles *rt)
1364 PixbufRenderer *pr = rt->pr;
1366 gint orientation = pr->orientation;
1367 static const gint mirror[] = {1, 2, 1, 4, 3, 6, 5, 8, 7};
1368 static const gint flip[] = {1, 4, 3, 2, 1, 8, 7, 6, 5};
1370 if (rt->stereo_mode & PR_STEREO_MIRROR) orientation = mirror[orientation];
1371 if (rt->stereo_mode & PR_STEREO_FLIP) orientation = flip[orientation];
1376 static void rt_tile_render(RendererTiles *rt, ImageTile *it,
1377 gint x, gint y, gint w, gint h,
1378 gboolean new_data, gboolean fast)
1380 PixbufRenderer *pr = rt->pr;
1382 gboolean draw = FALSE;
1383 gint orientation = rt_get_orientation(rt);
1384 gboolean wide_image = FALSE;
1386 if (it->render_todo == TILE_RENDER_NONE && it->surface && !new_data) return;
1388 if (it->render_done != TILE_RENDER_ALL)
1394 if (!fast) it->render_done = TILE_RENDER_ALL;
1396 else if (it->render_todo != TILE_RENDER_AREA)
1398 if (!fast) it->render_todo = TILE_RENDER_NONE;
1402 if (!fast) it->render_todo = TILE_RENDER_NONE;
1404 if (new_data) it->blank = FALSE;
1406 rt_tile_prepare(rt, it);
1407 has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf));
1409 /** @FIXME checker colors for alpha should be configurable,
1410 * also should be drawn for blank = TRUE
1415 /* no data, do fast rect fill */
1417 cr = cairo_create(it->surface);
1418 cairo_rectangle (cr, 0, 0, it->w, it->h);
1419 cairo_set_source_rgb(cr, 0, 0, 0);
1423 else if (pr->source_tiles_enabled)
1425 draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast);
1429 gdouble scale_x, scale_y;
1430 gdouble src_x, src_y;
1434 if (pr->image_width == 0 || pr->image_height == 0) return;
1436 scale_x = rt->hidpi_scale * (gdouble)pr->width / pr->image_width;
1437 scale_y = rt->hidpi_scale * (gdouble)pr->height / pr->image_height;
1439 pr_tile_coords_map_orientation(orientation, it->x, it->y,
1440 pr->width, pr->height,
1441 rt->tile_width, rt->tile_height,
1443 pr_tile_region_map_orientation(orientation, x, y,
1444 rt->tile_width, rt->tile_height,
1449 src_x *= rt->hidpi_scale;
1450 src_y *= rt->hidpi_scale;
1451 pb_x *= rt->hidpi_scale;
1452 pb_y *= rt->hidpi_scale;
1453 pb_w *= rt->hidpi_scale;
1454 pb_h *= rt->hidpi_scale;
1456 switch (orientation)
1459 case EXIF_ORIENTATION_LEFT_TOP:
1460 case EXIF_ORIENTATION_RIGHT_TOP:
1461 case EXIF_ORIENTATION_RIGHT_BOTTOM:
1462 case EXIF_ORIENTATION_LEFT_BOTTOM:
1472 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
1473 * small sizes for anything but GDK_INTERP_NEAREST
1475 if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE;
1476 if (pr->image_width > 32767) wide_image = TRUE;
1478 rt_tile_get_region(has_alpha, pr->ignore_alpha,
1479 pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h,
1480 (gdouble) 0.0 - src_x - GET_RIGHT_PIXBUF_OFFSET(rt) * scale_x,
1481 (gdouble) 0.0 - src_y,
1483 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1484 it->x + pb_x, it->y + pb_y, wide_image);
1485 if (rt->stereo_mode & PR_STEREO_ANAGLYPH &&
1486 (pr->stereo_pixbuf_offset_right > 0 || pr->stereo_pixbuf_offset_left > 0))
1488 GdkPixbuf *right_pb = rt_get_spare_tile(rt);
1489 rt_tile_get_region(has_alpha, pr->ignore_alpha,
1490 pr->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h,
1491 (gdouble) 0.0 - src_x - GET_LEFT_PIXBUF_OFFSET(rt) * scale_x,
1492 (gdouble) 0.0 - src_y,
1494 (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality,
1495 it->x + pb_x, it->y + pb_y, wide_image);
1496 pr_create_anaglyph(rt->stereo_mode, it->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h);
1497 /* do not care about freeing spare_tile, it will be reused */
1499 rt_tile_apply_orientation(rt, orientation, &it->pixbuf, pb_x, pb_y, pb_w, pb_h);
1503 if (draw && it->pixbuf && !it->blank)
1507 if (pr->func_post_process && !(pr->post_process_slow && fast))
1508 pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data);
1510 cr = cairo_create(it->surface);
1511 cairo_rectangle (cr, x, y, w, h);
1512 rt_hidpi_aware_draw(rt, cr, it->pixbuf, 0, 0);
1518 static void rt_tile_expose(RendererTiles *rt, ImageTile *it,
1519 gint x, gint y, gint w, gint h,
1520 gboolean new_data, gboolean fast)
1522 PixbufRenderer *pr = rt->pr;
1524 //~ GdkWindow *window;
1527 /* clamp to visible */
1528 if (it->x + x < rt->x_scroll)
1530 w -= rt->x_scroll - it->x - x;
1531 x = rt->x_scroll - it->x;
1533 if (it->x + x + w > rt->x_scroll + pr->vis_width)
1535 w = rt->x_scroll + pr->vis_width - it->x - x;
1538 if (it->y + y < rt->y_scroll)
1540 h -= rt->y_scroll - it->y - y;
1541 y = rt->y_scroll - it->y;
1543 if (it->y + y + h > rt->y_scroll + pr->vis_height)
1545 h = rt->y_scroll + pr->vis_height - it->y - y;
1549 rt_tile_render(rt, it, x, y, w, h, new_data, fast);
1551 //~ box = GTK_WIDGET(pr);
1552 //~ window = gtk_widget_get_window(box);
1554 cr = cairo_create(rt->surface);
1555 cairo_set_source_surface(cr, it->surface, pr->x_offset + (it->x - rt->x_scroll) + rt->stereo_off_x, pr->y_offset + (it->y - rt->y_scroll) + rt->stereo_off_y);
1556 cairo_rectangle (cr, 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);
1560 if (rt->overlay_list)
1562 rt_overlay_draw(rt, pr->x_offset + (it->x - rt->x_scroll) + x,
1563 pr->y_offset + (it->y - rt->y_scroll) + y,
1568 gtk_widget_queue_draw(GTK_WIDGET(rt->pr));
1572 static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it)
1574 PixbufRenderer *pr = rt->pr;
1575 return (it->x + it->w >= rt->x_scroll && it->x < rt->x_scroll + pr->vis_width &&
1576 it->y + it->h >= rt->y_scroll && it->y < rt->y_scroll + pr->vis_height);
1580 *-------------------------------------------------------------------
1582 *-------------------------------------------------------------------
1585 static gint rt_get_queued_area(GList *work)
1591 QueueData *qd = work->data;
1592 area += qd->w * qd->h;
1599 static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set)
1601 PixbufRenderer *pr = rt->pr;
1603 gint visible_area = pr->vis_width * pr->vis_height;
1608 DEBUG_2("redraw priority: 2pass");
1609 rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL);
1613 if (visible_area == 0)
1620 percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area;
1625 /* we have enough data for starting intensive redrawing */
1626 DEBUG_2("redraw priority: high %.2f %%", percent);
1627 rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL);
1631 if (percent < 1.0 || force_set)
1633 /* queue is (almost) empty, wait 50 ms*/
1634 DEBUG_2("redraw priority: wait %.2f %%", percent);
1635 rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL);
1639 /* keep the same priority as before */
1640 DEBUG_2("redraw priority: no change %.2f %%", percent);
1645 static gboolean rt_queue_draw_idle_cb(gpointer data)
1647 RendererTiles *rt = data;
1648 PixbufRenderer *pr = rt->pr;
1653 if ((!pr->pixbuf && !pr->source_tiles_enabled) ||
1654 (!rt->draw_queue && !rt->draw_queue_2pass) ||
1657 pr_render_complete_signal(pr);
1659 rt->draw_idle_id = 0;
1665 qd = rt->draw_queue->data;
1666 fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow));
1672 /* still loading, wait till done (also drops the higher priority) */
1674 return rt_queue_schedule_next_draw(rt, FALSE);
1677 qd = rt->draw_queue_2pass->data;
1681 if (gtk_widget_get_realized(GTK_WIDGET(pr)))
1683 if (rt_tile_is_visible(rt, qd->it))
1685 rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1687 else if (qd->new_data)
1689 /* if new pixel data, and we already have a pixmap, update the tile */
1690 qd->it->blank = FALSE;
1691 if (qd->it->surface && qd->it->render_done == TILE_RENDER_ALL)
1693 rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
1701 rt->draw_queue = g_list_remove(rt->draw_queue, qd);
1706 rt_queue_merge(qd->it->qd2, qd);
1712 rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd);
1723 rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd);
1727 if (!rt->draw_queue && !rt->draw_queue_2pass)
1729 pr_render_complete_signal(pr);
1731 rt->draw_idle_id = 0;
1735 return rt_queue_schedule_next_draw(rt, FALSE);
1738 static void rt_queue_list_free(GList *list)
1758 static void rt_queue_clear(RendererTiles *rt)
1760 rt_queue_list_free(rt->draw_queue);
1761 rt->draw_queue = NULL;
1763 rt_queue_list_free(rt->draw_queue_2pass);
1764 rt->draw_queue_2pass = NULL;
1766 if (rt->draw_idle_id)
1768 g_source_remove(rt->draw_idle_id);
1769 rt->draw_idle_id = 0;
1774 static void rt_queue_merge(QueueData *parent, QueueData *qd)
1776 if (parent->x + parent->w < qd->x + qd->w)
1778 parent->w += (qd->x + qd->w) - (parent->x + parent->w);
1780 if (parent->x > qd->x)
1782 parent->w += parent->x - qd->x;
1786 if (parent->y + parent->h < qd->y + qd->h)
1788 parent->h += (qd->y + qd->h) - (parent->y + parent->h);
1790 if (parent->y > qd->y)
1792 parent->h += parent->y - qd->y;
1796 parent->new_data |= qd->new_data;
1799 static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h)
1801 PixbufRenderer *pr = rt->pr;
1808 vh = pr->vis_height;
1813 if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
1816 nx = CLAMP(*x, vx, vx + vw);
1817 nw = CLAMP(*w - (nx - *x), 1, vw);
1819 ny = CLAMP(*y, vy, vy + vh);
1820 nh = CLAMP(*h - (ny - *y), 1, vh);
1830 static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h,
1831 gboolean clamp, ImageRenderType render,
1832 gboolean new_data, gboolean only_existing)
1834 PixbufRenderer *pr = rt->pr;
1839 if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE;
1841 x1 = ROUND_DOWN(x, rt->tile_width);
1842 x2 = ROUND_UP(x + w, rt->tile_width);
1844 y1 = ROUND_DOWN(y, rt->tile_height);
1845 y2 = ROUND_UP(y + h, rt->tile_height);
1847 for (j = y1; j <= y2; j += rt->tile_height)
1849 for (i = x1; i <= x2; i += rt->tile_width)
1853 it = rt_tile_get(rt, i, j,
1855 (i + rt->tile_width < rt->x_scroll ||
1856 i > rt->x_scroll + pr->vis_width ||
1857 j + rt->tile_height < rt->y_scroll ||
1858 j > rt->y_scroll + pr->vis_height)));
1863 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
1864 (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
1866 it->render_todo = render;
1869 qd = g_new(QueueData, 1);
1871 qd->new_data = new_data;
1881 qd->w = x + w - i - qd->x;
1882 if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x;
1892 qd->h = y + h - j - qd->y;
1893 if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y;
1895 if (qd->w < 1 || qd->h < 1)
1901 rt_queue_merge(it->qd, qd);
1907 rt->draw_queue = g_list_append(rt->draw_queue, qd);
1916 static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h,
1917 gboolean clamp, ImageRenderType render,
1918 gboolean new_data, gboolean only_existing)
1920 PixbufRenderer *pr = rt->pr;
1925 nx = CLAMP(x, 0, pr->width - 1);
1926 ny = CLAMP(y, 0, pr->height - 1);
1929 w = CLAMP(w, 0, pr->width - nx);
1930 h = CLAMP(h, 0, pr->height - ny);
1931 if (w < 1 || h < 1) return;
1933 if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) &&
1934 ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id))
1936 if (rt->draw_idle_id)
1938 g_source_remove(rt->draw_idle_id);
1939 rt->draw_idle_id = 0;
1941 rt_queue_schedule_next_draw(rt, TRUE);
1945 static void rt_scroll(void *renderer, gint x_off, gint y_off)
1947 RendererTiles *rt = (RendererTiles *) renderer;
1948 PixbufRenderer *pr = rt->pr;
1951 if (rt->stereo_mode & PR_STEREO_MIRROR) x_off = -x_off;
1952 if (rt->stereo_mode & PR_STEREO_FLIP) y_off = -y_off;
1954 gint w = pr->vis_width - abs(x_off);
1955 gint h = pr->vis_height - abs(y_off);
1959 /* scrolled completely to new material */
1960 rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
1968 cairo_surface_t *surface;
1992 cr = cairo_create(rt->surface);
1993 surface = rt->surface;
1995 /* clipping restricts the intermediate surface's size, so it's a good idea
1997 cairo_rectangle(cr, x1 + pr->x_offset + rt->stereo_off_x, y1 + pr->y_offset + rt->stereo_off_y, w, h);
1999 /* Now push a group to change the target */
2000 cairo_push_group (cr);
2001 cairo_set_source_surface(cr, surface, x1 - x2, y1 - y2);
2003 /* Now copy the intermediate target back */
2004 cairo_pop_group_to_source(cr);
2008 rt_overlay_queue_all(rt, x2, y2, x1, y1);
2010 w = pr->vis_width - w;
2011 h = pr->vis_height - h;
2016 x_off > 0 ? rt->x_scroll + (pr->vis_width - w) : rt->x_scroll, rt->y_scroll,
2017 w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
2021 /** @FIXME to optimize this, remove overlap */
2023 rt->x_scroll, y_off > 0 ? rt->y_scroll + (pr->vis_height - h) : rt->y_scroll,
2024 pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE);
2029 static void renderer_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h)
2031 RendererTiles *rt = (RendererTiles *)renderer;
2032 PixbufRenderer *pr = rt->pr;
2033 gint x, y, width, height, x1, y1, x2, y2;
2035 gint orientation = rt_get_orientation(rt);
2036 pr_coords_map_orientation_reverse(orientation,
2037 src_x - GET_RIGHT_PIXBUF_OFFSET(rt), src_y,
2038 pr->image_width, pr->image_height,
2043 if (pr->scale != 1.0 && pr->zoom_quality != GDK_INTERP_NEAREST)
2045 /* increase region when using a zoom quality that may access surrounding pixels */
2050 x1 = (gint)floor((gdouble)x * pr->scale);
2051 y1 = (gint)floor((gdouble)y * pr->scale * pr->aspect_ratio);
2052 x2 = (gint)ceil((gdouble)(x + width) * pr->scale);
2053 y2 = (gint)ceil((gdouble)(y + height) * pr->scale * pr->aspect_ratio);
2055 rt_queue(rt, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE);
2058 static void renderer_redraw(RendererTiles *rt, gint x, gint y, gint w, gint h,
2059 gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing)
2061 PixbufRenderer *pr = rt->pr;
2063 x -= rt->stereo_off_x;
2064 y -= rt->stereo_off_y;
2066 rt_border_draw(rt, x, y, w, h);
2068 x = MAX(0, x - pr->x_offset + pr->x_scroll);
2069 y = MAX(0, y - pr->y_offset + pr->y_scroll);
2073 MIN(w, pr->width - x),
2074 MIN(h, pr->height - y),
2075 clamp, render, new_data, only_existing);
2078 static void renderer_update_pixbuf(void *renderer, gboolean UNUSED(lazy))
2080 rt_queue_clear((RendererTiles *)renderer);
2083 static void renderer_update_zoom(void *renderer, gboolean lazy)
2085 RendererTiles *rt = (RendererTiles *)renderer;
2086 PixbufRenderer *pr = rt->pr;
2088 rt_tile_invalidate_all((RendererTiles *)renderer);
2091 renderer_redraw(renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, TRUE, FALSE);
2093 rt_border_clear(rt);
2096 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
2098 rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h);
2101 static void renderer_update_viewport(void *renderer)
2103 RendererTiles *rt = (RendererTiles *)renderer;
2105 rt->stereo_off_x = 0;
2106 rt->stereo_off_y = 0;
2108 if (rt->stereo_mode & PR_STEREO_RIGHT)
2110 if (rt->stereo_mode & PR_STEREO_HORIZ)
2112 rt->stereo_off_x = rt->pr->viewport_width;
2114 else if (rt->stereo_mode & PR_STEREO_VERT)
2116 rt->stereo_off_y = rt->pr->viewport_height;
2118 else if (rt->stereo_mode & PR_STEREO_FIXED)
2120 rt->stereo_off_x = rt->pr->stereo_fixed_x_right;
2121 rt->stereo_off_y = rt->pr->stereo_fixed_y_right;
2126 if (rt->stereo_mode & PR_STEREO_FIXED)
2128 rt->stereo_off_x = rt->pr->stereo_fixed_x_left;
2129 rt->stereo_off_y = rt->pr->stereo_fixed_y_left;
2132 DEBUG_1("update size: %p %d %d %d %d", (void *)rt, rt->stereo_off_x, rt->stereo_off_y, rt->pr->viewport_width, rt->pr->viewport_height);
2134 rt_overlay_update_sizes(rt);
2135 rt_border_clear(rt);
2138 static void renderer_stereo_set(void *renderer, gint stereo_mode)
2140 RendererTiles *rt = (RendererTiles *)renderer;
2142 rt->stereo_mode = stereo_mode;
2145 static void renderer_free(void *renderer)
2147 RendererTiles *rt = (RendererTiles *)renderer;
2149 rt_tile_free_all(rt);
2150 if (rt->spare_tile) g_object_unref(rt->spare_tile);
2151 if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer);
2152 rt_overlay_list_clear(rt);
2153 /* disconnect "hierarchy-changed" */
2154 g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA,
2159 static gboolean rt_realize_cb(GtkWidget *widget, gpointer data)
2161 RendererTiles *rt = (RendererTiles *)data;
2166 rt->surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, gtk_widget_get_allocated_width(widget), gtk_widget_get_allocated_height(widget));
2168 cr = cairo_create(rt->surface);
2169 cairo_set_source_rgb(cr, (gdouble)rt->pr->color.red / 65535, (gdouble)rt->pr->color.green / 65535, (gdouble)rt->pr->color.blue / 65535);
2177 static gboolean rt_size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer data)
2179 RendererTiles *rt = data;
2181 cairo_surface_t *old_surface;
2183 if (gtk_widget_get_realized(GTK_WIDGET(rt->pr)))
2185 old_surface = rt->surface;
2186 rt->surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation->width, allocation->height);
2188 cr = cairo_create(rt->surface);
2190 cairo_set_source_rgb(cr, (gdouble)options->image.border_color.red / 65535, (gdouble)options->image.border_color.green / 65535, (gdouble)options->image.border_color.blue / 65535);
2192 cairo_set_source_surface(cr, old_surface, 0, 0);
2195 cairo_surface_destroy(old_surface);
2197 renderer_redraw(rt, allocation->x, allocation->y, allocation->width, allocation->height, FALSE, TILE_RENDER_ALL, FALSE, FALSE);
2203 static gboolean rt_draw_cb(GtkWidget *UNUSED(widget), cairo_t *cr, gpointer data)
2205 RendererTiles *rt = (RendererTiles *)data;
2209 if (rt->stereo_mode & (PR_STEREO_HORIZ | PR_STEREO_VERT))
2211 cairo_push_group(cr);
2212 cairo_set_source_rgb(cr, (double)rt->pr->color.red / 65535, (double)rt->pr->color.green / 65535, (double)rt->pr->color.blue / 65535);
2214 if (rt->stereo_mode & PR_STEREO_HORIZ)
2216 cairo_rectangle(cr, rt->stereo_off_x, 0, rt->pr->viewport_width, rt->pr->viewport_height);
2220 cairo_rectangle(cr, 0, rt->stereo_off_y, rt->pr->viewport_width, rt->pr->viewport_height);
2225 cairo_rectangle(cr, rt->pr->x_offset + rt->stereo_off_x, rt->pr->y_offset + rt->stereo_off_y, rt->pr->vis_width, rt->pr->vis_height);
2227 cairo_set_source_surface(cr, rt->surface, 0, 0);
2230 cairo_pop_group_to_source(cr);
2235 cairo_set_source_surface(cr, rt->surface, 0, 0);
2239 work = rt->overlay_list;
2243 gint px, py, pw, ph;
2244 pw = gdk_pixbuf_get_width(od->pixbuf);
2245 ph = gdk_pixbuf_get_height(od->pixbuf);
2249 if (od->flags & OVL_RELATIVE)
2251 if (px < 0) px = rt->pr->viewport_width - pw + px;
2252 if (py < 0) py = rt->pr->viewport_height - ph + py;
2255 gdk_cairo_set_source_pixbuf(cr, od->pixbuf, px, py);
2263 RendererFuncs *renderer_tiles_new(PixbufRenderer *pr)
2265 RendererTiles *rt = g_new0(RendererTiles, 1);
2269 rt->f.area_changed = renderer_area_changed;
2270 rt->f.update_pixbuf = renderer_update_pixbuf;
2271 rt->f.free = renderer_free;
2272 rt->f.update_zoom = renderer_update_zoom;
2273 rt->f.invalidate_region = renderer_invalidate_region;
2274 rt->f.scroll = rt_scroll;
2275 rt->f.update_viewport = renderer_update_viewport;
2278 rt->f.overlay_add = renderer_tiles_overlay_add;
2279 rt->f.overlay_set = renderer_tiles_overlay_set;
2280 rt->f.overlay_get = renderer_tiles_overlay_get;
2282 rt->f.stereo_set = renderer_stereo_set;
2284 rt->tile_width = options->image.tile_size;
2285 rt->tile_height = options->image.tile_size;
2288 rt->tile_cache_size = 0;
2290 rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT;
2292 rt->draw_idle_id = 0;
2294 rt->stereo_mode = 0;
2295 rt->stereo_off_x = 0;
2296 rt->stereo_off_y = 0;
2298 rt->hidpi_scale = gtk_widget_get_scale_factor(GTK_WIDGET(rt->pr));
2300 g_signal_connect(G_OBJECT(pr), "hierarchy-changed",
2301 G_CALLBACK(rt_hierarchy_changed_cb), rt);
2303 g_signal_connect(G_OBJECT(pr), "draw",
2304 G_CALLBACK(rt_draw_cb), rt);
2305 g_signal_connect(G_OBJECT(pr), "realize", G_CALLBACK(rt_realize_cb), rt);
2306 g_signal_connect(G_OBJECT(pr), "size-allocate", G_CALLBACK(rt_size_allocate_cb), rt);
2308 return (RendererFuncs *) rt;
2312 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */