From 63fdb63ce1470f08c4be2d7a2ff02582c331560e Mon Sep 17 00:00:00 2001 From: Vladimir Nadvornik Date: Thu, 27 Jan 2011 13:40:05 +0100 Subject: [PATCH] separated tile renderer --- src/Makefile.am | 2 + src/pixbuf-renderer.c | 2343 +++++------------------------------------ src/pixbuf-renderer.h | 118 ++- src/renderer-tiles.c | 1996 +++++++++++++++++++++++++++++++++++ src/renderer-tiles.h | 25 + 5 files changed, 2390 insertions(+), 2094 deletions(-) create mode 100644 src/renderer-tiles.c create mode 100644 src/renderer-tiles.h diff --git a/src/Makefile.am b/src/Makefile.am index 144b0907..0372a578 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -199,6 +199,8 @@ geeqie_SOURCES = \ pan-view.h \ pixbuf-renderer.c \ pixbuf-renderer.h \ + renderer-tiles.c \ + renderer-tiles.h \ pixbuf_util.c \ pixbuf_util.h \ preferences.c \ diff --git a/src/pixbuf-renderer.c b/src/pixbuf-renderer.c index f1bbf600..d2b5389a 100644 --- a/src/pixbuf-renderer.c +++ b/src/pixbuf-renderer.c @@ -17,6 +17,7 @@ #include "main.h" #include "pixbuf-renderer.h" +#include "renderer-tiles.h" #include "intl.h" #include "layout.h" @@ -49,12 +50,6 @@ typedef enum { #endif -/* size to use when breaking up image pane for rendering */ -#define PR_TILE_SIZE 128 - -/* default size of tile cache (mb) */ -#define PR_CACHE_SIZE_DEFAULT 8 - /* default min and max zoom */ #define PR_ZOOM_MIN -32.0 #define PR_ZOOM_MAX 32.0 @@ -69,89 +64,11 @@ typedef enum { #define PR_SCROLLER_UPDATES_PER_SEC 30 #define PR_SCROLLER_DEAD_ZONE 6 -/* alpha channel checkerboard background (same as gimp) */ -#define PR_ALPHA_CHECK1 0x00999999 -#define PR_ALPHA_CHECK2 0x00666666 -#define PR_ALPHA_CHECK_SIZE 16 - /* when scaling image to below this size, use nearest pixel for scaling * (below about 4, the other scale types become slow generating their conversion tables) */ #define PR_MIN_SCALE_SIZE 8 -/* round A up/down to integer count of B */ -#define ROUND_UP(A,B) ((gint)(((A)+(B)-1)/(B))*(B)) -#define ROUND_DOWN(A,B) ((gint)(((A))/(B))*(B)) - -typedef enum { - TILE_RENDER_NONE = 0, /* do nothing */ - TILE_RENDER_AREA, /* render an area of the tile */ - TILE_RENDER_ALL /* render the whole tile */ -} ImageTileRenderType; - -typedef struct _ImageTile ImageTile; -typedef struct _QueueData QueueData; - -struct _ImageTile -{ - GdkPixmap *pixmap; /* off screen buffer */ - GdkPixbuf *pixbuf; /* pixbuf area for zooming */ - gint x; /* x offset into image */ - gint y; /* y offset into image */ - gint w; /* width that is visible (may be less if at edge of image) */ - gint h; /* height '' */ - - gboolean blank; - -/* render_todo: (explanation) - NONE do nothing - AREA render area of tile, usually only used when loading an image - note: will jump to an ALL if render_done is not ALL. - ALL render entire tile, if never done before w/ ALL, for expose events *only* -*/ - - ImageTileRenderType render_todo; /* what to do (see above) */ - ImageTileRenderType render_done; /* highest that has been done before on tile */ - - QueueData *qd; - QueueData *qd2; - - guint size; /* est. memory used by pixmap and pixbuf */ -}; - -struct _QueueData -{ - ImageTile *it; - gint x; - gint y; - gint w; - gint h; - gboolean new_data; -}; - -typedef struct _SourceTile SourceTile; -struct _SourceTile -{ - gint x; - gint y; - GdkPixbuf *pixbuf; - gboolean blank; -}; - -typedef struct _OverlayData OverlayData; -struct _OverlayData -{ - gint id; - - GdkPixbuf *pixbuf; - GdkWindow *window; - - gint x; - gint y; - - OverlayRendererFlags flags; -}; - enum { SIGNAL_ZOOM = 0, SIGNAL_CLICKED, @@ -206,32 +123,17 @@ static void pixbuf_renderer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static gboolean pixbuf_renderer_expose(GtkWidget *widget, GdkEventExpose *event); -static void pr_render_complete_signal(PixbufRenderer *pr); - -static void pr_overlay_list_clear(PixbufRenderer *pr); static void pr_scroller_timer_set(PixbufRenderer *pr, gboolean start); -static void pr_border_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h); static void pr_source_tile_free_all(PixbufRenderer *pr); -static void pr_tile_free_all(PixbufRenderer *pr); -static void pr_tile_invalidate_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h); -static gboolean pr_tile_is_visible(PixbufRenderer *pr, ImageTile *it); -static void pr_queue_clear(PixbufRenderer *pr); -static void pr_queue_merge(QueueData *parent, QueueData *qd); -static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gint clamp, ImageTileRenderType render, gboolean new_data, gboolean only_existing); - -static void pr_redraw(PixbufRenderer *pr, gboolean new_data); static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, PrZoomFlags flags, gint px, gint py); static void pr_signals_connect(PixbufRenderer *pr); static void pr_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data); -static void pr_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data); static void pixbuf_renderer_paint(PixbufRenderer *pr, GdkRectangle *area); -static gint pr_queue_draw_idle_cb(gpointer data); /* @@ -512,16 +414,6 @@ static void pixbuf_renderer_init(PixbufRenderer *pr) pr->scroll_reset = PR_SCROLL_RESET_TOPLEFT; - pr->draw_idle_id = 0; - - pr->tile_width = PR_TILE_SIZE; - pr->tile_height = PR_TILE_SIZE; - - pr->tiles = NULL; - pr->tile_cache_size = 0; - - pr->tile_cache_max = PR_CACHE_SIZE_DEFAULT; - pr->scroller_id = 0; pr->scroller_overlay = -1; @@ -535,14 +427,13 @@ static void pixbuf_renderer_init(PixbufRenderer *pr) pr->norm_center_x = 0.5; pr->norm_center_y = 0.5; + + pr->renderer = (void *)renderer_tiles_new(pr); gtk_widget_set_double_buffered(box, FALSE); g_signal_connect_after(G_OBJECT(box), "size_allocate", G_CALLBACK(pr_size_cb), pr); - g_signal_connect(G_OBJECT(pr), "hierarchy-changed", - G_CALLBACK(pr_hierarchy_changed_cb), pr); - pr_signals_connect(pr); } @@ -552,15 +443,12 @@ static void pixbuf_renderer_finalize(GObject *object) pr = PIXBUF_RENDERER(object); - pr_queue_clear(pr); - pr_tile_free_all(pr); + pr->renderer->free(pr->renderer); if (pr->pixbuf) g_object_unref(pr->pixbuf); - if (pr->spare_tile) g_object_unref(pr->spare_tile); pr_scroller_timer_set(pr, FALSE); - pr_overlay_list_clear(pr); pr_source_tile_free_all(pr); } @@ -610,7 +498,7 @@ static void pixbuf_renderer_set_property(GObject *object, guint prop_id, pr->complete = g_value_get_boolean(value); break; case PROP_CACHE_SIZE_DISPLAY: - pr->tile_cache_max = g_value_get_uint(value); +// pr->tile_cache_max = g_value_get_uint(value); break; case PROP_CACHE_SIZE_TILES: pr->source_tiles_cache_size = g_value_get_uint(value); @@ -676,7 +564,7 @@ static void pixbuf_renderer_get_property(GObject *object, guint prop_id, g_value_set_boolean(value, pr->complete); break; case PROP_CACHE_SIZE_DISPLAY: - g_value_set_uint(value, pr->tile_cache_max); +// g_value_set_uint(value, pr->tile_cache_max); break; case PROP_CACHE_SIZE_TILES: g_value_set_uint(value, pr->source_tiles_cache_size); @@ -764,16 +652,7 @@ static void widget_set_cursor(GtkWidget *widget, gint icon) if (cursor) gdk_cursor_unref(cursor); } -static gint pixmap_calc_size(GdkPixmap *pixmap) -{ - gint w, h, d; - - d = gdk_drawable_get_depth(pixmap); - gdk_drawable_get_size(pixmap, &w, &h); - return w * h * (d / 8); -} - -static gboolean pr_clip_region(gint x, gint y, gint w, gint h, +gboolean pr_clip_region(gint x, gint y, gint w, gint h, gint clip_x, gint clip_y, gint clip_w, gint clip_h, gint *rx, gint *ry, gint *rw, gint *rh) { @@ -862,350 +741,28 @@ GtkWindow *pixbuf_renderer_get_parent(PixbufRenderer *pr) *------------------------------------------------------------------- */ -static void pr_overlay_get_position(PixbufRenderer *pr, OverlayData *od, - gint *x, gint *y, gint *w, gint *h) -{ - gint px, py, pw, ph; - - pw = gdk_pixbuf_get_width(od->pixbuf); - ph = gdk_pixbuf_get_height(od->pixbuf); - px = od->x; - py = od->y; - - if (od->flags & OVL_RELATIVE) - { - if (px < 0) px = pr->window_width - pw + px; - if (py < 0) py = pr->window_height - ph + py; - } - - if (x) *x = px; - if (y) *y = py; - if (w) *w = pw; - if (h) *h = ph; -} - -static void pr_overlay_init_window(PixbufRenderer *pr, OverlayData *od) -{ - gint px, py, pw, ph; - GdkWindowAttr attributes; - gint attributes_mask; - - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.width = pw; - attributes.height = ph; - attributes.event_mask = GDK_EXPOSURE_MASK; - attributes_mask = 0; - - od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask); - gdk_window_set_user_data(od->window, pr); - gdk_window_move(od->window, px, py); - gdk_window_show(od->window); -} - -static void pr_overlay_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - ImageTile *it) -{ - GtkWidget *box; - GList *work; - - box = GTK_WIDGET(pr); - - work = pr->overlay_list; - while (work) - { - OverlayData *od; - gint px, py, pw, ph; - gint rx, ry, rw, rh; - - od = work->data; - work = work->next; - - if (!od->window) pr_overlay_init_window(pr, od); - - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh)) - { - if (!pr->overlay_buffer) - { - pr->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, pr->tile_width, pr->tile_height, -1); - } - - if (it) - { -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(pr->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(pr->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixmap, - rx - (pr->x_offset + (it->x - pr->x_scroll)), - ry - (pr->y_offset + (it->y - pr->y_scroll)), - 0, 0, rw, rh); - gdk_draw_pixbuf(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - od->pixbuf, - rx - px, ry - py, - 0, 0, rw, rh, - pr->dither_quality, rx, ry); -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->overlay_buffer, - 0, 0, - rx - px, ry - py, rw, rh); - } - else - { - /* no ImageTile means region may be larger than our scratch buffer */ - gint sx, sy; - - for (sx = rx; sx < rx + rw; sx += pr->tile_width) - for (sy = ry; sy < ry + rh; sy += pr->tile_height) - { - gint sw, sh; - - sw = MIN(rx + rw - sx, pr->tile_width); - sh = MIN(ry + rh - sy, pr->tile_height); - - gdk_draw_rectangle(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->bg_gc[gtk_widget_get_state(box)], TRUE, -#else - box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE, -#endif - 0, 0, sw, sh); - gdk_draw_pixbuf(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - od->pixbuf, - sx - px, sy - py, - 0, 0, sw, sh, - pr->dither_quality, sx, sy); -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->overlay_buffer, - 0, 0, - sx - px, sy - py, sw, sh); - } - } - } - } -} - -static void pr_overlay_queue_draw(PixbufRenderer *pr, OverlayData *od) -{ - gint x, y, w, h; - - pr_overlay_get_position(pr, od, &x, &y, &w, &h); - pr_queue(pr, pr->x_scroll - pr->x_offset + x, - pr->y_scroll - pr->y_offset + y, - w, h, - FALSE, TILE_RENDER_ALL, FALSE, FALSE); - - pr_border_draw(pr, x, y, w, h); -} - -static void pr_overlay_queue_all(PixbufRenderer *pr) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - pr_overlay_queue_draw(pr, od); - } -} - -static void pr_overlay_update_sizes(PixbufRenderer *pr) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - if (!od->window) pr_overlay_init_window(pr, od); - - if (od->flags & OVL_RELATIVE) - { - gint x, y, w, h; - - pr_overlay_get_position(pr, od, &x, &y, &w, &h); - gdk_window_move_resize(od->window, x, y, w, h); - } - } -} - -static OverlayData *pr_overlay_find(PixbufRenderer *pr, gint id) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - if (od->id == id) return od; - } - - return NULL; -} - gint pixbuf_renderer_overlay_add(PixbufRenderer *pr, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags) { - OverlayData *od; - gint id; - - g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1); - g_return_val_if_fail(pixbuf != NULL, -1); - - id = 1; - while (pr_overlay_find(pr, id)) id++; - - od = g_new0(OverlayData, 1); - od->id = id; - od->pixbuf = pixbuf; - g_object_ref(G_OBJECT(od->pixbuf)); - od->x = x; - od->y = y; - od->flags = flags; - - pr_overlay_init_window(pr, od); - - pr->overlay_list = g_list_append(pr->overlay_list, od); - - pr_overlay_queue_draw(pr, od); - - return od->id; -} - -static void pr_overlay_free(PixbufRenderer *pr, OverlayData *od) -{ - pr->overlay_list = g_list_remove(pr->overlay_list, od); - - if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf)); - if (od->window) gdk_window_destroy(od->window); - g_free(od); - - if (!pr->overlay_list && pr->overlay_buffer) - { - g_object_unref(pr->overlay_buffer); - pr->overlay_buffer = NULL; - } -} - -static void pr_overlay_list_clear(PixbufRenderer *pr) -{ - while (pr->overlay_list) - { - OverlayData *od; - - od = pr->overlay_list->data; - pr_overlay_free(pr, od); - } -} - -static void pr_overlay_list_reset_window(PixbufRenderer *pr) -{ - GList *work; - - if (pr->overlay_buffer) g_object_unref(pr->overlay_buffer); - pr->overlay_buffer = NULL; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - if (od->window) gdk_window_destroy(od->window); - od->window = NULL; - } + return pr->renderer->overlay_add(pr->renderer, pixbuf, x, y, flags); } void pixbuf_renderer_overlay_set(PixbufRenderer *pr, gint id, GdkPixbuf *pixbuf, gint x, gint y) { - OverlayData *od; - - g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - - od = pr_overlay_find(pr, id); - if (!od) return; - - if (pixbuf) - { - gint px, py, pw, ph; - - g_object_ref(G_OBJECT(pixbuf)); - g_object_unref(G_OBJECT(od->pixbuf)); - od->pixbuf = pixbuf; - - od->x = x; - od->y = y; - - if (!od->window) pr_overlay_init_window(pr, od); - - pr_overlay_queue_draw(pr, od); - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - gdk_window_move_resize(od->window, px, py, pw, ph); - } - else - { - pr_overlay_queue_draw(pr, od); - pr_overlay_free(pr, od); - } + pr->renderer->overlay_set(pr->renderer, id, pixbuf, x, y); } gboolean pixbuf_renderer_overlay_get(PixbufRenderer *pr, gint id, GdkPixbuf **pixbuf, gint *x, gint *y) { - OverlayData *od; - - g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); - - od = pr_overlay_find(pr, id); - if (!od) return FALSE; - - if (pixbuf) *pixbuf = od->pixbuf; - if (x) *x = od->x; - if (y) *y = od->y; - - return TRUE; + return pr->renderer->overlay_get(pr->renderer, id, pixbuf, x, y); } void pixbuf_renderer_overlay_remove(PixbufRenderer *pr, gint id) { - pixbuf_renderer_overlay_set(pr, id, NULL, 0, 0); -} - -static void pr_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data) -{ - PixbufRenderer *pr = data; - pr_overlay_list_reset_window(pr); + pr->renderer->overlay_set(pr->renderer, id, NULL, 0, 0); } - /* *------------------------------------------------------------------- * scroller overlay @@ -1359,75 +916,9 @@ static void pr_scroller_stop(PixbufRenderer *pr) *------------------------------------------------------------------- */ -static void pr_border_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h) -{ - GtkWidget *box; - gint rx, ry, rw, rh; - - box = GTK_WIDGET(pr); - - if (!box->window) return; - - if (!pr->pixbuf && !pr->source_tiles_enabled) - { - if (pr_clip_region(x, y, w, h, - 0, 0, - pr->window_width, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - return; - } - - if (pr->vis_width < pr->window_width) - { - if (pr->x_offset > 0 && - pr_clip_region(x, y, w, h, - 0, 0, - pr->x_offset, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - if (pr->window_width - pr->vis_width - pr->x_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset + pr->vis_width, 0, - pr->window_width - pr->vis_width - pr->x_offset, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - } - if (pr->vis_height < pr->window_height) - { - if (pr->y_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset, 0, - pr->vis_width, pr->y_offset, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - if (pr->window_height - pr->vis_height - pr->y_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset, pr->y_offset + pr->vis_height, - pr->vis_width, pr->window_height - pr->vis_height - pr->y_offset, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - } -} - static void pr_border_clear(PixbufRenderer *pr) { - pr_border_draw(pr, 0, 0, pr->window_width, pr->window_height); + pr->renderer->border_draw(pr->renderer, 0, 0, pr->window_width, pr->window_height); } void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color) @@ -1462,6 +953,11 @@ void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color) #endif } +static void pr_redraw(PixbufRenderer *pr, gboolean new_data) +{ + pr->renderer->queue_clear(pr->renderer); + pr->renderer->queue(pr->renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); +} /* *------------------------------------------------------------------- @@ -1508,10 +1004,14 @@ static gboolean pr_source_tile_visible(PixbufRenderer *pr, SourceTile *st) if (!st) return FALSE; - x1 = ROUND_DOWN(pr->x_scroll, pr->tile_width); - y1 = ROUND_DOWN(pr->y_scroll, pr->tile_height); - x2 = ROUND_UP(pr->x_scroll + pr->vis_width, pr->tile_width); - y2 = ROUND_UP(pr->y_scroll + pr->vis_height, pr->tile_height); +// x1 = ROUND_DOWN(pr->x_scroll, pr->tile_width); +// y1 = ROUND_DOWN(pr->y_scroll, pr->tile_height); +// x2 = ROUND_UP(pr->x_scroll + pr->vis_width, pr->tile_width); +// y2 = ROUND_UP(pr->y_scroll + pr->vis_height, pr->tile_height); + x1 = pr->x_scroll; + y1 = pr->y_scroll; + x2 = pr->x_scroll + pr->vis_width; + y2 = pr->y_scroll + pr->vis_height; return !((gdouble)st->x * pr->scale > (gdouble)x2 || (gdouble)(st->x + pr->source_tile_width) * pr->scale < (gdouble)x1 || @@ -1596,7 +1096,7 @@ static SourceTile *pr_source_tile_request(PixbufRenderer *pr, gint x, gint y) st->blank = FALSE; } - pr_tile_invalidate_region(pr, st->x * pr->scale, st->y * pr->scale, + pr->renderer->invalidate_region(pr->renderer, st->x * pr->scale, st->y * pr->scale, pr->source_tile_width * pr->scale, pr->source_tile_height * pr->scale); return st; @@ -1628,7 +1128,7 @@ static SourceTile *pr_source_tile_find(PixbufRenderer *pr, gint x, gint y) return NULL; } -static GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request) +GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request) { gint x1, y1; GList *list = NULL; @@ -1683,7 +1183,7 @@ static void pr_source_tile_changed(PixbufRenderer *pr, gint x, gint y, gint widt if (pr->func_tile_request && pr->func_tile_request(pr, rx, ry, rw, rh, pixbuf, pr->func_tile_data)) { - pr_tile_invalidate_region(pr, rx * pr->scale, ry * pr->scale, + pr->renderer->invalidate_region(pr->renderer, rx * pr->scale, ry * pr->scale, rw * pr->scale, rh * pr->scale); } g_object_unref(pixbuf); @@ -1691,144 +1191,24 @@ static void pr_source_tile_changed(PixbufRenderer *pr, gint x, gint y, gint widt } } -static gboolean pr_source_tile_render(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) +void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height, + gint tile_width, gint tile_height, gint cache_size, + PixbufRendererTileRequestFunc func_request, + PixbufRendererTileDisposeFunc func_dispose, + gpointer user_data, + gdouble zoom) { - GtkWidget *box; - GList *list; - GList *work; - gboolean draw = FALSE; + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + g_return_if_fail(tile_width >= 32 && tile_width >= 32); + g_return_if_fail(width >= 32 && height > 32); + g_return_if_fail(func_request != NULL); - box = GTK_WIDGET(pr); + if (pr->pixbuf) g_object_unref(pr->pixbuf); + pr->pixbuf = NULL; - if (pr->zoom == 1.0 || pr->scale == 1.0) - { - list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE); - work = list; - while (work) - { - SourceTile *st; - gint rx, ry, rw, rh; + pr_source_tile_unset(pr); - st = work->data; - work = work->next; - - if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height, - it->x + x, it->y + y, w, h, - &rx, &ry, &rw, &rh)) - { - if (st->blank) - { - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - rx - st->x, ry - st->y, rw, rh); - } - else /* (pr->zoom == 1.0 || pr->scale == 1.0) */ - { - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - st->pixbuf, - rx - st->x, ry - st->y, - rx - it->x, ry - it->y, - rw, rh, - pr->dither_quality, rx, ry); - } - } - } - } - else - { - gdouble scale_x, scale_y; - gint sx, sy, sw, sh; - - if (pr->image_width == 0 || pr->image_height == 0) return FALSE; - scale_x = (gdouble)pr->width / pr->image_width; - scale_y = (gdouble)pr->height / pr->image_height; - - sx = (gdouble)(it->x + x) / scale_x; - sy = (gdouble)(it->y + y) / scale_y; - sw = (gdouble)w / scale_x; - sh = (gdouble)h / scale_y; - - if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; - -#if 0 - /* draws red over draw region, to check for leaks (regions not filled) */ - pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255); -#endif - - list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE); - work = list; - while (work) - { - SourceTile *st; - gint rx, ry, rw, rh; - gint stx, sty, stw, sth; - - st = work->data; - work = work->next; - - stx = floor((gdouble)st->x * scale_x); - sty = floor((gdouble)st->y * scale_y); - stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx; - sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty; - - if (pr_clip_region(stx, sty, stw, sth, - it->x + x, it->y + y, w, h, - &rx, &ry, &rw, &rh)) - { - if (st->blank) - { - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - rx - st->x, ry - st->y, rw, rh); - } - else - { - gdouble offset_x; - gdouble offset_y; - - /* may need to use unfloored stx,sty values here */ - offset_x = (gdouble)(stx - it->x); - offset_y = (gdouble)(sty - it->y); - - gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh, - (gdouble) 0.0 + offset_x, - (gdouble) 0.0 + offset_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); - draw = TRUE; - } - } - } - } - - g_list_free(list); - - return draw; -} - -void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height, - gint tile_width, gint tile_height, gint cache_size, - PixbufRendererTileRequestFunc func_request, - PixbufRendererTileDisposeFunc func_dispose, - gpointer user_data, - gdouble zoom) -{ - g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - g_return_if_fail(tile_width >= 32 && tile_width >= 32); - g_return_if_fail(width >= 32 && height > 32); - g_return_if_fail(func_request != NULL); - - if (pr->pixbuf) g_object_unref(pr->pixbuf); - pr->pixbuf = NULL; - - pr_source_tile_unset(pr); - - if (cache_size < 4) cache_size = 4; + if (cache_size < 4) cache_size = 4; pr->source_tiles_enabled = TRUE; pr->source_tiles_cache_size = cache_size; @@ -1870,1318 +1250,49 @@ gint pixbuf_renderer_get_tiles(PixbufRenderer *pr) static void pr_zoom_adjust_real(PixbufRenderer *pr, gdouble increment, PrZoomFlags flags, gint x, gint y) { - gdouble zoom = pr->zoom; - - if (increment == 0.0) return; - - if (zoom == 0.0) - { - if (pr->scale < 1.0) - { - zoom = 0.0 - 1.0 / pr->scale; - } - else - { - zoom = pr->scale; - } - } - - if (increment < 0.0) - { - if (zoom >= 1.0 && zoom + increment < 1.0) - { - zoom = zoom + increment - 2.0; - } - else - { - zoom = zoom + increment; - } - } - else - { - if (zoom <= -1.0 && zoom + increment > -1.0) - { - zoom = zoom + increment + 2.0; - } - else - { - zoom = zoom + increment; - } - } - - pr_zoom_sync(pr, zoom, flags, x, y); -} - -/* - *------------------------------------------------------------------- - * display tiles - *------------------------------------------------------------------- - */ - -static ImageTile *pr_tile_new(gint x, gint y, gint width, gint height) -{ - ImageTile *it; - - it = g_new0(ImageTile, 1); - - it->x = x; - it->y = y; - it->w = width; - it->h = height; - - it->render_done = TILE_RENDER_NONE; - - return it; -} - -static void pr_tile_free(ImageTile *it) -{ - if (!it) return; - - if (it->pixbuf) g_object_unref(it->pixbuf); - if (it->pixmap) g_object_unref(it->pixmap); - - g_free(it); -} - -static void pr_tile_free_all(PixbufRenderer *pr) -{ - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - pr_tile_free(it); - } - - g_list_free(pr->tiles); - pr->tiles = NULL; - pr->tile_cache_size = 0; -} - -static ImageTile *pr_tile_add(PixbufRenderer *pr, gint x, gint y) -{ - ImageTile *it; - - it = pr_tile_new(x, y, pr->tile_width, pr->tile_height); - - if (it->x + it->w > pr->width) it->w = pr->width - it->x; - if (it->y + it->h > pr->height) it->h = pr->height - it->y; - - pr->tiles = g_list_prepend(pr->tiles, it); - pr->tile_cache_size += it->size; - - return it; -} - -static void pr_tile_remove(PixbufRenderer *pr, ImageTile *it) -{ - if (it->qd) - { - QueueData *qd = it->qd; - - it->qd = NULL; - pr->draw_queue = g_list_remove(pr->draw_queue, qd); - g_free(qd); - } - - if (it->qd2) - { - QueueData *qd = it->qd2; - - it->qd2 = NULL; - pr->draw_queue_2pass = g_list_remove(pr->draw_queue_2pass, qd); - g_free(qd); - } - - pr->tiles = g_list_remove(pr->tiles, it); - pr->tile_cache_size -= it->size; - - pr_tile_free(it); -} - -static void pr_tile_free_space(PixbufRenderer *pr, guint space, ImageTile *it) -{ - GList *work; - guint tile_max; - - work = g_list_last(pr->tiles); - - if (pr->source_tiles_enabled && pr->scale < 1.0) - { - gint tiles; - - tiles = (pr->vis_width / pr->tile_width + 1) * (pr->vis_height / pr->tile_height + 1); - tile_max = MAX(tiles * pr->tile_width * pr->tile_height * 3, - (gint)((gdouble)pr->tile_cache_max * 1048576.0 * pr->scale)); - } - else - { - tile_max = pr->tile_cache_max * 1048576; - } - - while (work && pr->tile_cache_size + space > tile_max) - { - ImageTile *needle; - - needle = work->data; - work = work->prev; - if (needle != it && - ((!needle->qd && !needle->qd2) || !pr_tile_is_visible(pr, needle))) pr_tile_remove(pr, needle); - } -} - -static void pr_tile_invalidate_all(PixbufRenderer *pr) -{ - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - it->render_done = TILE_RENDER_NONE; - it->render_todo = TILE_RENDER_ALL; - it->blank = FALSE; - - it->w = MIN(pr->tile_width, pr->width - it->x); - it->h = MIN(pr->tile_height, pr->height - it->y); - } -} - -static void pr_tile_invalidate_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h) -{ - gint x1, x2; - gint y1, y2; - GList *work; - - x1 = ROUND_DOWN(x, pr->tile_width); - x2 = ROUND_UP(x + w, pr->tile_width); - - y1 = ROUND_DOWN(y, pr->tile_height); - y2 = ROUND_UP(y + h, pr->tile_height); - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - if (it->x < x2 && it->x + it->w > x1 && - it->y < y2 && it->y + it->h > y1) - { - it->render_done = TILE_RENDER_NONE; - it->render_todo = TILE_RENDER_ALL; - } - } -} - -static ImageTile *pr_tile_get(PixbufRenderer *pr, gint x, gint y, gboolean only_existing) -{ - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - if (it->x == x && it->y == y) - { - pr->tiles = g_list_delete_link(pr->tiles, work); - pr->tiles = g_list_prepend(pr->tiles, it); - return it; - } - - work = work->next; - } - - if (only_existing) return NULL; - - return pr_tile_add(pr, x, y); -} - -static void pr_tile_prepare(PixbufRenderer *pr, ImageTile *it) -{ - if (!it->pixmap) - { - GdkPixmap *pixmap; - guint size; - - pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, pr->tile_width, pr->tile_height, -1); - - size = pixmap_calc_size(pixmap); - pr_tile_free_space(pr, size, it); - - it->pixmap = pixmap; - it->size += size; - pr->tile_cache_size += size; - } - - if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) || - pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf) - { - GdkPixbuf *pixbuf; - guint size; -#if 0 -/* I don't think that we need a pixbuf with alpha channel here */ - if (pr->pixbuf) - { - pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pr->pixbuf), - gdk_pixbuf_get_has_alpha(pr->pixbuf), - gdk_pixbuf_get_bits_per_sample(pr->pixbuf), - pr->tile_width, pr->tile_height); - } - else -#endif - { - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); - } - - size = gdk_pixbuf_get_rowstride(pixbuf) * pr->tile_height; - pr_tile_free_space(pr, size, it); - - it->pixbuf = pixbuf; - it->size += size; - pr->tile_cache_size += size; - } -} - -/* - *------------------------------------------------------------------- - * drawing - *------------------------------------------------------------------- - */ - - -static void pr_tile_coords_map_orientation(PixbufRenderer *pr, - gdouble tile_x, gdouble tile_y, /* coordinates of the tile */ - gdouble image_w, gdouble image_h, - gdouble tile_w, gdouble tile_h, - gdouble *res_x, gdouble *res_y) -{ - *res_x = tile_x; - *res_y = tile_y; - switch (pr->orientation) - { - case EXIF_ORIENTATION_TOP_LEFT: - /* normal -- nothing to do */ - break; - case EXIF_ORIENTATION_TOP_RIGHT: - /* mirrored */ - *res_x = image_w - tile_x - tile_w; - break; - case EXIF_ORIENTATION_BOTTOM_RIGHT: - /* upside down */ - *res_x = image_w - tile_x - tile_w; - *res_y = image_h - tile_y - tile_h; - break; - case EXIF_ORIENTATION_BOTTOM_LEFT: - /* flipped */ - *res_y = image_h - tile_y - tile_h; - break; - case EXIF_ORIENTATION_LEFT_TOP: - *res_x = tile_y; - *res_y = tile_x; - break; - case EXIF_ORIENTATION_RIGHT_TOP: - /* rotated -90 (270) */ - *res_x = tile_y; - *res_y = image_w - tile_x - tile_w; - break; - case EXIF_ORIENTATION_RIGHT_BOTTOM: - *res_x = image_h - tile_y - tile_h; - *res_y = image_w - tile_x - tile_w; - break; - case EXIF_ORIENTATION_LEFT_BOTTOM: - /* rotated 90 */ - *res_x = image_h - tile_y - tile_h; - *res_y = tile_x; - break; - default: - /* The other values are out of range */ - break; - } -// log_printf("tile coord y:%f, ih:%d, th:%f ry:%f\n", tile_y, image_h, tile_h, *res_x); -} - -static void pr_tile_region_map_orientation(PixbufRenderer *pr, - gint area_x, gint area_y, /* coordinates of the area inside tile */ - gint tile_w, gint tile_h, - gint area_w, gint area_h, - gint *res_x, gint *res_y, - gint *res_w, gint *res_h) -{ - *res_x = area_x; - *res_y = area_y; - *res_w = area_w; - *res_h = area_h; - - switch (pr->orientation) - { - case EXIF_ORIENTATION_TOP_LEFT: - /* normal -- nothing to do */ - break; - case EXIF_ORIENTATION_TOP_RIGHT: - /* mirrored */ - *res_x = tile_w - area_x - area_w; - break; - case EXIF_ORIENTATION_BOTTOM_RIGHT: - /* upside down */ - *res_x = tile_w - area_x - area_w; - *res_y = tile_h - area_y - area_h; - break; - case EXIF_ORIENTATION_BOTTOM_LEFT: - /* flipped */ - *res_y = tile_h - area_y - area_h; - break; - case EXIF_ORIENTATION_LEFT_TOP: - *res_x = area_y; - *res_y = area_x; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_RIGHT_TOP: - /* rotated -90 (270) */ - *res_x = area_y; - *res_y = tile_w - area_x - area_w; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_RIGHT_BOTTOM: - *res_x = tile_h - area_y - area_h; - *res_y = tile_w - area_x - area_w; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_LEFT_BOTTOM: - /* rotated 90 */ - *res_x = tile_h - area_y - area_h; - *res_y = area_x; - *res_w = area_h; - *res_h = area_w; - break; - default: - /* The other values are out of range */ - break; - } -// log_printf("inside y:%d, th:%d, ah:%d ry:%d\n", area_y, tile_h, area_h, *res_x); -} - -static void pr_coords_map_orientation_reverse(PixbufRenderer *pr, - gint area_x, gint area_y, - gint tile_w, gint tile_h, - gint area_w, gint area_h, - gint *res_x, gint *res_y, - gint *res_w, gint *res_h) -{ - *res_x = area_x; - *res_y = area_y; - *res_w = area_w; - *res_h = area_h; - - switch (pr->orientation) - { - case EXIF_ORIENTATION_TOP_LEFT: - /* normal -- nothing to do */ - break; - case EXIF_ORIENTATION_TOP_RIGHT: - /* mirrored */ - *res_x = tile_w - area_x - area_w; - break; - case EXIF_ORIENTATION_BOTTOM_RIGHT: - /* upside down */ - *res_x = tile_w - area_x - area_w; - *res_y = tile_h - area_y - area_h; - break; - case EXIF_ORIENTATION_BOTTOM_LEFT: - /* flipped */ - *res_y = tile_h - area_y - area_h; - break; - case EXIF_ORIENTATION_LEFT_TOP: - *res_x = area_y; - *res_y = area_x; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_RIGHT_TOP: - /* rotated -90 (270) */ - *res_x = tile_w - area_y - area_h; - *res_y = area_x; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_RIGHT_BOTTOM: - *res_x = tile_w - area_y - area_h; - *res_y = tile_h - area_x - area_w; - *res_w = area_h; - *res_h = area_w; - break; - case EXIF_ORIENTATION_LEFT_BOTTOM: - /* rotated 90 */ - *res_x = area_y; - *res_y = tile_h - area_x - area_w; - *res_w = area_h; - *res_h = area_w; - break; - default: - /* The other values are out of range */ - break; - } -} - - -static GdkPixbuf *pr_get_spare_tile(PixbufRenderer *pr) -{ - if (!pr->spare_tile) pr->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); - return pr->spare_tile; -} - -#define COLOR_BYTES 3 /* rgb */ - -static void pr_tile_rotate_90_clockwise(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *ip, *spi, *dpi; - gint i, j; - gint tw = pr->tile_width; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (tw - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - ip = dpi - (i * COLOR_BYTES); - for (j = x; j < x + w; j++) - { - dp = ip + (j * drs); - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_rotate_90_counter_clockwise(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *ip, *spi, *dpi; - gint i, j; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - ip = dpi + (i * COLOR_BYTES); - for (j = x; j < x + w; j++) - { - dp = ip - (j * drs); - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_mirror_only(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i, j; - - gint tw = pr->tile_width; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (tw - x - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - dp = dpi + (i * drs); - for (j = 0; j < w; j++) - { - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - dp -= COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_mirror_and_flip(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i, j; - gint tw = pr->tile_width; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = s_pix + (i * srs) + (x * COLOR_BYTES); - dp = dpi - (i * drs) - (x * COLOR_BYTES); - for (j = 0; j < w; j++) - { - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - dp -= COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_flip_only(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES); - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - dp = dpi - (i * drs); - memcpy(dp, sp, w * COLOR_BYTES); - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_apply_orientation(PixbufRenderer *pr, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h) -{ - switch (pr->orientation) - { - case EXIF_ORIENTATION_TOP_LEFT: - /* normal -- nothing to do */ - break; - case EXIF_ORIENTATION_TOP_RIGHT: - /* mirrored */ - { - pr_tile_mirror_only(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_BOTTOM_RIGHT: - /* upside down */ - { - pr_tile_mirror_and_flip(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_BOTTOM_LEFT: - /* flipped */ - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_LEFT_TOP: - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - pr_tile_rotate_90_clockwise(pr, pixbuf, x, pr->tile_height - y - h, w, h); - } - break; - case EXIF_ORIENTATION_RIGHT_TOP: - /* rotated -90 (270) */ - { - pr_tile_rotate_90_clockwise(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_RIGHT_BOTTOM: - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - pr_tile_rotate_90_counter_clockwise(pr, pixbuf, x, pr->tile_height - y - h, w, h); - } - break; - case EXIF_ORIENTATION_LEFT_BOTTOM: - /* rotated 90 */ - { - pr_tile_rotate_90_counter_clockwise(pr, pixbuf, x, y, w, h); - } - break; - default: - /* The other values are out of range */ - break; - } -} - - -static void pr_tile_render(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) -{ - GtkWidget *box; - gboolean has_alpha; - gboolean draw = FALSE; - - if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return; - - if (it->render_done != TILE_RENDER_ALL) - { - x = 0; - y = 0; - w = it->w; - h = it->h; - if (!fast) it->render_done = TILE_RENDER_ALL; - } - else if (it->render_todo != TILE_RENDER_AREA) - { - if (!fast) it->render_todo = TILE_RENDER_NONE; - return; - } - - if (!fast) it->render_todo = TILE_RENDER_NONE; - - if (new_data) it->blank = FALSE; - - pr_tile_prepare(pr, it); - has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)); - - box = GTK_WIDGET(pr); - - /* FIXME checker colors for alpha should be configurable, - * also should be drawn for blank = TRUE - */ - - if (it->blank) - { - /* no data, do fast rect fill */ - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - 0, 0, it->w, it->h); - } - else if (pr->source_tiles_enabled) - { - draw = pr_source_tile_render(pr, it, x, y, w, h, new_data, fast); - } - else if (pr->zoom == 1.0 || pr->scale == 1.0) - { - - gdouble src_x, src_y; - gint pb_x, pb_y; - gint pb_w, pb_h; - pr_tile_coords_map_orientation(pr, it->x, it->y, - pr->image_width, pr->image_height, - pr->tile_width, pr->tile_height, - &src_x, &src_y); - pr_tile_region_map_orientation(pr, x, y, - pr->tile_width, pr->tile_height, - w, h, - &pb_x, &pb_y, - &pb_w, &pb_h); - - if (has_alpha) - { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - 1.0, 1.0, GDK_INTERP_NEAREST, - 255, it->x + pb_x, it->y + pb_y, - PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - else - { - - - if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast))) - { - /* faster, simple, base orientation, no postprocessing */ - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->pixbuf, - it->x + x, it->y + y, - x, y, - w, h, - pr->dither_quality, it->x + x, it->y + y); - } - else - { - gdk_pixbuf_copy_area(pr->pixbuf, - src_x + pb_x, src_y + pb_y, - pb_w, pb_h, - it->pixbuf, - pb_x, pb_y); - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - } - } - else - { - gdouble scale_x, scale_y; - gdouble src_x, src_y; - gint pb_x, pb_y; - gint pb_w, pb_h; - - if (pr->image_width == 0 || pr->image_height == 0) return; - - scale_x = (gdouble)pr->width / pr->image_width; - scale_y = (gdouble)pr->height / pr->image_height; - - pr_tile_coords_map_orientation(pr, it->x, it->y, - pr->image_width * scale_x, pr->image_height * scale_y, - pr->tile_width, pr->tile_height, - &src_x, &src_y); - pr_tile_region_map_orientation(pr, x, y, - pr->tile_width, pr->tile_height, - w, h, - &pb_x, &pb_y, - &pb_w, &pb_h); - switch (pr->orientation) - { - gdouble tmp; - case EXIF_ORIENTATION_LEFT_TOP: - case EXIF_ORIENTATION_RIGHT_TOP: - case EXIF_ORIENTATION_RIGHT_BOTTOM: - case EXIF_ORIENTATION_LEFT_BOTTOM: - tmp = scale_x; - scale_x = scale_y; - scale_y = tmp; - break; - default: - /* nothing to do */ - break; - } - - /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely - * small sizes for anything but GDK_INTERP_NEAREST - */ - if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; - - if (!has_alpha) - { - gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); - } - else - { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, - 255, it->x + pb_x, it->y + pb_y, - PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); - } - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - - if (draw && it->pixbuf && !it->blank) - { - - if (pr->func_post_process && !(pr->post_process_slow && fast)) - pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data); - - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixbuf, - x, y, - x, y, - w, h, - pr->dither_quality, it->x + x, it->y + y); - } - -#if 0 - /* enable this line for debugging the edges of tiles */ - gdk_draw_rectangle(it->pixmap, box->style->white_gc, - FALSE, 0, 0, it->w, it->h); - gdk_draw_rectangle(it->pixmap, box->style->white_gc, - FALSE, x, y, w, h); -#endif -} - - -static void pr_tile_expose(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) -{ - GtkWidget *box; - - pr_tile_render(pr, it, x, y, w, h, new_data, fast); - - box = GTK_WIDGET(pr); - -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixmap, x, y, - pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h); - - if (pr->overlay_list) - { - pr_overlay_draw(pr, pr->x_offset + (it->x - pr->x_scroll) + x, - pr->y_offset + (it->y - pr->y_scroll) + y, - w, h, - it); - } -} - - -static gboolean pr_tile_is_visible(PixbufRenderer *pr, ImageTile *it) -{ - return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width && - it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height); -} - -/* - *------------------------------------------------------------------- - * draw queue - *------------------------------------------------------------------- - */ - -static gint pr_get_queued_area(GList *work) -{ - gint area = 0; - - while (work) - { - QueueData *qd = work->data; - area += qd->w * qd->h; - work = work->next; - } - return area; -} - - -static gboolean pr_queue_schedule_next_draw(PixbufRenderer *pr, gboolean force_set) -{ - gfloat percent; - gint visible_area = pr->vis_width * pr->vis_height; - - if (!pr->loading) - { - /* 2pass prio */ - DEBUG_2("redraw priority: 2pass"); - pr->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - if (visible_area == 0) - { - /* not known yet */ - percent = 100.0; - } - else - { - percent = 100.0 * pr_get_queued_area(pr->draw_queue) / visible_area; - } - - if (percent > 10.0) - { - /* we have enough data for starting intensive redrawing */ - DEBUG_2("redraw priority: high %.2f %%", percent); - pr->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - if (percent < 1.0 || force_set) - { - /* queue is (almost) empty, wait 50 ms*/ - DEBUG_2("redraw priority: wait %.2f %%", percent); - pr->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - /* keep the same priority as before */ - DEBUG_2("redraw priority: no change %.2f %%", percent); - return TRUE; -} - - -static gboolean pr_queue_draw_idle_cb(gpointer data) -{ - PixbufRenderer *pr = data; - QueueData *qd; - gboolean fast; - - - if ((!pr->pixbuf && !pr->source_tiles_enabled) || - (!pr->draw_queue && !pr->draw_queue_2pass) || - !pr->draw_idle_id) - { - pr_render_complete_signal(pr); - - pr->draw_idle_id = 0; - return FALSE; - } - - if (pr->draw_queue) - { - qd = pr->draw_queue->data; - fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow)); - } - else - { - if (pr->loading) - { - /* still loading, wait till done (also drops the higher priority) */ - - return pr_queue_schedule_next_draw(pr, FALSE); - } - - qd = pr->draw_queue_2pass->data; - fast = FALSE; - } - -#if GTK_CHECK_VERSION(2,20,0) - if (gtk_widget_get_realized(pr)) -#else - if (GTK_WIDGET_REALIZED(pr)) -#endif - { - if (pr_tile_is_visible(pr, qd->it)) - { - pr_tile_expose(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); - } - else if (qd->new_data) - { - /* if new pixel data, and we already have a pixmap, update the tile */ - qd->it->blank = FALSE; - if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL) - { - pr_tile_render(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); - } - } - } - - if (pr->draw_queue) - { - qd->it->qd = NULL; - pr->draw_queue = g_list_remove(pr->draw_queue, qd); - if (fast) - { - if (qd->it->qd2) - { - pr_queue_merge(qd->it->qd2, qd); - g_free(qd); - } - else - { - qd->it->qd2 = qd; - pr->draw_queue_2pass = g_list_append(pr->draw_queue_2pass, qd); - } - } - else - { - g_free(qd); - } - } - else - { - qd->it->qd2 = NULL; - pr->draw_queue_2pass = g_list_remove(pr->draw_queue_2pass, qd); - g_free(qd); - } - - if (!pr->draw_queue && !pr->draw_queue_2pass) - { - pr_render_complete_signal(pr); - - pr->draw_idle_id = 0; - return FALSE; - } - - return pr_queue_schedule_next_draw(pr, FALSE); -} - -static void pr_queue_list_free(GList *list) -{ - GList *work; - - work = list; - while (work) - { - QueueData *qd; - - qd = work->data; - work = work->next; - - qd->it->qd = NULL; - qd->it->qd2 = NULL; - g_free(qd); - } - - g_list_free(list); -} - -static void pr_queue_clear(PixbufRenderer *pr) -{ - pr_queue_list_free(pr->draw_queue); - pr->draw_queue = NULL; - - pr_queue_list_free(pr->draw_queue_2pass); - pr->draw_queue_2pass = NULL; - - if (pr->draw_idle_id) - { - g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = 0; - } -} - -static void pr_queue_merge(QueueData *parent, QueueData *qd) -{ - if (parent->x + parent->w < qd->x + qd->w) - { - parent->w += (qd->x + qd->w) - (parent->x + parent->w); - } - if (parent->x > qd->x) - { - parent->w += parent->x - qd->x; - parent->x = qd->x; - } - - if (parent->y + parent->h < qd->y + qd->h) - { - parent->h += (qd->y + qd->h) - (parent->y + parent->h); - } - if (parent->y > qd->y) - { - parent->h += parent->y - qd->y; - parent->y = qd->y; - } - - parent->new_data |= qd->new_data; -} - -static gboolean pr_clamp_to_visible(PixbufRenderer *pr, gint *x, gint *y, gint *w, gint *h) -{ - gint nx, ny; - gint nw, nh; - gint vx, vy; - gint vw, vh; - - vw = pr->vis_width; - vh = pr->vis_height; - - vx = pr->x_scroll; - vy = pr->y_scroll; - - if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE; - - /* now clamp it */ - nx = CLAMP(*x, vx, vx + vw); - nw = CLAMP(*w - (nx - *x), 1, vw); - - ny = CLAMP(*y, vy, vy + vh); - nh = CLAMP(*h - (ny - *y), 1, vh); - - *x = nx; - *y = ny; - *w = nw; - *h = nh; - - return TRUE; -} - -static gboolean pr_queue_to_tiles(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gboolean clamp, ImageTileRenderType render, - gboolean new_data, gboolean only_existing) -{ - gint i, j; - gint x1, x2; - gint y1, y2; - - if (clamp && !pr_clamp_to_visible(pr, &x, &y, &w, &h)) return FALSE; - - x1 = ROUND_DOWN(x, pr->tile_width); - x2 = ROUND_UP(x + w, pr->tile_width); - - y1 = ROUND_DOWN(y, pr->tile_height); - y2 = ROUND_UP(y + h, pr->tile_height); - - for (j = y1; j <= y2; j += pr->tile_height) - { - for (i = x1; i <= x2; i += pr->tile_width) - { - ImageTile *it; - - it = pr_tile_get(pr, i, j, - (only_existing && - (i + pr->tile_width < pr->x_scroll || - i > pr->x_scroll + pr->vis_width || - j + pr->tile_height < pr->y_scroll || - j > pr->y_scroll + pr->vis_height))); - if (it) - { - QueueData *qd; - - if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) || - (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL)) - { - it->render_todo = render; - } - - qd = g_new(QueueData, 1); - qd->it = it; - qd->new_data = new_data; - - if (i < x) - { - qd->x = x - i; - } - else - { - qd->x = 0; - } - qd->w = x + w - i - qd->x; - if (qd->x + qd->w > pr->tile_width) qd->w = pr->tile_width - qd->x; - - if (j < y) - { - qd->y = y - j; - } - else - { - qd->y = 0; - } - qd->h = y + h - j - qd->y; - if (qd->y + qd->h > pr->tile_height) qd->h = pr->tile_height - qd->y; - - if (qd->w < 1 || qd->h < 1) - { - g_free(qd); - } - else if (it->qd) - { - pr_queue_merge(it->qd, qd); - g_free(qd); - } - else - { - it->qd = qd; - pr->draw_queue = g_list_append(pr->draw_queue, qd); - } - } + gdouble zoom = pr->zoom; + + if (increment == 0.0) return; + + if (zoom == 0.0) + { + if (pr->scale < 1.0) + { + zoom = 0.0 - 1.0 / pr->scale; + } + else + { + zoom = pr->scale; } } - return TRUE; -} - -static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gboolean clamp, ImageTileRenderType render, - gboolean new_data, gboolean only_existing) -{ - gint nx, ny; - - nx = CLAMP(x, 0, pr->width - 1); - ny = CLAMP(y, 0, pr->height - 1); - w -= (nx - x); - h -= (ny - y); - w = CLAMP(w, 0, pr->width - nx); - h = CLAMP(h, 0, pr->height - ny); - if (w < 1 || h < 1) return; - - if (pr_queue_to_tiles(pr, nx, ny, w, h, clamp, render, new_data, only_existing) && - ((!pr->draw_queue && !pr->draw_queue_2pass) || !pr->draw_idle_id)) + if (increment < 0.0) { - if (pr->draw_idle_id) + if (zoom >= 1.0 && zoom + increment < 1.0) + { + zoom = zoom + increment - 2.0; + } + else { - g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = 0; + zoom = zoom + increment; + } + } + else + { + if (zoom <= -1.0 && zoom + increment > -1.0) + { + zoom = zoom + increment + 2.0; + } + else + { + zoom = zoom + increment; } - pr_queue_schedule_next_draw(pr, TRUE); } -} -static void pr_redraw(PixbufRenderer *pr, gboolean new_data) -{ - pr_queue_clear(pr); - pr_queue(pr, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); + pr_zoom_sync(pr, zoom, flags, x, y); } + /* *------------------------------------------------------------------- * signal emission @@ -3212,7 +1323,7 @@ static void pr_scroll_notify_signal(PixbufRenderer *pr) g_signal_emit(pr, signals[SIGNAL_SCROLL_NOTIFY], 0); } -static void pr_render_complete_signal(PixbufRenderer *pr) +void pr_render_complete_signal(PixbufRenderer *pr) { if (!pr->complete) { @@ -3242,6 +1353,185 @@ static void pr_update_pixel_signal(PixbufRenderer *pr) *------------------------------------------------------------------- */ + +void pr_tile_coords_map_orientation(gint orientation, + gdouble tile_x, gdouble tile_y, /* coordinates of the tile */ + gdouble image_w, gdouble image_h, + gdouble tile_w, gdouble tile_h, + gdouble *res_x, gdouble *res_y) +{ + *res_x = tile_x; + *res_y = tile_y; + switch (orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = image_w - tile_x - tile_w; + *res_y = image_h - tile_y - tile_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = image_h - tile_y - tile_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = tile_y; + *res_y = tile_x; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = tile_y; + *res_y = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = image_h - tile_y - tile_h; + *res_y = image_w - tile_x - tile_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = image_h - tile_y - tile_h; + *res_y = tile_x; + break; + default: + /* The other values are out of range */ + break; + } +// log_printf("tile coord y:%f, ih:%d, th:%f ry:%f\n", tile_y, image_h, tile_h, *res_x); +} + +void pr_tile_region_map_orientation(gint orientation, + gint area_x, gint area_y, /* coordinates of the area inside tile */ + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h) +{ + *res_x = area_x; + *res_y = area_y; + *res_w = area_w; + *res_h = area_h; + + switch (orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = tile_w - area_x - area_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = tile_w - area_x - area_w; + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = area_y; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = area_y; + *res_y = tile_w - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = tile_h - area_y - area_h; + *res_y = tile_w - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = tile_h - area_y - area_h; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + default: + /* The other values are out of range */ + break; + } +// log_printf("inside y:%d, th:%d, ah:%d ry:%d\n", area_y, tile_h, area_h, *res_x); +} + +void pr_coords_map_orientation_reverse(gint orientation, + gint area_x, gint area_y, + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h) +{ + *res_x = area_x; + *res_y = area_y; + *res_w = area_w; + *res_h = area_h; + + switch (orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + *res_x = tile_w - area_x - area_w; + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + *res_x = tile_w - area_x - area_w; + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + *res_y = tile_h - area_y - area_h; + break; + case EXIF_ORIENTATION_LEFT_TOP: + *res_x = area_y; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + *res_x = tile_w - area_y - area_h; + *res_y = area_x; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + *res_x = tile_w - area_y - area_h; + *res_y = tile_h - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + *res_x = area_y; + *res_y = tile_h - area_x - area_w; + *res_w = area_h; + *res_h = area_w; + break; + default: + /* The other values are out of range */ + break; + } +} + + + static void pixbuf_renderer_sync_scroll_center(PixbufRenderer *pr) { gint src_x, src_y; @@ -3442,7 +1732,7 @@ static gboolean pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, if (invalidate || invalid) { - pr_tile_invalidate_all(pr); + pr->renderer->invalidate_all(pr->renderer); if (!lazy) pr_redraw(pr, TRUE); } if (redrawn) *redrawn = (invalidate || invalid); @@ -3537,7 +1827,7 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, if (lazy) { - pr_queue_clear(pr); + pr->renderer->queue_clear(pr->renderer); } else { @@ -3568,7 +1858,7 @@ static void pr_size_sync(PixbufRenderer *pr, gint new_width, gint new_height) pr_size_clamp(pr); pr_scroll_clamp(pr); - pr_overlay_update_sizes(pr); + pr->renderer->overlay_update_sizes(pr->renderer); /* ensure scroller remains visible */ if (pr->scroller_overlay != -1) @@ -3622,15 +1912,16 @@ static void pixbuf_renderer_paint(PixbufRenderer *pr, GdkRectangle *area) { gint x, y; - pr_border_draw(pr, area->x, area->y, area->width, area->height); + pr->renderer->border_draw(pr->renderer, area->x, area->y, area->width, area->height); x = MAX(0, (gint)area->x - pr->x_offset + pr->x_scroll); y = MAX(0, (gint)area->y - pr->y_offset + pr->y_scroll); - pr_queue(pr, x, y, - MIN((gint)area->width, pr->width - x), - MIN((gint)area->height, pr->height - y), - FALSE, TILE_RENDER_ALL, FALSE, FALSE); + pr->renderer->queue(pr->renderer, + x, y, + MIN((gint)area->width, pr->width - x), + MIN((gint)area->height, pr->height - y), + FALSE, TILE_RENDER_ALL, FALSE, FALSE); } /* @@ -3643,7 +1934,6 @@ void pixbuf_renderer_scroll(PixbufRenderer *pr, gint x, gint y) { gint old_x, old_y; gint x_off, y_off; - gint w, h; g_return_if_fail(IS_PIXBUF_RENDERER(pr)); @@ -3665,93 +1955,8 @@ void pixbuf_renderer_scroll(PixbufRenderer *pr, gint x, gint y) x_off = pr->x_scroll - old_x; y_off = pr->y_scroll - old_y; - - w = pr->vis_width - abs(x_off); - h = pr->vis_height - abs(y_off); - - if (w < 1 || h < 1) - { - /* scrolled completely to new material */ - pr_queue(pr, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - return; - } - else - { - gint x1, y1; - gint x2, y2; - GtkWidget *box; - GdkGC *gc; - GdkEvent *event; - - if (x_off < 0) - { - x1 = abs(x_off); - x2 = 0; - } - else - { - x1 = 0; - x2 = abs(x_off); - } - - if (y_off < 0) - { - y1 = abs(y_off); - y2 = 0; - } - else - { - y1 = 0; - y2 = abs(y_off); - } - - box = GTK_WIDGET(pr); - - gc = gdk_gc_new(box->window); - gdk_gc_set_exposures(gc, TRUE); - gdk_draw_drawable(box->window, gc, - box->window, - x2 + pr->x_offset, y2 + pr->y_offset, - x1 + pr->x_offset, y1 + pr->y_offset, w, h); - g_object_unref(gc); - - if (pr->overlay_list) - { - pr_overlay_queue_all(pr); - } - - w = pr->vis_width - w; - h = pr->vis_height - h; - - if (w > 0) - { - pr_queue(pr, - x_off > 0 ? pr->x_scroll + (pr->vis_width - w) : pr->x_scroll, pr->y_scroll, - w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - } - if (h > 0) - { - /* FIXME, to optimize this, remove overlap */ - pr_queue(pr, - pr->x_scroll, y_off > 0 ? pr->y_scroll + (pr->vis_height - h) : pr->y_scroll, - pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - } - - /* process exposures here, "expose_event" seems to miss a few with obstructed windows */ -#if ! GTK_CHECK_VERSION(2,18,0) - while ((event = gdk_event_get_graphics_expose(box->window)) != NULL) - { - pixbuf_renderer_paint(pr, &event->expose.area); - - if (event->expose.count == 0) - { - gdk_event_free(event); - break; - } - gdk_event_free(event); - } -#endif - } + + pr->renderer->scroll(pr->renderer, x_off, y_off); } void pixbuf_renderer_scroll_to_point(PixbufRenderer *pr, gint x, gint y, @@ -3929,7 +2134,7 @@ static gboolean pr_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, g } #if GTK_CHECK_VERSION(2,20,0) - if (gdk_pointer_is_grabbed() && gtk_widget_has_grab(pr)) + if (gdk_pointer_is_grabbed() && gtk_widget_has_grab(GTK_WIDGET(pr))) #else if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(pr)) #endif @@ -3992,8 +2197,6 @@ static void pr_signals_connect(PixbufRenderer *pr) G_CALLBACK(pr_mouse_release_cb), pr); g_signal_connect(G_OBJECT(pr), "leave_notify_event", G_CALLBACK(pr_mouse_leave_cb), pr); - g_signal_connect(G_OBJECT(pr), "hierarchy-changed", - G_CALLBACK(pr_hierarchy_changed_cb), pr); g_signal_connect(G_OBJECT(pr), "leave_notify_event", G_CALLBACK(pr_leave_notify_cb), pr); @@ -4055,7 +2258,7 @@ static void pr_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, P #endif { gdk_window_clear(box->window); - pr_overlay_draw(pr, 0, 0, pr->window_width, pr->window_height, NULL); + pr->renderer->overlay_draw(pr->renderer, 0, 0, pr->window_width, pr->window_height); } pr_update_signal(pr); @@ -4184,8 +2387,8 @@ void pixbuf_renderer_move(PixbufRenderer *pr, PixbufRenderer *source) pr->scroll_reset = scroll_reset; pixbuf_renderer_set_pixbuf(source, NULL, source->zoom); - pr_queue_clear(source); - pr_tile_free_all(source); +// pr_queue_clear(source); +// pr_tile_free_all(source); } void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gint src_w, gint src_h) @@ -4194,7 +2397,7 @@ void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gi g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - pr_coords_map_orientation_reverse(pr, + pr_coords_map_orientation_reverse(pr->orientation, src_x, src_y, pr->image_width, pr->image_height, src_w, src_h, @@ -4218,7 +2421,7 @@ void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gi x2 = (gint)ceil((gdouble)(x + width) * pr->scale); y2 = (gint)ceil((gdouble)(y + height) * pr->scale); - pr_queue(pr, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE); + pr->renderer->queue(pr->renderer, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE); } void pixbuf_renderer_zoom_adjust(PixbufRenderer *pr, gdouble increment) @@ -4297,7 +2500,7 @@ gboolean pixbuf_renderer_get_pixel_colors(PixbufRenderer *pr, gint x_pixel, gint if (!pb) return FALSE; - pr_tile_region_map_orientation(pr, + pr_tile_region_map_orientation(pr->orientation, x_pixel, y_pixel, pr->image_width, pr->image_height, 1, 1, /*single pixel */ diff --git a/src/pixbuf-renderer.h b/src/pixbuf-renderer.h index ef3d976b..46dbe5bf 100644 --- a/src/pixbuf-renderer.h +++ b/src/pixbuf-renderer.h @@ -24,6 +24,24 @@ #define IS_PIXBUF_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_PIXBUF_RENDERER)) #define PIXBUF_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_PIXBUF_RENDERER, PixbufRendererClass)) +/* alpha channel checkerboard background (same as gimp) */ +#define PR_ALPHA_CHECK1 0x00999999 +#define PR_ALPHA_CHECK2 0x00666666 +#define PR_ALPHA_CHECK_SIZE 16 +/* when scaling image to below this size, use nearest pixel for scaling + * (below about 4, the other scale types become slow generating their conversion tables) + */ +#define PR_MIN_SCALE_SIZE 8 + +/* default size of tile cache (mb) */ +#define PR_CACHE_SIZE_DEFAULT 8 + +/* round A up/down to integer count of B */ +#define ROUND_UP(A,B) ((gint)(((A)+(B)-1)/(B))*(B)) +#define ROUND_DOWN(A,B) ((gint)(((A))/(B))*(B)) + + +typedef struct _RendererFuncs RendererFuncs; typedef struct _PixbufRenderer PixbufRenderer; typedef struct _PixbufRendererClass PixbufRendererClass; @@ -44,6 +62,37 @@ typedef enum { PR_SCROLL_RESET_COUNT, } PixbufRendererScrollResetType; +typedef enum { + TILE_RENDER_NONE = 0, /* do nothing */ + TILE_RENDER_AREA, /* render an area of the tile */ + TILE_RENDER_ALL /* render the whole tile */ +} ImageRenderType; + +typedef enum { + OVL_NORMAL = 0, + OVL_RELATIVE = 1 << 0, /* x,y coordinates are relative, negative values start bottom right */ + /* OVL_HIDE_ON_SCROLL = 1 << 1*/ /* hide temporarily when scrolling (not yet implemented) */ +} OverlayRendererFlags; + +struct _RendererFuncs +{ + void (*queue)(void *renderer, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing); + void (*queue_clear)(void *renderer); + void (*border_draw)(void *renderer, gint x, gint y, gint w, gint h); + void (*invalidate_all)(void *renderer); + void (*invalidate_region)(void *renderer, gint x, gint y, gint w, gint h); + void (*scroll)(void *renderer, gint x_off, gint y_off); + + gint (*overlay_add)(void *renderer, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags); + void (*overlay_set)(void *renderer, gint id, GdkPixbuf *pixbuf, gint x, gint y); + gboolean (*overlay_get)(void *renderer, gint id, GdkPixbuf **pixbuf, gint *x, gint *y); + void (*overlay_update_sizes)(void *renderer); + void (*overlay_draw)(void *renderer, gint x, gint y, gint w, gint h); + + void (*free)(void *renderer); +}; + struct _PixbufRenderer { GtkEventBox eventbox; @@ -56,7 +105,10 @@ struct _PixbufRenderer gint window_width; /* allocated size of window (drawing area) */ gint window_height; - gint x_offset; /* offset of image start (non-zero when image < window) */ + gint viewport_width; /* allocated size of viewport (same as window for normal mode, half of window for SBS mode) */ + gint viewport_height; + + gint x_offset; /* offset of image start (non-zero when viewport < window) */ gint y_offset; gint x_mouse; /* coordinates of the mouse taken from GtkEvent */ @@ -102,20 +154,8 @@ struct _PixbufRenderer gint autofit_limit_size; - gint tile_cache_max; /* max mb to use for offscreen buffer */ /*< private >*/ - - gint tile_width; - gint tile_height; - gint tile_cols; /* count of tile columns */ - GList *tiles; /* list of buffer tiles */ - gint tile_cache_size; /* allocated size of pixmaps/pixbufs */ - GList *draw_queue; /* list of areas to redraw */ - GList *draw_queue_2pass;/* list when 2 pass is enabled */ - - guint draw_idle_id; /* event source id */ - gboolean in_drag; gint drag_last_x; gint drag_last_y; @@ -151,12 +191,9 @@ struct _PixbufRenderer gint scroller_xinc; gint scroller_yinc; - GList *overlay_list; - GdkPixmap *overlay_buffer; - - GdkPixbuf *spare_tile; - gint orientation; + + RendererFuncs *renderer; }; struct _PixbufRendererClass @@ -173,6 +210,8 @@ struct _PixbufRendererClass }; + + GType pixbuf_renderer_get_type(void); PixbufRenderer *pixbuf_renderer_new(void); @@ -250,11 +289,6 @@ gboolean pixbuf_renderer_get_virtual_rect(PixbufRenderer *pr, GdkRectangle *rect void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color); /* overlay */ -typedef enum { - OVL_NORMAL = 0, - OVL_RELATIVE = 1 << 0, /* x,y coordinates are relative, negative values start bottom right */ - /* OVL_HIDE_ON_SCROLL = 1 << 1*/ /* hide temporarily when scrolling (not yet implemented) */ -} OverlayRendererFlags; gint pixbuf_renderer_overlay_add(PixbufRenderer *pr, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags); @@ -265,9 +299,45 @@ void pixbuf_renderer_overlay_remove(PixbufRenderer *pr, gint id); gboolean pixbuf_renderer_get_mouse_position(PixbufRenderer *pr, gint *x_pixel, gint *y_pixel); /* x_pixel and y_pixel are the pixel coordinates \see pixbuf_renderer_get_mouse_position */ gboolean pixbuf_renderer_get_pixel_colors(PixbufRenderer *pr, gint x_pixel, gint y_pixel, - gint *r_mouse, gint *g_mouse, gint *b_mouse); + gint *r_mouse, gint *g_mouse, gint *b_mouse); void pixbuf_renderer_set_size_early(PixbufRenderer *pr, guint width, guint height); +/* protected - for renderer use only*/ + +typedef struct _SourceTile SourceTile; +struct _SourceTile +{ + gint x; + gint y; + GdkPixbuf *pixbuf; + gboolean blank; +}; + + +gboolean pr_clip_region(gint x, gint y, gint w, gint h, + gint clip_x, gint clip_y, gint clip_w, gint clip_h, + gint *rx, gint *ry, gint *rw, gint *rh); +void pr_render_complete_signal(PixbufRenderer *pr); + +void pr_tile_coords_map_orientation(gint orientation, + gdouble tile_x, gdouble tile_y, /* coordinates of the tile */ + gdouble image_w, gdouble image_h, + gdouble tile_w, gdouble tile_h, + gdouble *res_x, gdouble *res_y); +void pr_tile_region_map_orientation(gint orientation, + gint area_x, gint area_y, /* coordinates of the area inside tile */ + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h); +void pr_coords_map_orientation_reverse(gint orientation, + gint area_x, gint area_y, + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h); + +GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/renderer-tiles.c b/src/renderer-tiles.c new file mode 100644 index 00000000..f1b313a5 --- /dev/null +++ b/src/renderer-tiles.c @@ -0,0 +1,1996 @@ +/* + * Geeqie + * (C) 2006 John Ellis + * Copyright (C) 2008 - 2010 The Geeqie Team + * + * Author: John Ellis + * + * This software is released under the GNU General Public License (GNU GPL). + * Please read the included file COPYING for more information. + * This software comes with no warranty of any kind, use at your own risk! + */ + +#include +#include +#include +#include + +#include "main.h" +#include "pixbuf-renderer.h" +#include "renderer-tiles.h" + +#include "intl.h" +#include "layout.h" + +#include + + +/* comment this out if not using this from within Geeqie + * defining GQ_BUILD does these things: + * - Sets the shift-click scroller pixbuf to a nice icon instead of a black box + */ +#define GQ_BUILD 1 + +#ifdef GQ_BUILD +#include "main.h" +#include "pixbuf_util.h" +#include "exif.h" +#else +typedef enum { + EXIF_ORIENTATION_UNKNOWN = 0, + EXIF_ORIENTATION_TOP_LEFT = 1, + EXIF_ORIENTATION_TOP_RIGHT = 2, + EXIF_ORIENTATION_BOTTOM_RIGHT = 3, + EXIF_ORIENTATION_BOTTOM_LEFT = 4, + EXIF_ORIENTATION_LEFT_TOP = 5, + EXIF_ORIENTATION_RIGHT_TOP = 6, + EXIF_ORIENTATION_RIGHT_BOTTOM = 7, + EXIF_ORIENTATION_LEFT_BOTTOM = 8 +} ExifOrientationType; +#endif + + +/* size to use when breaking up image pane for rendering */ +#define PR_TILE_SIZE 128 + +typedef struct _ImageTile ImageTile; +typedef struct _QueueData QueueData; + +struct _ImageTile +{ + GdkPixmap *pixmap; /* off screen buffer */ + GdkPixbuf *pixbuf; /* pixbuf area for zooming */ + gint x; /* x offset into image */ + gint y; /* y offset into image */ + gint w; /* width that is visible (may be less if at edge of image) */ + gint h; /* height '' */ + + gboolean blank; + +/* render_todo: (explanation) + NONE do nothing + AREA render area of tile, usually only used when loading an image + note: will jump to an ALL if render_done is not ALL. + ALL render entire tile, if never done before w/ ALL, for expose events *only* +*/ + + ImageRenderType render_todo; /* what to do (see above) */ + ImageRenderType render_done; /* highest that has been done before on tile */ + + QueueData *qd; + QueueData *qd2; + + guint size; /* est. memory used by pixmap and pixbuf */ +}; + +struct _QueueData +{ + ImageTile *it; + gint x; + gint y; + gint w; + gint h; + gboolean new_data; +}; + +typedef struct _OverlayData OverlayData; +struct _OverlayData +{ + gint id; + + GdkPixbuf *pixbuf; + GdkWindow *window; + + gint x; + gint y; + + OverlayRendererFlags flags; +}; + +typedef struct _RendererTiles RendererTiles; + +struct _RendererTiles +{ + RendererFuncs f; + PixbufRenderer *pr; + + gint tile_cache_max; /* max mb to use for offscreen buffer */ + + gint tile_width; + gint tile_height; + gint tile_cols; /* count of tile columns */ + GList *tiles; /* list of buffer tiles */ + gint tile_cache_size; /* allocated size of pixmaps/pixbufs */ + GList *draw_queue; /* list of areas to redraw */ + GList *draw_queue_2pass;/* list when 2 pass is enabled */ + + GList *overlay_list; + GdkPixmap *overlay_buffer; + + guint draw_idle_id; /* event source id */ + + GdkPixbuf *spare_tile; + +}; + + +static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h); +static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, ImageTile *it); + + +static void rt_tile_free_all(RendererTiles *rt); +static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h); +static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it); +static void rt_queue_clear(RendererTiles *rt); +static void rt_queue_merge(QueueData *parent, QueueData *qd); +static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing); + +static void rt_redraw(RendererTiles *rt, gboolean new_data); + + +static void rt_signals_connect(RendererTiles *rt); +static void rt_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data); +static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data); +static void pixbuf_renderer_paint(RendererTiles *rt, GdkRectangle *area); +static gint rt_queue_draw_idle_cb(gpointer data); + + +/* + *------------------------------------------------------------------- + * borders + *------------------------------------------------------------------- + */ + +static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + gint rx, ry, rw, rh; + + box = GTK_WIDGET(pr); + + if (!box->window) return; + + if (!pr->pixbuf && !pr->source_tiles_enabled) + { + if (pr_clip_region(x, y, w, h, + 0, 0, + pr->window_width, pr->window_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx, ry, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + return; + } + + if (pr->vis_width < pr->window_width) + { + if (pr->x_offset > 0 && + pr_clip_region(x, y, w, h, + 0, 0, + pr->x_offset, pr->window_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx, ry, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + if (pr->window_width - pr->vis_width - pr->x_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset + pr->vis_width, 0, + pr->window_width - pr->vis_width - pr->x_offset, pr->window_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx, ry, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + } + if (pr->vis_height < pr->window_height) + { + if (pr->y_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset, 0, + pr->vis_width, pr->y_offset, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx, ry, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + if (pr->window_height - pr->vis_height - pr->y_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset, pr->y_offset + pr->vis_height, + pr->vis_width, pr->window_height - pr->vis_height - pr->y_offset, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx, ry, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + } +} + +static void rt_border_clear(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + rt_border_draw(rt, 0, 0, pr->window_width, pr->window_height); +} + + +/* + *------------------------------------------------------------------- + * display tiles + *------------------------------------------------------------------- + */ + +static ImageTile *rt_tile_new(gint x, gint y, gint width, gint height) +{ + ImageTile *it; + + it = g_new0(ImageTile, 1); + + it->x = x; + it->y = y; + it->w = width; + it->h = height; + + it->render_done = TILE_RENDER_NONE; + + return it; +} + +static void rt_tile_free(ImageTile *it) +{ + if (!it) return; + + if (it->pixbuf) g_object_unref(it->pixbuf); + if (it->pixmap) g_object_unref(it->pixmap); + + g_free(it); +} + +static void rt_tile_free_all(RendererTiles *rt) +{ + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + rt_tile_free(it); + } + + g_list_free(rt->tiles); + rt->tiles = NULL; + rt->tile_cache_size = 0; +} + +static ImageTile *rt_tile_add(RendererTiles *rt, gint x, gint y) +{ + PixbufRenderer *pr = rt->pr; + ImageTile *it; + + it = rt_tile_new(x, y, rt->tile_width, rt->tile_height); + + if (it->x + it->w > pr->width) it->w = pr->width - it->x; + if (it->y + it->h > pr->height) it->h = pr->height - it->y; + + rt->tiles = g_list_prepend(rt->tiles, it); + rt->tile_cache_size += it->size; + + return it; +} + +static void rt_tile_remove(RendererTiles *rt, ImageTile *it) +{ + if (it->qd) + { + QueueData *qd = it->qd; + + it->qd = NULL; + rt->draw_queue = g_list_remove(rt->draw_queue, qd); + g_free(qd); + } + + if (it->qd2) + { + QueueData *qd = it->qd2; + + it->qd2 = NULL; + rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd); + g_free(qd); + } + + rt->tiles = g_list_remove(rt->tiles, it); + rt->tile_cache_size -= it->size; + + rt_tile_free(it); +} + +static void rt_tile_free_space(RendererTiles *rt, guint space, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + GList *work; + guint tile_max; + + work = g_list_last(rt->tiles); + + if (pr->source_tiles_enabled && pr->scale < 1.0) + { + gint tiles; + + tiles = (pr->vis_width / rt->tile_width + 1) * (pr->vis_height / rt->tile_height + 1); + tile_max = MAX(tiles * rt->tile_width * rt->tile_height * 3, + (gint)((gdouble)rt->tile_cache_max * 1048576.0 * pr->scale)); + } + else + { + tile_max = rt->tile_cache_max * 1048576; + } + + while (work && rt->tile_cache_size + space > tile_max) + { + ImageTile *needle; + + needle = work->data; + work = work->prev; + if (needle != it && + ((!needle->qd && !needle->qd2) || !rt_tile_is_visible(rt, needle))) rt_tile_remove(rt, needle); + } +} + +static void rt_tile_invalidate_all(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + it->render_done = TILE_RENDER_NONE; + it->render_todo = TILE_RENDER_ALL; + it->blank = FALSE; + + it->w = MIN(rt->tile_width, pr->width - it->x); + it->h = MIN(rt->tile_height, pr->height - it->y); + } +} + +static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h) +{ + gint x1, x2; + gint y1, y2; + GList *work; + + x1 = ROUND_DOWN(x, rt->tile_width); + x2 = ROUND_UP(x + w, rt->tile_width); + + y1 = ROUND_DOWN(y, rt->tile_height); + y2 = ROUND_UP(y + h, rt->tile_height); + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + if (it->x < x2 && it->x + it->w > x1 && + it->y < y2 && it->y + it->h > y1) + { + it->render_done = TILE_RENDER_NONE; + it->render_todo = TILE_RENDER_ALL; + } + } +} + +static ImageTile *rt_tile_get(RendererTiles *rt, gint x, gint y, gboolean only_existing) +{ + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + if (it->x == x && it->y == y) + { + rt->tiles = g_list_delete_link(rt->tiles, work); + rt->tiles = g_list_prepend(rt->tiles, it); + return it; + } + + work = work->next; + } + + if (only_existing) return NULL; + + return rt_tile_add(rt, x, y); +} + +static gint pixmap_calc_size(GdkPixmap *pixmap) +{ + gint w, h, d; + + d = gdk_drawable_get_depth(pixmap); + gdk_drawable_get_size(pixmap, &w, &h); + return w * h * (d / 8); +} + +static void rt_tile_prepare(RendererTiles *rt, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + if (!it->pixmap) + { + GdkPixmap *pixmap; + guint size; + + pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1); + + size = pixmap_calc_size(pixmap); + rt_tile_free_space(rt, size, it); + + it->pixmap = pixmap; + it->size += size; + rt->tile_cache_size += size; + } + + if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) || + pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf) + { + GdkPixbuf *pixbuf; + guint size; + { + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height); + } + + size = gdk_pixbuf_get_rowstride(pixbuf) * rt->tile_height; + rt_tile_free_space(rt, size, it); + + it->pixbuf = pixbuf; + it->size += size; + rt->tile_cache_size += size; + } +} + +/* + *------------------------------------------------------------------- + * overlays + *------------------------------------------------------------------- + */ + +static void rt_overlay_get_position(RendererTiles *rt, OverlayData *od, + gint *x, gint *y, gint *w, gint *h) +{ + PixbufRenderer *pr = rt->pr; + gint px, py, pw, ph; + + pw = gdk_pixbuf_get_width(od->pixbuf); + ph = gdk_pixbuf_get_height(od->pixbuf); + px = od->x; + py = od->y; + + if (od->flags & OVL_RELATIVE) + { + if (px < 0) px = pr->window_width - pw + px; + if (py < 0) py = pr->window_height - ph + py; + } + + if (x) *x = px; + if (y) *y = py; + if (w) *w = pw; + if (h) *h = ph; +} + +static void rt_overlay_init_window(RendererTiles *rt, OverlayData *od) +{ + PixbufRenderer *pr = rt->pr; + gint px, py, pw, ph; + GdkWindowAttr attributes; + gint attributes_mask; + + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.width = pw; + attributes.height = ph; + attributes.event_mask = GDK_EXPOSURE_MASK; + attributes_mask = 0; + + od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask); + gdk_window_set_user_data(od->window, pr); + gdk_window_move(od->window, px, py); + gdk_window_show(od->window); +} + +static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, + ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + GList *work; + + box = GTK_WIDGET(pr); + + work = rt->overlay_list; + while (work) + { + OverlayData *od; + gint px, py, pw, ph; + gint rx, ry, rw, rh; + + od = work->data; + work = work->next; + + if (!od->window) rt_overlay_init_window(rt, od); + + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh)) + { + if (!rt->overlay_buffer) + { + rt->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1); + } + + if (it) + { +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixmap, + rx - (pr->x_offset + (it->x - pr->x_scroll)), + ry - (pr->y_offset + (it->y - pr->y_scroll)), + 0, 0, rw, rh); + gdk_draw_pixbuf(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + od->pixbuf, + rx - px, ry - py, + 0, 0, rw, rh, + pr->dither_quality, rx, ry); +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + rt->overlay_buffer, + 0, 0, + rx - px, ry - py, rw, rh); + } + else + { + /* no ImageTile means region may be larger than our scratch buffer */ + gint sx, sy; + + for (sx = rx; sx < rx + rw; sx += rt->tile_width) + for (sy = ry; sy < ry + rh; sy += rt->tile_height) + { + gint sw, sh; + + sw = MIN(rx + rw - sx, rt->tile_width); + sh = MIN(ry + rh - sy, rt->tile_height); + + gdk_draw_rectangle(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->bg_gc[gtk_widget_get_state(box)], TRUE, +#else + box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE, +#endif + 0, 0, sw, sh); + gdk_draw_pixbuf(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + od->pixbuf, + sx - px, sy - py, + 0, 0, sw, sh, + pr->dither_quality, sx, sy); +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + rt->overlay_buffer, + 0, 0, + sx - px, sy - py, sw, sh); + } + } + } + } +} + +static void rt_overlay_queue_draw(RendererTiles *rt, OverlayData *od, gint x1, gint y1, gint x2, gint y2) +{ + PixbufRenderer *pr = rt->pr; + gint x, y, w, h; + + rt_overlay_get_position(rt, od, &x, &y, &w, &h); + + /* add borders */ + x -= x1; + y -= y1; + w += x1 + x2; + h += y1 + y2; + + rt_queue(rt, pr->x_scroll - pr->x_offset + x, + pr->y_scroll - pr->y_offset + y, + w, h, + FALSE, TILE_RENDER_ALL, FALSE, FALSE); + + rt_border_draw(rt, x, y, w, h); +} + +static void rt_overlay_queue_all(RendererTiles *rt, gint x1, gint y1, gint x2, gint y2) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + rt_overlay_queue_draw(rt, od, x1, y1, x2, y2); + } +} + +static void rt_overlay_update_sizes(RendererTiles *rt) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + if (!od->window) rt_overlay_init_window(rt, od); + + if (od->flags & OVL_RELATIVE) + { + gint x, y, w, h; + + rt_overlay_get_position(rt, od, &x, &y, &w, &h); + gdk_window_move_resize(od->window, x, y, w, h); + } + } +} + +static OverlayData *rt_overlay_find(RendererTiles *rt, gint id) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + if (od->id == id) return od; + } + + return NULL; +} + + +gint renderer_tiles_overlay_add(RendererTiles *rt, GdkPixbuf *pixbuf, gint x, gint y, + OverlayRendererFlags flags) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + gint id; + + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1); + g_return_val_if_fail(pixbuf != NULL, -1); + + id = 1; + while (rt_overlay_find(rt, id)) id++; + + od = g_new0(OverlayData, 1); + od->id = id; + od->pixbuf = pixbuf; + g_object_ref(G_OBJECT(od->pixbuf)); + od->x = x; + od->y = y; + od->flags = flags; + + rt_overlay_init_window(rt, od); + + rt->overlay_list = g_list_append(rt->overlay_list, od); + + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + + return od->id; +} + +static void rt_overlay_free(RendererTiles *rt, OverlayData *od) +{ + rt->overlay_list = g_list_remove(rt->overlay_list, od); + + if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf)); + if (od->window) gdk_window_destroy(od->window); + g_free(od); + + if (!rt->overlay_list && rt->overlay_buffer) + { + g_object_unref(rt->overlay_buffer); + rt->overlay_buffer = NULL; + } +} + +static void rt_overlay_list_clear(RendererTiles *rt) +{ + while (rt->overlay_list) + { + OverlayData *od; + + od = rt->overlay_list->data; + rt_overlay_free(rt, od); + } +} + +static void rt_overlay_list_reset_window(RendererTiles *rt) +{ + GList *work; + + if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer); + rt->overlay_buffer = NULL; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + if (od->window) gdk_window_destroy(od->window); + od->window = NULL; + } +} + +void renderer_tiles_overlay_set(RendererTiles *rt, gint id, GdkPixbuf *pixbuf, gint x, gint y) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + od = rt_overlay_find(rt, id); + if (!od) return; + + if (pixbuf) + { + gint px, py, pw, ph; + + g_object_ref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(od->pixbuf)); + od->pixbuf = pixbuf; + + od->x = x; + od->y = y; + + if (!od->window) rt_overlay_init_window(rt, od); + + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + gdk_window_move_resize(od->window, px, py, pw, ph); + } + else + { + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + rt_overlay_free(rt, od); + } +} + +gboolean renderer_tiles_overlay_get(RendererTiles *rt, gint id, GdkPixbuf **pixbuf, gint *x, gint *y) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); + + od = rt_overlay_find(rt, id); + if (!od) return FALSE; + + if (pixbuf) *pixbuf = od->pixbuf; + if (x) *x = od->x; + if (y) *y = od->y; + + return TRUE; +} + +static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data) +{ + RendererTiles *rt = data; + rt_overlay_list_reset_window(rt); +} + +/* + *------------------------------------------------------------------- + * drawing + *------------------------------------------------------------------- + */ + +static GdkPixbuf *rt_get_spare_tile(RendererTiles *rt) +{ + if (!rt->spare_tile) rt->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height); + return rt->spare_tile; +} + +#define COLOR_BYTES 3 /* rgb */ + +static void rt_tile_rotate_90_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *ip, *spi, *dpi; + gint i, j; + gint tw = rt->tile_width; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (tw - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + ip = dpi - (i * COLOR_BYTES); + for (j = x; j < x + w; j++) + { + dp = ip + (j * drs); + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_rotate_90_counter_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *ip, *spi, *dpi; + gint i, j; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + ip = dpi + (i * COLOR_BYTES); + for (j = x; j < x + w; j++) + { + dp = ip - (j * drs); + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_mirror_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + + gint tw = rt->tile_width; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (tw - x - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + dp -= COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_mirror_and_flip(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + gint tw = rt->tile_width; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = s_pix + (i * srs) + (x * COLOR_BYTES); + dp = dpi - (i * drs) - (x * COLOR_BYTES); + for (j = 0; j < w; j++) + { + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + dp -= COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_flip_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES); + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi - (i * drs); + memcpy(dp, sp, w * COLOR_BYTES); + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_apply_orientation(RendererTiles *rt, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h) +{ + PixbufRenderer *pr = rt->pr; + switch (pr->orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + { + rt_tile_mirror_only(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + { + rt_tile_mirror_and_flip(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_TOP: + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + rt_tile_rotate_90_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + { + rt_tile_rotate_90_clockwise(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + { + rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, y, w, h); + } + break; + default: + /* The other values are out of range */ + break; + } +} + +static gboolean rt_source_tile_render(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + GList *list; + GList *work; + gboolean draw = FALSE; + + box = GTK_WIDGET(pr); + + if (pr->zoom == 1.0 || pr->scale == 1.0) + { + list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE); + work = list; + while (work) + { + SourceTile *st; + gint rx, ry, rw, rh; + + st = work->data; + work = work->next; + + if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height, + it->x + x, it->y + y, w, h, + &rx, &ry, &rw, &rh)) + { + if (st->blank) + { + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + rx - st->x, ry - st->y, rw, rh); + } + else /* (pr->zoom == 1.0 || pr->scale == 1.0) */ + { + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + st->pixbuf, + rx - st->x, ry - st->y, + rx - it->x, ry - it->y, + rw, rh, + pr->dither_quality, rx, ry); + } + } + } + } + else + { + gdouble scale_x, scale_y; + gint sx, sy, sw, sh; + + if (pr->image_width == 0 || pr->image_height == 0) return FALSE; + scale_x = (gdouble)pr->width / pr->image_width; + scale_y = (gdouble)pr->height / pr->image_height; + + sx = (gdouble)(it->x + x) / scale_x; + sy = (gdouble)(it->y + y) / scale_y; + sw = (gdouble)w / scale_x; + sh = (gdouble)h / scale_y; + + if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; + +#if 0 + /* draws red over draw region, to check for leaks (regions not filled) */ + pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255); +#endif + + list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE); + work = list; + while (work) + { + SourceTile *st; + gint rx, ry, rw, rh; + gint stx, sty, stw, sth; + + st = work->data; + work = work->next; + + stx = floor((gdouble)st->x * scale_x); + sty = floor((gdouble)st->y * scale_y); + stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx; + sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty; + + if (pr_clip_region(stx, sty, stw, sth, + it->x + x, it->y + y, w, h, + &rx, &ry, &rw, &rh)) + { + if (st->blank) + { + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + rx - st->x, ry - st->y, rw, rh); + } + else + { + gdouble offset_x; + gdouble offset_y; + + /* may need to use unfloored stx,sty values here */ + offset_x = (gdouble)(stx - it->x); + offset_y = (gdouble)(sty - it->y); + + gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh, + (gdouble) 0.0 + offset_x, + (gdouble) 0.0 + offset_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); + draw = TRUE; + } + } + } + } + + g_list_free(list); + + return draw; +} + + +static void rt_tile_render(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + gboolean has_alpha; + gboolean draw = FALSE; + + if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return; + + if (it->render_done != TILE_RENDER_ALL) + { + x = 0; + y = 0; + w = it->w; + h = it->h; + if (!fast) it->render_done = TILE_RENDER_ALL; + } + else if (it->render_todo != TILE_RENDER_AREA) + { + if (!fast) it->render_todo = TILE_RENDER_NONE; + return; + } + + if (!fast) it->render_todo = TILE_RENDER_NONE; + + if (new_data) it->blank = FALSE; + + rt_tile_prepare(rt, it); + has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)); + + box = GTK_WIDGET(pr); + + /* FIXME checker colors for alpha should be configurable, + * also should be drawn for blank = TRUE + */ + + if (it->blank) + { + /* no data, do fast rect fill */ + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + 0, 0, it->w, it->h); + } + else if (pr->source_tiles_enabled) + { + draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast); + } + else if (pr->zoom == 1.0 || pr->scale == 1.0) + { + + gdouble src_x, src_y; + gint pb_x, pb_y; + gint pb_w, pb_h; + pr_tile_coords_map_orientation(pr->orientation, it->x, it->y, + pr->image_width, pr->image_height, + rt->tile_width, rt->tile_height, + &src_x, &src_y); + pr_tile_region_map_orientation(pr->orientation, x, y, + rt->tile_width, rt->tile_height, + w, h, + &pb_x, &pb_y, + &pb_w, &pb_h); + + if (has_alpha) + { + gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (gdouble) 0.0 - src_x, + (gdouble) 0.0 - src_y, + 1.0, 1.0, GDK_INTERP_NEAREST, + 255, it->x + pb_x, it->y + pb_y, + PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); + rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); + draw = TRUE; + } + else + { + + + if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast))) + { + /* faster, simple, base orientation, no postprocessing */ + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + pr->pixbuf, + it->x + x, it->y + y, + x, y, + w, h, + pr->dither_quality, it->x + x, it->y + y); + } + else + { + gdk_pixbuf_copy_area(pr->pixbuf, + src_x + pb_x, src_y + pb_y, + pb_w, pb_h, + it->pixbuf, + pb_x, pb_y); + rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); + draw = TRUE; + } + } + } + else + { + gdouble scale_x, scale_y; + gdouble src_x, src_y; + gint pb_x, pb_y; + gint pb_w, pb_h; + + if (pr->image_width == 0 || pr->image_height == 0) return; + + scale_x = (gdouble)pr->width / pr->image_width; + scale_y = (gdouble)pr->height / pr->image_height; + + pr_tile_coords_map_orientation(pr->orientation, it->x, it->y, + pr->image_width * scale_x, pr->image_height * scale_y, + rt->tile_width, rt->tile_height, + &src_x, &src_y); + pr_tile_region_map_orientation(pr->orientation, x, y, + rt->tile_width, rt->tile_height, + w, h, + &pb_x, &pb_y, + &pb_w, &pb_h); + switch (pr->orientation) + { + gdouble tmp; + case EXIF_ORIENTATION_LEFT_TOP: + case EXIF_ORIENTATION_RIGHT_TOP: + case EXIF_ORIENTATION_RIGHT_BOTTOM: + case EXIF_ORIENTATION_LEFT_BOTTOM: + tmp = scale_x; + scale_x = scale_y; + scale_y = tmp; + break; + default: + /* nothing to do */ + break; + } + + /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely + * small sizes for anything but GDK_INTERP_NEAREST + */ + if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; + + if (!has_alpha) + { + gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (gdouble) 0.0 - src_x, + (gdouble) 0.0 - src_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); + } + else + { + gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (gdouble) 0.0 - src_x, + (gdouble) 0.0 - src_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, + 255, it->x + pb_x, it->y + pb_y, + PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); + } + rt_tile_apply_orientation(rt, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); + draw = TRUE; + } + + if (draw && it->pixbuf && !it->blank) + { + + if (pr->func_post_process && !(pr->post_process_slow && fast)) + pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data); + + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixbuf, + x, y, + x, y, + w, h, + pr->dither_quality, it->x + x, it->y + y); + } + +#if 0 + /* enable this line for debugging the edges of tiles */ + gdk_draw_rectangle(it->pixmap, box->style->white_gc, + FALSE, 0, 0, it->w, it->h); + gdk_draw_rectangle(it->pixmap, box->style->white_gc, + FALSE, x, y, w, h); +#endif +} + + +static void rt_tile_expose(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + + rt_tile_render(rt, it, x, y, w, h, new_data, fast); + + box = GTK_WIDGET(pr); + +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixmap, x, y, + pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h); + + if (rt->overlay_list) + { + rt_overlay_draw(rt, pr->x_offset + (it->x - pr->x_scroll) + x, + pr->y_offset + (it->y - pr->y_scroll) + y, + w, h, + it); + } +} + + +static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width && + it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height); +} + +/* + *------------------------------------------------------------------- + * draw queue + *------------------------------------------------------------------- + */ + +static gint rt_get_queued_area(GList *work) +{ + gint area = 0; + + while (work) + { + QueueData *qd = work->data; + area += qd->w * qd->h; + work = work->next; + } + return area; +} + + +static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set) +{ + PixbufRenderer *pr = rt->pr; + gfloat percent; + gint visible_area = pr->vis_width * pr->vis_height; + + if (!pr->loading) + { + /* 2pass prio */ + DEBUG_2("redraw priority: 2pass"); + rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + if (visible_area == 0) + { + /* not known yet */ + percent = 100.0; + } + else + { + percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area; + } + + if (percent > 10.0) + { + /* we have enough data for starting intensive redrawing */ + DEBUG_2("redraw priority: high %.2f %%", percent); + rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + if (percent < 1.0 || force_set) + { + /* queue is (almost) empty, wait 50 ms*/ + DEBUG_2("redraw priority: wait %.2f %%", percent); + rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + /* keep the same priority as before */ + DEBUG_2("redraw priority: no change %.2f %%", percent); + return TRUE; +} + + +static gboolean rt_queue_draw_idle_cb(gpointer data) +{ + RendererTiles *rt = data; + PixbufRenderer *pr = rt->pr; + QueueData *qd; + gboolean fast; + + + if ((!pr->pixbuf && !pr->source_tiles_enabled) || + (!rt->draw_queue && !rt->draw_queue_2pass) || + !rt->draw_idle_id) + { + pr_render_complete_signal(pr); + + rt->draw_idle_id = 0; + return FALSE; + } + + if (rt->draw_queue) + { + qd = rt->draw_queue->data; + fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow)); + } + else + { + if (pr->loading) + { + /* still loading, wait till done (also drops the higher priority) */ + + return rt_queue_schedule_next_draw(rt, FALSE); + } + + qd = rt->draw_queue_2pass->data; + fast = FALSE; + } + +#if GTK_CHECK_VERSION(2,20,0) + if (gtk_widget_get_realized(pr)) +#else + if (GTK_WIDGET_REALIZED(pr)) +#endif + { + if (rt_tile_is_visible(rt, qd->it)) + { + rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); + } + else if (qd->new_data) + { + /* if new pixel data, and we already have a pixmap, update the tile */ + qd->it->blank = FALSE; + if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL) + { + rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); + } + } + } + + if (rt->draw_queue) + { + qd->it->qd = NULL; + rt->draw_queue = g_list_remove(rt->draw_queue, qd); + if (fast) + { + if (qd->it->qd2) + { + rt_queue_merge(qd->it->qd2, qd); + g_free(qd); + } + else + { + qd->it->qd2 = qd; + rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd); + } + } + else + { + g_free(qd); + } + } + else + { + qd->it->qd2 = NULL; + rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd); + g_free(qd); + } + + if (!rt->draw_queue && !rt->draw_queue_2pass) + { + pr_render_complete_signal(pr); + + rt->draw_idle_id = 0; + return FALSE; + } + + return rt_queue_schedule_next_draw(rt, FALSE); +} + +static void rt_queue_list_free(GList *list) +{ + GList *work; + + work = list; + while (work) + { + QueueData *qd; + + qd = work->data; + work = work->next; + + qd->it->qd = NULL; + qd->it->qd2 = NULL; + g_free(qd); + } + + g_list_free(list); +} + +static void rt_queue_clear(RendererTiles *rt) +{ + rt_queue_list_free(rt->draw_queue); + rt->draw_queue = NULL; + + rt_queue_list_free(rt->draw_queue_2pass); + rt->draw_queue_2pass = NULL; + + if (rt->draw_idle_id) + { + g_source_remove(rt->draw_idle_id); + rt->draw_idle_id = 0; + } +} + +static void rt_queue_merge(QueueData *parent, QueueData *qd) +{ + if (parent->x + parent->w < qd->x + qd->w) + { + parent->w += (qd->x + qd->w) - (parent->x + parent->w); + } + if (parent->x > qd->x) + { + parent->w += parent->x - qd->x; + parent->x = qd->x; + } + + if (parent->y + parent->h < qd->y + qd->h) + { + parent->h += (qd->y + qd->h) - (parent->y + parent->h); + } + if (parent->y > qd->y) + { + parent->h += parent->y - qd->y; + parent->y = qd->y; + } + + parent->new_data |= qd->new_data; +} + +static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h) +{ + PixbufRenderer *pr = rt->pr; + gint nx, ny; + gint nw, nh; + gint vx, vy; + gint vw, vh; + + vw = pr->vis_width; + vh = pr->vis_height; + + vx = pr->x_scroll; + vy = pr->y_scroll; + + if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE; + + /* now clamp it */ + nx = CLAMP(*x, vx, vx + vw); + nw = CLAMP(*w - (nx - *x), 1, vw); + + ny = CLAMP(*y, vy, vy + vh); + nh = CLAMP(*h - (ny - *y), 1, vh); + + *x = nx; + *y = ny; + *w = nw; + *h = nh; + + return TRUE; +} + +static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h, + gboolean clamp, ImageRenderType render, + gboolean new_data, gboolean only_existing) +{ + PixbufRenderer *pr = rt->pr; + gint i, j; + gint x1, x2; + gint y1, y2; + + if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE; + + x1 = ROUND_DOWN(x, rt->tile_width); + x2 = ROUND_UP(x + w, rt->tile_width); + + y1 = ROUND_DOWN(y, rt->tile_height); + y2 = ROUND_UP(y + h, rt->tile_height); + + for (j = y1; j <= y2; j += rt->tile_height) + { + for (i = x1; i <= x2; i += rt->tile_width) + { + ImageTile *it; + + it = rt_tile_get(rt, i, j, + (only_existing && + (i + rt->tile_width < pr->x_scroll || + i > pr->x_scroll + pr->vis_width || + j + rt->tile_height < pr->y_scroll || + j > pr->y_scroll + pr->vis_height))); + if (it) + { + QueueData *qd; + + if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) || + (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL)) + { + it->render_todo = render; + } + + qd = g_new(QueueData, 1); + qd->it = it; + qd->new_data = new_data; + + if (i < x) + { + qd->x = x - i; + } + else + { + qd->x = 0; + } + qd->w = x + w - i - qd->x; + if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x; + + if (j < y) + { + qd->y = y - j; + } + else + { + qd->y = 0; + } + qd->h = y + h - j - qd->y; + if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y; + + if (qd->w < 1 || qd->h < 1) + { + g_free(qd); + } + else if (it->qd) + { + rt_queue_merge(it->qd, qd); + g_free(qd); + } + else + { + it->qd = qd; + rt->draw_queue = g_list_append(rt->draw_queue, qd); + } + } + } + } + + return TRUE; +} + +static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h, + gboolean clamp, ImageRenderType render, + gboolean new_data, gboolean only_existing) +{ + PixbufRenderer *pr = rt->pr; + gint nx, ny; + + nx = CLAMP(x, 0, pr->width - 1); + ny = CLAMP(y, 0, pr->height - 1); + w -= (nx - x); + h -= (ny - y); + w = CLAMP(w, 0, pr->width - nx); + h = CLAMP(h, 0, pr->height - ny); + if (w < 1 || h < 1) return; + + if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) && + ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id)) + { + if (rt->draw_idle_id) + { + g_source_remove(rt->draw_idle_id); + rt->draw_idle_id = 0; + } + rt_queue_schedule_next_draw(rt, TRUE); + } +} + +static void rt_redraw(RendererTiles *rt, gboolean new_data) +{ + PixbufRenderer *pr = rt->pr; + rt_queue_clear(rt); + rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); +} + + +static void rt_scroll(RendererTiles *rt, gint x_off, gint y_off) +{ + PixbufRenderer *pr = rt->pr; + + gint w = pr->vis_width - abs(x_off); + gint h = pr->vis_height - abs(y_off); + + if (w < 1 || h < 1) + { + /* scrolled completely to new material */ + pr->renderer->queue(pr->renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + return; + } + else + { + gint x1, y1; + gint x2, y2; + GtkWidget *box; + GdkGC *gc; + GdkEvent *event; + + if (x_off < 0) + { + x1 = abs(x_off); + x2 = 0; + } + else + { + x1 = 0; + x2 = abs(x_off); + } + + if (y_off < 0) + { + y1 = abs(y_off); + y2 = 0; + } + else + { + y1 = 0; + y2 = abs(y_off); + } + + box = GTK_WIDGET(pr); + + gc = gdk_gc_new(box->window); + gdk_gc_set_exposures(gc, TRUE); + gdk_draw_drawable(box->window, gc, + box->window, + x2 + pr->x_offset, y2 + pr->y_offset, + x1 + pr->x_offset, y1 + pr->y_offset, w, h); + g_object_unref(gc); + + rt_overlay_queue_all(rt, x2, y2, x1, y1); + + w = pr->vis_width - w; + h = pr->vis_height - h; + + if (w > 0) + { + rt_queue(rt, + x_off > 0 ? pr->x_scroll + (pr->vis_width - w) : pr->x_scroll, pr->y_scroll, + w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + } + if (h > 0) + { + /* FIXME, to optimize this, remove overlap */ + rt_queue(rt, + pr->x_scroll, y_off > 0 ? pr->y_scroll + (pr->vis_height - h) : pr->y_scroll, + pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + } + + /* process exposures here, "expose_event" seems to miss a few with obstructed windows */ +#if ! GTK_CHECK_VERSION(2,18,0) + while ((event = gdk_event_get_graphics_expose(box->window)) != NULL) + { + pixbuf_renderer_paint(pr, &event->expose.area); + + if (event->expose.count == 0) + { + gdk_event_free(event); + break; + } + gdk_event_free(event); + } +#endif + } +} + +static void renderer_queue(void *renderer, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing) +{ + rt_queue((RendererTiles *)renderer, x, y, w, h, clamp, render, new_data, only_existing); +} + +static void renderer_queue_clear(void *renderer) +{ + rt_queue_clear((RendererTiles *)renderer); +} + +static void renderer_border_draw(void *renderer, gint x, gint y, gint w, gint h) +{ + rt_border_draw((RendererTiles *)renderer, x, y, w, h); +} + + +static void renderer_invalidate_all(void *renderer) +{ + rt_tile_invalidate_all((RendererTiles *)renderer); +} + +static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h) +{ + rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h); +} + +static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h) +{ + rt_overlay_draw((RendererTiles *)renderer, x, y, w, h, NULL); +} + +static void renderer_free(void *renderer) +{ + RendererTiles *rt = (RendererTiles *)renderer; + rt_queue_clear(rt); + rt_tile_free_all(rt); + if (rt->spare_tile) g_object_unref(rt->spare_tile); + if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer); + rt_overlay_list_clear(rt); + /* disconnect "hierarchy-changed" */ + g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA, + 0, 0, 0, NULL, rt); + g_free(rt); +} + +RendererFuncs *renderer_tiles_new(PixbufRenderer *pr) +{ + RendererTiles *rt = g_new0(RendererTiles, 1); + + rt->pr = pr; + + rt->f.queue = renderer_queue; + rt->f.queue_clear = renderer_queue_clear; + rt->f.border_draw = renderer_border_draw; + rt->f.free = renderer_free; + rt->f.invalidate_all = renderer_invalidate_all; + rt->f.invalidate_region = renderer_invalidate_region; + rt->f.scroll = rt_scroll; + + + rt->f.overlay_add = renderer_tiles_overlay_add; + rt->f.overlay_set = renderer_tiles_overlay_set; + rt->f.overlay_get = renderer_tiles_overlay_get; + rt->f.overlay_update_sizes = rt_overlay_update_sizes; + rt->f.overlay_draw = renderer_overlay_draw; + + rt->tile_width = PR_TILE_SIZE; + rt->tile_height = PR_TILE_SIZE; + + rt->tiles = NULL; + rt->tile_cache_size = 0; + + rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT; + + rt->draw_idle_id = 0; + + g_signal_connect(G_OBJECT(pr), "hierarchy-changed", + G_CALLBACK(rt_hierarchy_changed_cb), rt); + + return (RendererFuncs *) rt; +} + + +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/renderer-tiles.h b/src/renderer-tiles.h new file mode 100644 index 00000000..168023b2 --- /dev/null +++ b/src/renderer-tiles.h @@ -0,0 +1,25 @@ +/* + * Geeqie + * (C) 2006 John Ellis + * Copyright (C) 2008 - 2010 The Geeqie Team + * + * Author: John Ellis + * + * This software is released under the GNU General Public License (GNU GPL). + * Please read the included file COPYING for more information. + * This software comes with no warranty of any kind, use at your own risk! + */ + +#ifndef RENDERER_TILES_H +#define RENDERER_TILES_H + +#include +#include + +#include + + +RendererFuncs *renderer_tiles_new(PixbufRenderer *pr); + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ -- 2.20.1