X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fpixbuf-renderer.c;h=07de0e7d36e64ad52a55e1885b266e91ffbcc4bf;hb=67d93b5d662ac97d6dd2c421387cd28330e404a1;hp=0f2198a1390cb3be0ff498bbff391197887b7c02;hpb=5f4a945ac78df503f9765056e946c68905ed0a32;p=geeqie.git diff --git a/src/pixbuf-renderer.c b/src/pixbuf-renderer.c index 0f2198a1..07de0e7d 100644 --- a/src/pixbuf-renderer.c +++ b/src/pixbuf-renderer.c @@ -1,13 +1,22 @@ /* - * Geeqie - * (C) 2006 John Ellis - * Copyright (C) 2008 The Geeqie Team + * Copyright (C) 2006 John Ellis + * Copyright (C) 2008 - 2016 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! + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include @@ -15,12 +24,16 @@ #include #include +#include "main.h" #include "pixbuf-renderer.h" +#include "renderer-tiles.h" +#include "renderer-clutter.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 @@ -46,12 +59,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 @@ -66,91 +73,18 @@ 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 -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, SIGNAL_SCROLL_NOTIFY, SIGNAL_RENDER_COMPLETE, SIGNAL_DRAG, + SIGNAL_UPDATE_PIXEL, SIGNAL_COUNT }; @@ -161,7 +95,6 @@ enum { PROP_ZOOM_QUALITY, PROP_ZOOM_2PASS, PROP_ZOOM_EXPAND, - PROP_DITHER_QUALITY, PROP_SCROLL_RESET, PROP_DELAY_FLIP, PROP_LOADING, @@ -172,7 +105,8 @@ enum { PROP_WINDOW_LIMIT, PROP_WINDOW_LIMIT_VALUE, PROP_AUTOFIT_LIMIT, - PROP_AUTOFIT_LIMIT_VALUE + PROP_AUTOFIT_LIMIT_VALUE, + PROP_ENLARGEMENT_LIMIT_VALUE }; typedef enum { @@ -181,6 +115,7 @@ typedef enum { PR_ZOOM_NEW = 1 << 1, PR_ZOOM_CENTER = 1 << 2, PR_ZOOM_INVALIDATE = 1 << 3, + PR_ZOOM_LAZY = 1 << 4 /* wait with redraw for pixbuf_renderer_area_changed */ } PrZoomFlags; static guint signals[SIGNAL_COUNT] = { 0 }; @@ -195,32 +130,17 @@ static void pixbuf_renderer_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void pixbuf_renderer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); -static gint 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, gint start); -static void pr_border_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h); +static void pr_scroller_timer_set(PixbufRenderer *pr, gboolean start); 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 gint 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, gint new_data, gint only_existing); - -static void pr_redraw(PixbufRenderer *pr, gint 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 pixbuf_renderer_paint(PixbufRenderer *pr, GdkRectangle *area); +static void pr_stereo_temp_disable(PixbufRenderer *pr, gboolean disable); /* @@ -259,7 +179,6 @@ GType pixbuf_renderer_get_type(void) static void pixbuf_renderer_class_init(PixbufRendererClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS(class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class); parent_class = g_type_class_peek_parent(class); @@ -268,8 +187,6 @@ static void pixbuf_renderer_class_init(PixbufRendererClass *class) gobject_class->finalize = pixbuf_renderer_finalize; - widget_class->expose_event = pixbuf_renderer_expose; - g_object_class_install_property(gobject_class, PROP_ZOOM_MIN, g_param_spec_double("zoom_min", @@ -315,17 +232,6 @@ static void pixbuf_renderer_class_init(PixbufRendererClass *class) NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); - - g_object_class_install_property(gobject_class, - PROP_DITHER_QUALITY, - g_param_spec_uint("dither_quality", - "Dither quality", - NULL, - GDK_RGB_DITHER_NONE, - GDK_RGB_DITHER_MAX, - GDK_RGB_DITHER_NORMAL, - G_PARAM_READABLE | G_PARAM_WRITABLE)); - g_object_class_install_property(gobject_class, PROP_SCROLL_RESET, g_param_spec_uint("scroll_reset", @@ -424,6 +330,16 @@ static void pixbuf_renderer_class_init(PixbufRendererClass *class) 100, G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property(gobject_class, + PROP_ENLARGEMENT_LIMIT_VALUE, + g_param_spec_uint("enlargement_limit_value", + "Size increase limit of image when autofitting", + NULL, + 100, + 999, + 500, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + signals[SIGNAL_ZOOM] = g_signal_new("zoom", @@ -472,8 +388,31 @@ static void pixbuf_renderer_class_init(PixbufRendererClass *class) g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GDK_TYPE_EVENT); + + signals[SIGNAL_UPDATE_PIXEL] = + g_signal_new("update-pixel", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PixbufRendererClass, update_pixel), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static RendererFuncs *pr_backend_renderer_new(PixbufRenderer *pr) +{ + if (options->image.use_clutter_renderer) + { +#ifdef HAVE_CLUTTER + return renderer_clutter_new(pr); +#else + DEBUG_0("Geeqie is built without clutter renderer support"); +#endif + } + return renderer_tiles_new(pr); } + static void pixbuf_renderer_init(PixbufRenderer *pr) { GtkWidget *box; @@ -487,30 +426,36 @@ static void pixbuf_renderer_init(PixbufRenderer *pr) pr->zoom = 1.0; pr->scale = 1.0; - - pr->dither_quality = GDK_RGB_DITHER_NORMAL; + pr->aspect_ratio = 1.0; pr->scroll_reset = PR_SCROLL_RESET_TOPLEFT; - pr->draw_idle_id = -1; - - 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 = -1; + pr->scroller_id = 0; pr->scroller_overlay = -1; + pr->x_mouse = -1; + pr->y_mouse = -1; + pr->source_tiles_enabled = FALSE; pr->source_tiles = NULL; pr->orientation = 1; + pr->norm_center_x = 0.5; + pr->norm_center_y = 0.5; + + pr->stereo_mode = PR_STEREO_NONE; + + pr->color.red =0; + pr->color.green =0; + pr->color.blue =0; + + pr->renderer = pr_backend_renderer_new(pr); + + pr->renderer2 = NULL; + gtk_widget_set_double_buffered(box, FALSE); + gtk_widget_set_app_paintable(box, TRUE); g_signal_connect_after(G_OBJECT(box), "size_allocate", G_CALLBACK(pr_size_cb), pr); @@ -523,19 +468,18 @@ 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->renderer2) pr->renderer2->free(pr->renderer2); + if (pr->pixbuf) g_object_unref(pr->pixbuf); - if (pr->spare_tile) gdk_pixbuf_unref(pr->spare_tile); pr_scroller_timer_set(pr, FALSE); - pr_overlay_list_clear(pr); pr_source_tile_free_all(pr); } -PixbufRenderer* pixbuf_renderer_new(void) +PixbufRenderer *pixbuf_renderer_new(void) { return g_object_new(TYPE_PIXBUF_RENDERER, NULL); } @@ -564,9 +508,6 @@ static void pixbuf_renderer_set_property(GObject *object, guint prop_id, case PROP_ZOOM_EXPAND: pr->zoom_expand = g_value_get_boolean(value); break; - case PROP_DITHER_QUALITY: - pr->dither_quality = g_value_get_uint(value); - break; case PROP_SCROLL_RESET: pr->scroll_reset = g_value_get_uint(value); break; @@ -580,7 +521,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); @@ -600,6 +541,9 @@ static void pixbuf_renderer_set_property(GObject *object, guint prop_id, case PROP_AUTOFIT_LIMIT_VALUE: pr->autofit_limit_size = g_value_get_uint(value); break; + case PROP_ENLARGEMENT_LIMIT_VALUE: + pr->enlargement_limit_size = g_value_get_uint(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -630,9 +574,6 @@ static void pixbuf_renderer_get_property(GObject *object, guint prop_id, case PROP_ZOOM_EXPAND: g_value_set_boolean(value, pr->zoom_expand); break; - case PROP_DITHER_QUALITY: - g_value_set_uint(value, pr->dither_quality); - break; case PROP_SCROLL_RESET: g_value_set_uint(value, pr->scroll_reset); break; @@ -646,7 +587,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); @@ -666,39 +607,15 @@ static void pixbuf_renderer_get_property(GObject *object, guint prop_id, case PROP_AUTOFIT_LIMIT_VALUE: g_value_set_uint(value, pr->autofit_limit_size); break; + case PROP_ENLARGEMENT_LIMIT_VALUE: + g_value_set_uint(value, pr->enlargement_limit_size); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } -static gint pixbuf_renderer_expose(GtkWidget *widget, GdkEventExpose *event) -{ - if (GTK_WIDGET_DRAWABLE(widget)) - { - if (!GTK_WIDGET_NO_WINDOW(widget)) - { - if (event->window != widget->window) - { - GdkRectangle area; - - gdk_window_get_position(event->window, &area.x, &area.y); - - area.x += event->area.x; - area.y += event->area.y; - area.width = event->area.width; - area.height = event->area.height; - pixbuf_renderer_paint(PIXBUF_RENDERER(widget), &area); - } - else - { - pixbuf_renderer_paint(PIXBUF_RENDERER(widget), &event->area); - } - } - } - - return FALSE; -} /* *------------------------------------------------------------------- @@ -710,7 +627,7 @@ static void widget_set_cursor(GtkWidget *widget, gint icon) { GdkCursor *cursor; - if (!widget->window) return; + if (!gtk_widget_get_window(widget)) return; if (icon == -1) { @@ -721,23 +638,14 @@ static void widget_set_cursor(GtkWidget *widget, gint icon) cursor = gdk_cursor_new(icon); } - gdk_window_set_cursor(widget->window, cursor); + gdk_window_set_cursor(gtk_widget_get_window(widget), cursor); 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 gint 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) +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) { if (clip_x + clip_w <= x || clip_x >= x + w || @@ -756,26 +664,28 @@ static gint pr_clip_region(gint x, gint y, gint w, gint h, return TRUE; } -static gint pr_parent_window_sizable(PixbufRenderer *pr) +static gboolean pr_parent_window_sizable(PixbufRenderer *pr) { GdkWindowState state; if (!pr->parent_window) return FALSE; if (!pr->window_fit) return FALSE; - if (!GTK_WIDGET(pr)->window) return FALSE; + if (!gtk_widget_get_window(GTK_WIDGET(pr))) return FALSE; - if (!pr->parent_window->window) return FALSE; - state = gdk_window_get_state(pr->parent_window->window); + if (!gtk_widget_get_window(pr->parent_window)) return FALSE; + state = gdk_window_get_state(gtk_widget_get_window(pr->parent_window)); if (state & GDK_WINDOW_STATE_MAXIMIZED) return FALSE; return TRUE; } -static gint pr_parent_window_resize(PixbufRenderer *pr, gint w, gint h) +static gboolean pr_parent_window_resize(PixbufRenderer *pr, gint w, gint h) { GtkWidget *widget; GtkWidget *parent; gint ww, wh; + GtkAllocation widget_allocation; + GtkAllocation parent_allocation; if (!pr_parent_window_sizable(pr)) return FALSE; @@ -791,13 +701,17 @@ static gint pr_parent_window_resize(PixbufRenderer *pr, gint w, gint h) widget = GTK_WIDGET(pr); parent = GTK_WIDGET(pr->parent_window); - w += (parent->allocation.width - widget->allocation.width); - h += (parent->allocation.height - widget->allocation.height); + gtk_widget_get_allocation(widget, &widget_allocation); + gtk_widget_get_allocation(parent, &parent_allocation); - gdk_drawable_get_size(parent->window, &ww, &wh); + w += (parent_allocation.width - widget_allocation.width); + h += (parent_allocation.height - widget_allocation.height); + + ww = gdk_window_get_width(gtk_widget_get_window(parent)); + wh = gdk_window_get_height(gtk_widget_get_window(parent)); if (w == ww && h == wh) return FALSE; - gdk_window_resize(parent->window, w, h); + gdk_window_resize(gtk_widget_get_window(parent), w, h); return TRUE; } @@ -824,289 +738,31 @@ 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_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; - - 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) - { - gdk_draw_drawable(pr->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - od->pixbuf, - rx - px, ry - py, - 0, 0, rw, rh, - pr->dither_quality, rx, ry); - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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, - box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE, - 0, 0, sw, sh); - gdk_draw_pixbuf(pr->overlay_buffer, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - od->pixbuf, - sx - px, sy - py, - 0, 0, sw, sh, - pr->dither_quality, sx, sy); - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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) continue; - - 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; - gint px, py, pw, ph; - GdkWindowAttr attributes; - gint attributes_mask; - - 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_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); - - 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); - } + /* let's assume both renderers returns the same value */ + if (pr->renderer2) pr->renderer2->overlay_add(pr->renderer2, pixbuf, x, y, flags); + 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; - - 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); + if (pr->renderer2) pr->renderer2->overlay_set(pr->renderer2, id, pixbuf, x, y); } -gint pixbuf_renderer_overlay_get(PixbufRenderer *pr, gint id, GdkPixbuf **pixbuf, gint *x, gint *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; + if (pr->renderer2) pr->renderer2->overlay_get(pr->renderer2, id, pixbuf, x, y); + 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); + pr->renderer->overlay_set(pr->renderer, id, NULL, 0, 0); + if (pr->renderer2) pr->renderer2->overlay_set(pr->renderer2, id, NULL, 0, 0); } /* @@ -1203,12 +859,12 @@ static gboolean pr_scroller_update_cb(gpointer data) return TRUE; } -static void pr_scroller_timer_set(PixbufRenderer *pr, gint start) +static void pr_scroller_timer_set(PixbufRenderer *pr, gboolean start) { - if (pr->scroller_id != -1) + if (pr->scroller_id) { g_source_remove(pr->scroller_id); - pr->scroller_id = -1; + pr->scroller_id = 0; } if (start) @@ -1248,7 +904,7 @@ static void pr_scroller_start(PixbufRenderer *pr, gint x, gint y) static void pr_scroller_stop(PixbufRenderer *pr) { - if (pr->scroller_id == -1) return; + if (!pr->scroller_id) return; pixbuf_renderer_overlay_remove(pr, pr->scroller_overlay); pr->scroller_overlay = -1; @@ -1262,77 +918,6 @@ 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); -} - void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color) { GtkStyle *style; @@ -1340,28 +925,23 @@ void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color) g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - widget = GTK_WIDGET(pr); - - if (color) { - GdkColor *slot; - - style = gtk_style_copy(gtk_widget_get_style(widget)); - slot = &style->bg[GTK_STATE_NORMAL]; - - slot->red = color->red; - slot->green = color->green; - slot->blue = color->blue; + if (color) + { + pr->color.red = color->red; + pr->color.green = color->green; + pr->color.blue = color->blue; + } + else + { + pr->color.red = 0; + pr->color.green = 0; + pr->color.blue = 0; } - else { - style = gtk_style_copy(gtk_widget_get_default_style()); - } - - gtk_widget_set_style(widget, style); - if (GTK_WIDGET_VISIBLE(widget)) pr_border_clear(pr); + pr->renderer->update_viewport(pr->renderer); + if (pr->renderer2) pr->renderer2->update_viewport(pr->renderer2); } - /* *------------------------------------------------------------------- * source tiles @@ -1401,21 +981,25 @@ static void pr_source_tile_unset(PixbufRenderer *pr) pr->source_tiles_enabled = FALSE; } -static gint pr_source_tile_visible(PixbufRenderer *pr, SourceTile *st) +static gboolean pr_source_tile_visible(PixbufRenderer *pr, SourceTile *st) { gint x1, y1, x2, y2; if (!st) return FALSE; - x1 = (pr->x_scroll / pr->tile_width) * pr->tile_width; - y1 = (pr->y_scroll / pr->tile_height) * pr->tile_height; - x2 = ((pr->x_scroll + pr->vis_width) / pr->tile_width) * pr->tile_width + pr->tile_width; - y2 = ((pr->y_scroll + pr->vis_height) / pr->tile_height) * pr->tile_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 !((double)st->x * pr->scale > (double)x2 || - (double)(st->x + pr->source_tile_width) * pr->scale < (double)x1 || - (double)st->y * pr->scale > (double)y2 || - (double)(st->y + pr->source_tile_height) * pr->scale < (double)y1); + return !((gdouble)st->x * pr->scale > (gdouble)x2 || + (gdouble)(st->x + pr->source_tile_width) * pr->scale < (gdouble)x1 || + (gdouble)st->y * pr->scale > (gdouble)y2 || + (gdouble)(st->y + pr->source_tile_height) * pr->scale < (gdouble)y1); } static SourceTile *pr_source_tile_new(PixbufRenderer *pr, gint x, gint y) @@ -1472,8 +1056,8 @@ static SourceTile *pr_source_tile_new(PixbufRenderer *pr, gint x, gint y) pr->source_tile_width, pr->source_tile_height); } - st->x = (x / pr->source_tile_width) * pr->source_tile_width; - st->y = (y / pr->source_tile_height) * pr->source_tile_height; + st->x = ROUND_DOWN(x, pr->source_tile_width); + st->y = ROUND_DOWN(y, pr->source_tile_height); st->blank = TRUE; pr->source_tiles = g_list_prepend(pr->source_tiles, st); @@ -1495,9 +1079,10 @@ 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); + if (pr->renderer2) pr->renderer2->invalidate_region(pr->renderer2, st->x * pr->scale, st->y * pr->scale, pr->source_tile_width * pr->scale, pr->source_tile_height * pr->scale); - return st; } @@ -1527,7 +1112,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, gint 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; @@ -1538,8 +1123,8 @@ static GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, if (w > pr->image_width) w = pr->image_width; if (h > pr->image_height) h = pr->image_height; - sx = (x / pr->source_tile_width) * pr->source_tile_width; - sy = (y / pr->source_tile_height) * pr->source_tile_height; + sx = ROUND_DOWN(x, pr->source_tile_width); + sy = ROUND_DOWN(y, pr->source_tile_height); for (x1 = sx; x1 < x + w; x1+= pr->source_tile_width) { @@ -1582,130 +1167,16 @@ 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); + if (pr->renderer2) pr->renderer2->invalidate_region(pr->renderer2, rx * pr->scale, ry * pr->scale, + rw * pr->scale, rh * pr->scale); } g_object_unref(pixbuf); } } } -static gint pr_source_tile_render(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gint new_data, gint fast) -{ - GtkWidget *box; - GList *list; - GList *work; - gint 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, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - st->pixbuf, - rx - st->x, ry - st->y, - rx - it->x, ry - it->y, - rw, rh, - pr->dither_quality, rx, ry); - } - } - } - } - else - { - double scale_x, scale_y; - gint sx, sy, sw, sh; - - if (pr->image_width == 0 || pr->image_height == 0) return FALSE; - scale_x = (double)pr->width / pr->image_width; - scale_y = (double)pr->height / pr->image_height; - - sx = (double)(it->x + x) / scale_x; - sy = (double)(it->y + y) / scale_y; - sw = (double)w / scale_x; - sh = (double)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((double)st->x * scale_x); - sty = floor((double)st->y * scale_y); - stw = ceil((double)(st->x + pr->source_tile_width) * scale_x) - stx; - sth = ceil((double)(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 - { - double offset_x; - double offset_y; - - /* may need to use unfloored stx,sty values here */ - offset_x = (double)(stx - it->x); - offset_y = (double)(sty - it->y); - - gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh, - (double) 0.0 + offset_x, - (double) 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, @@ -1714,7 +1185,7 @@ void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height, gdouble zoom) { g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - g_return_if_fail(tile_width >= 32 && tile_width >= 32); + g_return_if_fail(tile_width >= 32 && tile_height >= 32); g_return_if_fail(width >= 32 && height > 32); g_return_if_fail(func_request != NULL); @@ -1737,8 +1208,8 @@ void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height, pr->func_tile_dispose = func_dispose; pr->func_tile_data = user_data; + pr_stereo_temp_disable(pr, TRUE); pr_zoom_sync(pr, zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); - pr_redraw(pr, TRUE); } void pixbuf_renderer_set_tiles_size(PixbufRenderer *pr, gint width, gint height) @@ -1807,267 +1278,74 @@ static void pr_zoom_adjust_real(PixbufRenderer *pr, gdouble increment, pr_zoom_sync(pr, zoom, flags, x, y); } + /* *------------------------------------------------------------------- - * display tiles + * signal emission *------------------------------------------------------------------- */ -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) gdk_pixbuf_unref(it->pixbuf); - if (it->pixmap) g_object_unref(it->pixmap); - - g_free(it); -} - -static void pr_tile_free_all(PixbufRenderer *pr) +static void pr_update_signal(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; + DEBUG_1("%s pixbuf renderer updated - started drawing %p, img: %dx%d", get_exec_time(), pr, pr->image_width, pr->image_height); + pr->debug_updated = TRUE; } -static ImageTile *pr_tile_add(PixbufRenderer *pr, gint x, gint y) +static void pr_zoom_signal(PixbufRenderer *pr) { - 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; + g_signal_emit(pr, signals[SIGNAL_ZOOM], 0, pr->zoom); } -static void pr_tile_remove(PixbufRenderer *pr, ImageTile *it) +static void pr_clicked_signal(PixbufRenderer *pr, GdkEventButton *bevent) { - 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); + g_signal_emit(pr, signals[SIGNAL_CLICKED], 0, bevent); } -static void pr_tile_free_space(PixbufRenderer *pr, guint space, ImageTile *it) +static void pr_scroll_notify_signal(PixbufRenderer *pr) { - 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)((double)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); - } + g_signal_emit(pr, signals[SIGNAL_SCROLL_NOTIFY], 0); } -static void pr_tile_invalidate_all(PixbufRenderer *pr) +void pr_render_complete_signal(PixbufRenderer *pr) { - GList *work; - - work = pr->tiles; - while (work) + if (!pr->complete) { - 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); + g_signal_emit(pr, signals[SIGNAL_RENDER_COMPLETE], 0); + g_object_set(G_OBJECT(pr), "complete", TRUE, NULL); } -} - -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 = (gint)floor(x / pr->tile_width) * pr->tile_width; - x2 = (gint)ceil((x + w) / pr->tile_width) * pr->tile_width; - - y1 = (gint)floor(y / pr->tile_height) * pr->tile_height; - y2 = (gint)ceil((y + h) / pr->tile_height) * pr->tile_height; - - work = pr->tiles; - while (work) + if (pr->debug_updated) { - 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; - } + DEBUG_1("%s pixbuf renderer done %p", get_exec_time(), pr); + pr->debug_updated = FALSE; } } -static ImageTile *pr_tile_get(PixbufRenderer *pr, gint x, gint y, gint only_existing) +static void pr_drag_signal(PixbufRenderer *pr, GdkEventMotion *event) { - 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); + g_signal_emit(pr, signals[SIGNAL_DRAG], 0, event); } -static void pr_tile_prepare(PixbufRenderer *pr, ImageTile *it) +static void pr_update_pixel_signal(PixbufRenderer *pr) { - 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; - } + g_signal_emit(pr, signals[SIGNAL_UPDATE_PIXEL], 0); } /* *------------------------------------------------------------------- - * drawing + * sync and clamp *------------------------------------------------------------------- */ -static void pr_tile_coords_map_orientation(PixbufRenderer *pr, - double tile_x, double tile_y, /* coordinates of the tile */ - gint image_w, gint image_h, - double tile_w, double tile_h, - double *res_x, double *res_y) +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 (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2110,7 +1388,7 @@ static void pr_tile_coords_map_orientation(PixbufRenderer *pr, // 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, +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, @@ -2122,7 +1400,7 @@ static void pr_tile_region_map_orientation(PixbufRenderer *pr, *res_w = area_w; *res_h = area_h; - switch (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2173,7 +1451,7 @@ static void pr_tile_region_map_orientation(PixbufRenderer *pr, // 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, +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, @@ -2185,7 +1463,7 @@ static void pr_coords_map_orientation_reverse(PixbufRenderer *pr, *res_w = area_w; *res_h = area_h; - switch (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2236,885 +1514,112 @@ static void pr_coords_map_orientation_reverse(PixbufRenderer *pr, } -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) +static void pixbuf_renderer_sync_scroll_center(PixbufRenderer *pr) { - 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); + gint src_x, src_y; + if (!pr->width || !pr->height) return; - 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; + /* + * Update norm_center only if the image is bigger than the window. + * With this condition the stored center survives also a temporary display + * of the "broken image" icon. + */ - for (i = y; i < y + h; i++) + if (pr->width > pr->viewport_width) { - 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; - } + src_x = pr->x_scroll + pr->vis_width / 2; + pr->norm_center_x = (gdouble)src_x / pr->width; } - pr->spare_tile = src; - *tile = dest; + if (pr->height > pr->viewport_height) + { + src_y = pr->y_scroll + pr->vis_height / 2; + pr->norm_center_y = (gdouble)src_y / pr->height; + } } -static void pr_tile_rotate_90_counter_clockwise(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) + +static gboolean pr_scroll_clamp(PixbufRenderer *pr) { - 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; + gint old_xs; + gint old_ys; - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); + if (pr->zoom == 0.0) + { + pr->x_scroll = 0; + pr->y_scroll = 0; - 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; + return FALSE; + } - for (i = y; i < y + h; i++) + old_xs = pr->x_scroll; + old_ys = pr->y_scroll; + + if (pr->x_offset > 0) { - 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->x_scroll = 0; + } + else + { + pr->x_scroll = CLAMP(pr->x_scroll, 0, pr->width - pr->vis_width); } - pr->spare_tile = src; - *tile = dest; + if (pr->y_offset > 0) + { + pr->y_scroll = 0; + } + else + { + pr->y_scroll = CLAMP(pr->y_scroll, 0, pr->height - pr->vis_height); + } + + pixbuf_renderer_sync_scroll_center(pr); + + return (old_xs != pr->x_scroll || old_ys != pr->y_scroll); } -static void pr_tile_mirror_only(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +static gboolean pr_size_clamp(PixbufRenderer *pr) { - 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 old_vw, old_vh; - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); + old_vw = pr->vis_width; + old_vh = pr->vis_height; - 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; + if (pr->width < pr->viewport_width) + { + pr->vis_width = pr->width; + pr->x_offset = (pr->viewport_width - pr->width) / 2; + } + else + { + pr->vis_width = pr->viewport_width; + pr->x_offset = 0; + } - for (i = y; i < y + h; i++) + if (pr->height < pr->viewport_height) { - 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->vis_height = pr->height; + pr->y_offset = (pr->viewport_height - pr->height) / 2; + } + else + { + pr->vis_height = pr->viewport_height; + pr->y_offset = 0; } - pr->spare_tile = src; - *tile = dest; + pixbuf_renderer_sync_scroll_center(pr); + + return (old_vw != pr->vis_width || old_vh != pr->vis_height); } -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, - gint new_data, gint fast) -{ - GtkWidget *box; - gint has_alpha; - gint 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) - { - - double 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, - (double) 0.0 - src_x, - (double) 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, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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 - { - double scale_x, scale_y; - double 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 = (double)pr->width / pr->image_width; - scale_y = (double)pr->height / pr->image_height; - - pr_tile_coords_map_orientation(pr, it->x / scale_x, it->y /scale_y , - pr->image_width, pr->image_height, - pr->tile_width / scale_x , pr->tile_height / scale_y, - &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); - - /* 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, - (double) 0.0 - src_x * scale_x, - (double) 0.0 - src_y * scale_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, - (double) 0.0 - src_x * scale_x, - (double) 0.0 - src_y * scale_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, - box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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, - gint new_data, gint fast) -{ - GtkWidget *box; - - pr_tile_render(pr, it, x, y, w, h, new_data, fast); - - box = GTK_WIDGET(pr); - - gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], - 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 gint 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_queue_draw_idle_cb(gpointer data) -{ - PixbufRenderer *pr = data; - QueueData *qd; - gint fast; - - if ((!pr->pixbuf && !pr->source_tiles_enabled) || - (!pr->draw_queue && !pr->draw_queue_2pass) || - pr->draw_idle_id == -1) - { - pr_render_complete_signal(pr); - - pr->draw_idle_id = -1; - 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) */ - - pr->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, - pr_queue_draw_idle_cb, pr, NULL); - pr->draw_idle_high = FALSE; - return FALSE; - } - - qd = pr->draw_queue_2pass->data; - fast = FALSE; - } - - if (GTK_WIDGET_REALIZED(pr)) - { - 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 = -1; - return FALSE; - } - - return TRUE; -} - -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 != -1) g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = -1; -} - -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 gint 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 gint pr_queue_to_tiles(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gint clamp, ImageTileRenderType render, gint new_data, gint 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 = (gint)floor(x / pr->tile_width) * pr->tile_width; - x2 = (gint)ceil((x + w) / pr->tile_width) * pr->tile_width; - - y1 = (gint)floor(y / pr->tile_height) * pr->tile_height; - y2 = (gint)ceil((y + h) / pr->tile_height) * 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); - } - } - } - } - - return TRUE; -} - -static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gint clamp, ImageTileRenderType render, gint new_data, gint 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 == -1 || !pr->draw_idle_high)) - { - if (pr->draw_idle_id != -1) g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, - pr_queue_draw_idle_cb, pr, NULL); - pr->draw_idle_high = TRUE; - } -} - -static void pr_redraw(PixbufRenderer *pr, gint new_data) -{ - pr_queue_clear(pr); - pr_queue(pr, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); -} - -/* - *------------------------------------------------------------------- - * signal emission - *------------------------------------------------------------------- - */ - -static void pr_update_signal(PixbufRenderer *pr) -{ -#if 0 - log_printf("FIXME: send updated signal\n"); -#endif - DEBUG_1("%s pixbuf renderer updated - started drawing %p", get_exec_time(), pr); - pr->debug_updated = TRUE; -} - -static void pr_zoom_signal(PixbufRenderer *pr) -{ - g_signal_emit(pr, signals[SIGNAL_ZOOM], 0, pr->zoom); -} - -static void pr_clicked_signal(PixbufRenderer *pr, GdkEventButton *bevent) -{ - g_signal_emit(pr, signals[SIGNAL_CLICKED], 0, bevent); -} - -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) -{ - if (!pr->complete) - { - g_signal_emit(pr, signals[SIGNAL_RENDER_COMPLETE], 0); - g_object_set(G_OBJECT(pr), "complete", TRUE, NULL); - } - if (pr->debug_updated) - { - DEBUG_1("%s pixbuf renderer done %p", get_exec_time(), pr); - pr->debug_updated = FALSE; - } -} - -static void pr_drag_signal(PixbufRenderer *pr, GdkEventButton *bevent) -{ - g_signal_emit(pr, signals[SIGNAL_DRAG], 0, bevent); -} - -/* - *------------------------------------------------------------------- - * sync and clamp - *------------------------------------------------------------------- - */ - -static gint pr_scroll_clamp(PixbufRenderer *pr) -{ - gint old_xs; - gint old_ys; - - if (pr->zoom == 0.0) - { - pr->x_scroll = 0; - pr->y_scroll = 0; - - return FALSE; - } - - old_xs = pr->x_scroll; - old_ys = pr->y_scroll; - - if (pr->x_offset > 0) - { - pr->x_scroll = 0; - } - else - { - pr->x_scroll = CLAMP(pr->x_scroll, 0, pr->width - pr->vis_width); - } - - if (pr->y_offset > 0) - { - pr->y_scroll = 0; - } - else - { - pr->y_scroll = CLAMP(pr->y_scroll, 0, pr->height - pr->vis_height); - } - - return (old_xs != pr->x_scroll || old_ys != pr->y_scroll); -} - -static gint pr_size_clamp(PixbufRenderer *pr) -{ - gint old_vw, old_vh; - - old_vw = pr->vis_width; - old_vh = pr->vis_height; - - if (pr->width < pr->window_width) - { - pr->vis_width = pr->width; - pr->x_offset = (pr->window_width - pr->width) / 2; - } - else - { - pr->vis_width = pr->window_width; - pr->x_offset = 0; - } - - if (pr->height < pr->window_height) - { - pr->vis_height = pr->height; - pr->y_offset = (pr->window_height - pr->height) / 2; - } - else - { - pr->vis_height = pr->window_height; - pr->y_offset = 0; - } - - return (old_vw != pr->vis_width || old_vh != pr->vis_height); -} - -static gint pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, - PrZoomFlags flags, gboolean *redrawn) +static gboolean pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, + PrZoomFlags flags) { gint w, h; gdouble scale; - gboolean invalid; gboolean force = !!(flags & PR_ZOOM_FORCE); gboolean new = !!(flags & PR_ZOOM_NEW); - gboolean invalidate = !!(flags & PR_ZOOM_INVALIDATE); zoom = CLAMP(zoom, pr->zoom_min, pr->zoom_max); @@ -3148,15 +1653,15 @@ static gint pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, } else { - max_w = pr->window_width; - max_h = pr->window_height; + max_w = pr->viewport_width; + max_h = pr->viewport_height; } if ((pr->zoom_expand && !sizeable) || w > max_w || h > max_h) { - if ((gdouble)max_w / w > (gdouble)max_h / h) + if ((gdouble)max_w / w > (gdouble)max_h / h / pr->aspect_ratio) { - scale = (gdouble)max_h / h; + scale = (gdouble)max_h / h / pr->aspect_ratio; h = max_h; w = w * scale + 0.5; if (w > max_w) w = max_w; @@ -3165,7 +1670,7 @@ static gint pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, { scale = (gdouble)max_w / w; w = max_w; - h = h * scale + 0.5; + h = h * scale * pr->aspect_ratio + 0.5; if (h > max_h) h = max_h; } @@ -3177,6 +1682,17 @@ static gint pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, scale = scale * factor; } + if (pr->zoom_expand) + { + gdouble factor = (gdouble)pr->enlargement_limit_size / 100; + if (scale > factor) + { + w = w * factor / scale; + h = h * factor / scale; + scale = factor; + } + } + if (w < 1) w = 1; if (h < 1) h = 1; } @@ -3189,29 +1705,20 @@ static gint pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom, { scale = zoom; w = w * scale; - h = h * scale; + h = h * scale * pr->aspect_ratio; } else /* zoom out */ { scale = 1.0 / (0.0 - zoom); w = w * scale; - h = h * scale; + h = h * scale * pr->aspect_ratio; } - invalid = (pr->width != w || pr->height != h); - pr->zoom = zoom; pr->width = w; pr->height = h; pr->scale = scale; - if (invalidate || invalid) - { - pr_tile_invalidate_all(pr); - pr_redraw(pr, TRUE); - } - if (redrawn) *redrawn = (invalidate || invalid); - return TRUE; } @@ -3220,13 +1727,13 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, { gdouble old_scale; gint old_cx, old_cy; - gint clamped; - gint sized; - gboolean redrawn = FALSE; gboolean center_point = !!(flags & PR_ZOOM_CENTER); gboolean force = !!(flags & PR_ZOOM_FORCE); gboolean new = !!(flags & PR_ZOOM_NEW); + gboolean lazy = !!(flags & PR_ZOOM_LAZY); PrZoomFlags clamp_flags = flags; + gdouble old_center_x = pr->norm_center_x; + gdouble old_center_y = pr->norm_center_y; old_scale = pr->scale; if (center_point) @@ -3244,22 +1751,24 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, } if (force) clamp_flags |= PR_ZOOM_INVALIDATE; - if (!pr_zoom_clamp(pr, zoom, clamp_flags, &redrawn)) return; + (void) pr_parent_window_resize(pr, pr->width, pr->height); + if (!pr_zoom_clamp(pr, zoom, clamp_flags)) return; - clamped = pr_size_clamp(pr); - sized = pr_parent_window_resize(pr, pr->width, pr->height); + (void) pr_size_clamp(pr); if (force && new) { switch (pr->scroll_reset) { case PR_SCROLL_RESET_NOCHANGE: - /* maintain old scroll position, do nothing */ + /* maintain old scroll position */ + pr->x_scroll = ((gdouble)pr->image_width * old_center_x * pr->scale) - pr->vis_width / 2; + pr->y_scroll = ((gdouble)pr->image_height * old_center_y * pr->scale * pr->aspect_ratio) - pr->vis_height / 2; break; case PR_SCROLL_RESET_CENTER: /* center new image */ - pr->x_scroll = ((double)pr->image_width / 2.0 * pr->scale) - pr->vis_width / 2; - pr->y_scroll = ((double)pr->image_height / 2.0 * pr->scale) - pr->vis_height / 2; + pr->x_scroll = ((gdouble)pr->image_width / 2.0 * pr->scale) - pr->vis_width / 2; + pr->y_scroll = ((gdouble)pr->image_height / 2.0 * pr->scale * pr->aspect_ratio) - pr->vis_height / 2; break; case PR_SCROLL_RESET_TOPLEFT: default: @@ -3275,23 +1784,19 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, if (center_point) { pr->x_scroll = old_cx / old_scale * pr->scale - (px - pr->x_offset); - pr->y_scroll = old_cy / old_scale * pr->scale - (py - pr->y_offset); + pr->y_scroll = old_cy / old_scale * pr->scale * pr->aspect_ratio - (py - pr->y_offset); } else { pr->x_scroll = old_cx / old_scale * pr->scale - (pr->vis_width / 2); - pr->y_scroll = old_cy / old_scale * pr->scale - (pr->vis_height / 2); + pr->y_scroll = old_cy / old_scale * pr->scale * pr->aspect_ratio - (pr->vis_height / 2); } } pr_scroll_clamp(pr); - /* If the window was not sized, redraw the image - we know there will be no size/expose signal. - * But even if a size is claimed, there is no guarantee that the window manager will allow it, - * so redraw the window anyway :/ - */ - if (sized || clamped) pr_border_clear(pr); - pr_redraw(pr, redrawn); + pr->renderer->update_zoom(pr->renderer, lazy); + if (pr->renderer2) pr->renderer2->update_zoom(pr->renderer2, lazy); pr_scroll_notify_signal(pr); pr_zoom_signal(pr); @@ -3300,29 +1805,60 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, static void pr_size_sync(PixbufRenderer *pr, gint new_width, gint new_height) { - gint zoom_changed = FALSE; + gboolean zoom_changed = FALSE; + + gint new_viewport_width = new_width; + gint new_viewport_height = new_height; + + if (!pr->stereo_temp_disable) + { + if (pr->stereo_mode & PR_STEREO_HORIZ) + { + new_viewport_width = new_width / 2; + } + else if (pr->stereo_mode & PR_STEREO_VERT) + { + new_viewport_height = new_height / 2; + } + else if (pr->stereo_mode & PR_STEREO_FIXED) + { + new_viewport_width = pr->stereo_fixed_width; + new_viewport_height = pr->stereo_fixed_height; + } + } - if (pr->window_width == new_width && pr->window_height == new_height) return; + if (pr->window_width == new_width && pr->window_height == new_height && + pr->viewport_width == new_viewport_width && pr->viewport_height == new_viewport_height) return; pr->window_width = new_width; pr->window_height = new_height; + pr->viewport_width = new_viewport_width; + pr->viewport_height = new_viewport_height; if (pr->zoom == 0.0) { gdouble old_scale = pr->scale; - pr_zoom_clamp(pr, 0.0, PR_ZOOM_FORCE, NULL); + pr_zoom_clamp(pr, 0.0, PR_ZOOM_FORCE); zoom_changed = (old_scale != pr->scale); } pr_size_clamp(pr); pr_scroll_clamp(pr); - pr_overlay_update_sizes(pr); + if (zoom_changed) + { + pr->renderer->update_zoom(pr->renderer, FALSE); + if (pr->renderer2) pr->renderer2->update_zoom(pr->renderer2, FALSE); + } + + pr->renderer->update_viewport(pr->renderer); + if (pr->renderer2) pr->renderer2->update_viewport(pr->renderer2); + /* ensure scroller remains visible */ if (pr->scroller_overlay != -1) { - gint update = FALSE; + gboolean update = FALSE; if (pr->scroller_x > new_width) { @@ -3353,8 +1889,6 @@ static void pr_size_sync(PixbufRenderer *pr, gint new_width, gint new_height) } } - pr_border_clear(pr); - pr_scroll_notify_signal(pr); if (zoom_changed) pr_zoom_signal(pr); pr_update_signal(pr); @@ -3367,21 +1901,6 @@ static void pr_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer da pr_size_sync(pr, allocation->width, allocation->height); } -static void pixbuf_renderer_paint(PixbufRenderer *pr, GdkRectangle *area) -{ - gint x, y; - - pr_border_draw(pr, 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); -} - /* *------------------------------------------------------------------- * scrolling @@ -3392,7 +1911,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)); @@ -3405,6 +1923,9 @@ void pixbuf_renderer_scroll(PixbufRenderer *pr, gint x, gint y) pr->y_scroll += y; pr_scroll_clamp(pr); + + pixbuf_renderer_sync_scroll_center(pr); + if (pr->x_scroll == old_x && pr->y_scroll == old_y) return; pr_scroll_notify_signal(pr); @@ -3412,90 +1933,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 */ - 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); - } - } + pr->renderer->scroll(pr->renderer, x_off, y_off); + if (pr->renderer2) pr->renderer2->scroll(pr->renderer2, x_off, y_off); } void pixbuf_renderer_scroll_to_point(PixbufRenderer *pr, gint x, gint y, @@ -3511,7 +1950,7 @@ void pixbuf_renderer_scroll_to_point(PixbufRenderer *pr, gint x, gint y, ay = (gdouble)pr->vis_height * y_align; px = (gdouble)x * pr->scale - (pr->x_scroll + ax); - py = (gdouble)y * pr->scale - (pr->y_scroll + ay); + py = (gdouble)y * pr->scale * pr->aspect_ratio - (pr->y_scroll + ay); pixbuf_renderer_scroll(pr, px, py); } @@ -3520,20 +1959,8 @@ void pixbuf_renderer_scroll_to_point(PixbufRenderer *pr, gint x, gint y, void pixbuf_renderer_get_scroll_center(PixbufRenderer *pr, gdouble *x, gdouble *y) { - gint src_x, src_y; - - src_x = pr->x_scroll + pr->vis_width / 2; - src_y = pr->y_scroll + pr->vis_height / 2; - - if (pr->width) - *x = (gdouble)src_x / pr->width; - else - *x = 0.5; /* center */ - - if (pr->height) - *y = (gdouble)src_y / pr->height; - else - *y = 0.5; /* center */ + *x = pr->norm_center_x; + *y = pr->norm_center_y; } void pixbuf_renderer_set_scroll_center(PixbufRenderer *pr, gdouble x, gdouble y) @@ -3543,32 +1970,52 @@ void pixbuf_renderer_set_scroll_center(PixbufRenderer *pr, gdouble x, gdouble y) dst_x = x * pr->width - pr->vis_width / 2 - pr->x_scroll + CLAMP(pr->subpixel_x_scroll, -1.0, 1.0); dst_y = y * pr->height - pr->vis_height / 2 - pr->y_scroll + CLAMP(pr->subpixel_y_scroll, -1.0, 1.0); - pr->subpixel_x_scroll = dst_x - (int)dst_x; - pr->subpixel_y_scroll = dst_y - (int)dst_y; + pr->subpixel_x_scroll = dst_x - (gint)dst_x; + pr->subpixel_y_scroll = dst_y - (gint)dst_y; - pixbuf_renderer_scroll(pr, (int)dst_x, (int)dst_y); + pixbuf_renderer_scroll(pr, (gint)dst_x, (gint)dst_y); } - /* *------------------------------------------------------------------- * mouse *------------------------------------------------------------------- */ -static gint pr_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +static gboolean pr_mouse_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data) { PixbufRenderer *pr; gint accel; +#if GTK_CHECK_VERSION(3,0,0) + GdkDeviceManager *device_manager; + GdkDevice *device; +#endif + + /* This is a hack, but work far the best, at least for single pointer systems. + * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */ + gint x, y; +#if GTK_CHECK_VERSION(3,0,0) + device_manager = gdk_display_get_device_manager(gdk_window_get_display(event->window)); + device = gdk_device_manager_get_client_pointer(device_manager); + gdk_window_get_device_position(event->window, device, &x, &y, NULL); +#else + gdk_window_get_pointer (event->window, &x, &y, NULL); +#endif + event->x = x; + event->y = y; pr = PIXBUF_RENDERER(widget); - if (pr->scroller_id != -1) + if (pr->scroller_id) { - pr->scroller_xpos = bevent->x; - pr->scroller_ypos = bevent->y; + pr->scroller_xpos = event->x; + pr->scroller_ypos = event->y; } + pr->x_mouse = event->x; + pr->y_mouse = event->y; + pr_update_pixel_signal(pr); + if (!pr->in_drag || !gdk_pointer_is_grabbed()) return FALSE; if (pr->drag_moved < PR_DRAG_SCROLL_THRESHHOLD) @@ -3580,7 +2027,7 @@ static gint pr_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpoint widget_set_cursor(widget, GDK_FLEUR); } - if (bevent->state & GDK_SHIFT_MASK) + if (event->state & GDK_CONTROL_MASK) { accel = PR_PAN_SHIFT_MULTIPLIER; } @@ -3590,25 +2037,42 @@ static gint pr_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpoint } /* do the scroll */ - pixbuf_renderer_scroll(pr, (pr->drag_last_x - bevent->x) * accel, - (pr->drag_last_y - bevent->y) * accel); + pixbuf_renderer_scroll(pr, (pr->drag_last_x - event->x) * accel, + (pr->drag_last_y - event->y) * accel); + + pr_drag_signal(pr, event); - pr_drag_signal(pr, bevent); + pr->drag_last_x = event->x; + pr->drag_last_y = event->y; - pr->drag_last_x = bevent->x; - pr->drag_last_y = bevent->y; + /* This is recommended by the GTK+ documentation, but does not work properly. + * Use deprecated way until GTK+ gets a solution for correct motion hint handling: + * http://bugzilla.gnome.org/show_bug.cgi?id=587714 + */ + /* gdk_event_request_motions (event); */ + return FALSE; +} + +static gboolean pr_leave_notify_cb(GtkWidget *widget, GdkEventCrossing *cevent, gpointer data) +{ + PixbufRenderer *pr; + + pr = PIXBUF_RENDERER(widget); + pr->x_mouse = -1; + pr->y_mouse = -1; + pr_update_pixel_signal(pr); return FALSE; } -static gint pr_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +static gboolean pr_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) { PixbufRenderer *pr; GtkWidget *parent; pr = PIXBUF_RENDERER(widget); - if (pr->scroller_id != -1) return TRUE; + if (pr->scroller_id) return TRUE; switch (bevent->button) { @@ -3617,8 +2081,8 @@ static gint pr_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointe pr->drag_last_x = bevent->x; pr->drag_last_y = bevent->y; pr->drag_moved = 0; - gdk_pointer_grab(widget->window, FALSE, - GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + gdk_pointer_grab(gtk_widget_get_window(widget), FALSE, + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent->time); gtk_grab_add(widget); break; @@ -3633,7 +2097,7 @@ static gint pr_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointe } parent = gtk_widget_get_parent(widget); - if (widget && GTK_WIDGET_CAN_FOCUS(parent)) + if (widget && gtk_widget_get_can_focus(parent)) { gtk_widget_grab_focus(parent); } @@ -3641,86 +2105,276 @@ static gint pr_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointe return FALSE; } -static gint pr_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +static gboolean pr_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + PixbufRenderer *pr; + + pr = PIXBUF_RENDERER(widget); + + if (pr->scroller_id) + { + pr_scroller_stop(pr); + return TRUE; + } + + if (gdk_pointer_is_grabbed() && gtk_widget_has_grab(GTK_WIDGET(pr))) + { + gtk_grab_remove(widget); + gdk_pointer_ungrab(bevent->time); + widget_set_cursor(widget, -1); + } + + if (pr->drag_moved < PR_DRAG_SCROLL_THRESHHOLD) + { + if (bevent->button == MOUSE_BUTTON_LEFT && (bevent->state & GDK_CONTROL_MASK)) + { + pr_scroller_start(pr, bevent->x, bevent->y); + } + else if (bevent->button == MOUSE_BUTTON_LEFT || bevent->button == MOUSE_BUTTON_MIDDLE) + { + pr_clicked_signal(pr, bevent); + } + } + + pr->in_drag = FALSE; + + return FALSE; +} + +static gboolean pr_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + PixbufRenderer *pr; + + pr = PIXBUF_RENDERER(widget); + + if (pr->scroller_id) + { + pr->scroller_xpos = pr->scroller_x; + pr->scroller_ypos = pr->scroller_y; + pr->scroller_xinc = 0; + pr->scroller_yinc = 0; + } + + return FALSE; +} + +static void pr_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data) { PixbufRenderer *pr; pr = PIXBUF_RENDERER(widget); - if (pr->scroller_id != -1) - { - pr_scroller_stop(pr); - return TRUE; - } + pr->drag_moved = PR_DRAG_SCROLL_THRESHHOLD; +} + +static void pr_signals_connect(PixbufRenderer *pr) +{ + g_signal_connect(G_OBJECT(pr), "motion_notify_event", + G_CALLBACK(pr_mouse_motion_cb), pr); + g_signal_connect(G_OBJECT(pr), "button_press_event", + G_CALLBACK(pr_mouse_press_cb), pr); + g_signal_connect(G_OBJECT(pr), "button_release_event", + 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), "leave_notify_event", + G_CALLBACK(pr_leave_notify_cb), pr); + + gtk_widget_set_events(GTK_WIDGET(pr), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK | + GDK_LEAVE_NOTIFY_MASK); + + g_signal_connect(G_OBJECT(pr), "drag_begin", + G_CALLBACK(pr_mouse_drag_cb), pr); + +} + +/* + *------------------------------------------------------------------- + * stereo support + *------------------------------------------------------------------- + */ + +#define COLOR_BYTES 3 /* rgb */ +#define RC 0 /* Red-Cyan */ +#define GM 1 /* Green-Magenta */ +#define YB 2 /* Yellow-Blue */ + +static void pr_create_anaglyph_color(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h, guint mode) +{ + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); + + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + switch(mode) + { + case RC: + dp[0] = sp[0]; /* copy red channel */ + break; + case GM: + dp[1] = sp[1]; + break; + case YB: + dp[0] = sp[0]; + dp[1] = sp[1]; + break; + } + sp += COLOR_BYTES; + dp += COLOR_BYTES; + } + } +} + +static void pr_create_anaglyph_gray(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h, guint mode) +{ + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + const double gc[3] = {0.299, 0.587, 0.114}; + + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); - if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(pr)) - { - gtk_grab_remove(widget); - gdk_pointer_ungrab(bevent->time); - widget_set_cursor(widget, -1); - } + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; - if (pr->drag_moved < PR_DRAG_SCROLL_THRESHHOLD) + for (i = y; i < y + h; i++) { - if (bevent->button == MOUSE_BUTTON_LEFT && (bevent->state & GDK_SHIFT_MASK)) - { - pr_scroller_start(pr, bevent->x, bevent->y); - } - else if (bevent->button == MOUSE_BUTTON_LEFT || bevent->button == MOUSE_BUTTON_MIDDLE) + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) { - pr_clicked_signal(pr, bevent); + guchar g1 = dp[0] * gc[0] + dp[1] * gc[1] + dp[2] * gc[2]; + guchar g2 = sp[0] * gc[0] + sp[1] * gc[1] + sp[2] * gc[2]; + switch(mode) + { + case RC: + dp[0] = g2; /* red channel from sp */ + dp[1] = g1; /* green and blue from dp */ + dp[2] = g1; + break; + case GM: + dp[0] = g1; + dp[1] = g2; + dp[2] = g1; + break; + case YB: + dp[0] = g2; + dp[1] = g2; + dp[2] = g1; + break; + } + sp += COLOR_BYTES; + dp += COLOR_BYTES; } } - - pr->in_drag = FALSE; - - return FALSE; } -static gint pr_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +static void pr_create_anaglyph_dubois(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h, guint mode) { - PixbufRenderer *pr; - - pr = PIXBUF_RENDERER(widget); + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j, k; + double pr_dubois_matrix[3][6]; + const static double pr_dubois_matrix_RC[3][6] = { + { 0.456, 0.500, 0.176, -0.043, -0.088, -0.002}, + {-0.040, -0.038, -0.016, 0.378, 0.734, -0.018}, + {-0.015, -0.021, -0.005, -0.072, -0.113, 1.226}}; + const static double pr_dubois_matrix_GM[3][6] = { + {-0.062, -0.158, -0.039, 0.529, 0.705, 0.024}, + { 0.284, 0.668, 0.143, -0.016, -0.015, -0.065}, + {-0.015, -0.027, 0.021, 0.009, 0.075, 0.937}}; + const static double pr_dubois_matrix_YB[3][6] = { + { 1.000, -0.193, 0.282, -0.015, -0.116, -0.016}, + {-0.024, 0.855, 0.064, 0.006, 0.058, -0.016}, + {-0.036, -0.163, 0.021, 0.089, 0.174, 0.858}}; - if (pr->scroller_id != -1) + switch(mode) { - pr->scroller_xpos = pr->scroller_x; - pr->scroller_ypos = pr->scroller_y; - pr->scroller_xinc = 0; - pr->scroller_yinc = 0; + case RC: + memcpy(pr_dubois_matrix, pr_dubois_matrix_RC, sizeof pr_dubois_matrix); + break; + case GM: + memcpy(pr_dubois_matrix, pr_dubois_matrix_GM, sizeof pr_dubois_matrix); + break; + case YB: + memcpy(pr_dubois_matrix, pr_dubois_matrix_YB, sizeof pr_dubois_matrix); + break; } - return FALSE; -} - -static void pr_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data) -{ - PixbufRenderer *pr; + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); - pr = PIXBUF_RENDERER(widget); + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; - pr->drag_moved = PR_DRAG_SCROLL_THRESHHOLD; + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + double res[3]; + for (k = 0; k < 3; k++) + { + const double *m = pr_dubois_matrix[k]; + res[k] = sp[0] * m[0] + sp[1] * m[1] + sp[2] * m[2] + dp[0] * m[3] + dp[1] * m[4] + dp[2] * m[5]; + if (res[k] < 0.0) res[k] = 0; + if (res[k] > 255.0) res[k] = 255.0; + } + dp[0] = res[0]; + dp[1] = res[1]; + dp[2] = res[2]; + sp += COLOR_BYTES; + dp += COLOR_BYTES; + } + } } -static void pr_signals_connect(PixbufRenderer *pr) +void pr_create_anaglyph(guint mode, GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h) { - g_signal_connect(G_OBJECT(pr), "motion_notify_event", - G_CALLBACK(pr_mouse_motion_cb), pr); - g_signal_connect(G_OBJECT(pr), "button_press_event", - G_CALLBACK(pr_mouse_press_cb), pr); - g_signal_connect(G_OBJECT(pr), "button_release_event", - G_CALLBACK(pr_mouse_release_cb), pr); - g_signal_connect(G_OBJECT(pr), "leave_notify_event", - G_CALLBACK(pr_mouse_leave_cb), pr); - - gtk_widget_set_events(GTK_WIDGET(pr), GDK_POINTER_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | - GDK_LEAVE_NOTIFY_MASK); - - g_signal_connect(G_OBJECT(pr), "drag_begin", - G_CALLBACK(pr_mouse_drag_cb), pr); - + if (mode & PR_STEREO_ANAGLYPH_RC) + pr_create_anaglyph_color(pixbuf, right, x, y, w, h, RC); + else if (mode & PR_STEREO_ANAGLYPH_GM) + pr_create_anaglyph_color(pixbuf, right, x, y, w, h, GM); + else if (mode & PR_STEREO_ANAGLYPH_YB) + pr_create_anaglyph_color(pixbuf, right, x, y, w, h, YB); + else if (mode & PR_STEREO_ANAGLYPH_GRAY_RC) + pr_create_anaglyph_gray(pixbuf, right, x, y, w, h, RC); + else if (mode & PR_STEREO_ANAGLYPH_GRAY_GM) + pr_create_anaglyph_gray(pixbuf, right, x, y, w, h, GM); + else if (mode & PR_STEREO_ANAGLYPH_GRAY_YB) + pr_create_anaglyph_gray(pixbuf, right, x, y, w, h, YB); + else if (mode & PR_STEREO_ANAGLYPH_DB_RC) + pr_create_anaglyph_dubois(pixbuf, right, x, y, w, h, RC); + else if (mode & PR_STEREO_ANAGLYPH_DB_GM) + pr_create_anaglyph_dubois(pixbuf, right, x, y, w, h, GM); + else if (mode & PR_STEREO_ANAGLYPH_DB_YB) + pr_create_anaglyph_dubois(pixbuf, right, x, y, w, h, YB); } /* @@ -3730,6 +2384,8 @@ static void pr_signals_connect(PixbufRenderer *pr) */ static void pr_pixbuf_size_sync(PixbufRenderer *pr) { + pr->stereo_pixbuf_offset_left = 0; + pr->stereo_pixbuf_offset_right = 0; if (!pr->pixbuf) return; switch (pr->orientation) { @@ -3739,57 +2395,89 @@ static void pr_pixbuf_size_sync(PixbufRenderer *pr) case EXIF_ORIENTATION_LEFT_BOTTOM: pr->image_width = gdk_pixbuf_get_height(pr->pixbuf); pr->image_height = gdk_pixbuf_get_width(pr->pixbuf); + if (pr->stereo_data == STEREO_PIXBUF_SBS) + { + pr->image_height /= 2; + pr->stereo_pixbuf_offset_right = pr->image_height; + } + else if (pr->stereo_data == STEREO_PIXBUF_CROSS) + { + pr->image_height /= 2; + pr->stereo_pixbuf_offset_left = pr->image_height; + } + break; default: pr->image_width = gdk_pixbuf_get_width(pr->pixbuf); pr->image_height = gdk_pixbuf_get_height(pr->pixbuf); + if (pr->stereo_data == STEREO_PIXBUF_SBS) + { + pr->image_width /= 2; + pr->stereo_pixbuf_offset_right = pr->image_width; + } + else if (pr->stereo_data == STEREO_PIXBUF_CROSS) + { + pr->image_width /= 2; + pr->stereo_pixbuf_offset_left = pr->image_width; + } } } -static void pr_pixbuf_sync(PixbufRenderer *pr, gdouble zoom) +static void pr_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, PrZoomFlags flags) { + if (pixbuf) g_object_ref(pixbuf); + if (pr->pixbuf) g_object_unref(pr->pixbuf); + pr->pixbuf = pixbuf; + if (!pr->pixbuf) { - GtkWidget *box; - /* no pixbuf so just clear the window */ pr->image_width = 0; pr->image_height = 0; pr->scale = 1.0; + pr->zoom = zoom; /* don't throw away the zoom value, it is set by pixbuf_renderer_move, among others, + and used for pixbuf_renderer_zoom_get */ - box = GTK_WIDGET(pr); - - if (GTK_WIDGET_REALIZED(box)) - { - gdk_window_clear(box->window); - pr_overlay_draw(pr, 0, 0, pr->window_width, pr->window_height, NULL); - } + pr->renderer->update_pixbuf(pr->renderer, flags & PR_ZOOM_LAZY); + if (pr->renderer2) pr->renderer2->update_pixbuf(pr->renderer2, flags & PR_ZOOM_LAZY); pr_update_signal(pr); return; } + if (pr->stereo_mode & PR_STEREO_TEMP_DISABLE) + { + gint disable = !pr->pixbuf || ! pr->stereo_data; + pr_stereo_temp_disable(pr, disable); + } + pr_pixbuf_size_sync(pr); - pr_zoom_sync(pr, zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); + pr->renderer->update_pixbuf(pr->renderer, flags & PR_ZOOM_LAZY); + if (pr->renderer2) pr->renderer2->update_pixbuf(pr->renderer2, flags & PR_ZOOM_LAZY); + pr_zoom_sync(pr, zoom, flags | PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); } -static void pr_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom) +void pixbuf_renderer_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom) { - if (pixbuf) g_object_ref(pixbuf); - if (pr->pixbuf) g_object_unref(pr->pixbuf); - pr->pixbuf = pixbuf; + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - pr_pixbuf_sync(pr, zoom); + pr_source_tile_unset(pr); + + pr_set_pixbuf(pr, pixbuf, zoom, 0); + + pr_update_signal(pr); } -void pixbuf_renderer_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom) +void pixbuf_renderer_set_pixbuf_lazy(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, gint orientation, StereoPixbufData stereo_data) { g_return_if_fail(IS_PIXBUF_RENDERER(pr)); pr_source_tile_unset(pr); - pr_set_pixbuf(pr, pixbuf, zoom); + pr->orientation = orientation; + pr->stereo_data = stereo_data; + pr_set_pixbuf(pr, pixbuf, zoom, PR_ZOOM_LAZY); pr_update_signal(pr); } @@ -3817,7 +2505,26 @@ gint pixbuf_renderer_get_orientation(PixbufRenderer *pr) return pr->orientation; } -void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gint slow) +void pixbuf_renderer_set_stereo_data(PixbufRenderer *pr, StereoPixbufData stereo_data) +{ + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + if (pr->stereo_data == stereo_data) return; + + + pr->stereo_data = stereo_data; + + if (pr->stereo_mode & PR_STEREO_TEMP_DISABLE) + { + gint disable = !pr->pixbuf || ! pr->stereo_data; + pr_stereo_temp_disable(pr, disable); + } + pr_pixbuf_size_sync(pr); + pr->renderer->update_pixbuf(pr->renderer, FALSE); + if (pr->renderer2) pr->renderer2->update_pixbuf(pr->renderer2, FALSE); + pr_zoom_sync(pr, pr->zoom, PR_ZOOM_FORCE, 0, 0); +} + +void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gboolean slow) { g_return_if_fail(IS_PIXBUF_RENDERER(pr)); @@ -3848,6 +2555,8 @@ void pixbuf_renderer_move(PixbufRenderer *pr, PixbufRenderer *source) pr->x_scroll = source->x_scroll; pr->y_scroll = source->y_scroll; + pr->x_mouse = source->x_mouse; + pr->y_mouse = source->y_mouse; scroll_reset = pr->scroll_reset; pr->scroll_reset = PR_SCROLL_RESET_NOCHANGE; @@ -3856,6 +2565,7 @@ void pixbuf_renderer_move(PixbufRenderer *pr, PixbufRenderer *source) pr->post_process_user_data = source->post_process_user_data; pr->post_process_slow = source->post_process_slow; pr->orientation = source->orientation; + pr->stereo_data = source->stereo_data; if (source->source_tiles_enabled) { @@ -3876,7 +2586,6 @@ void pixbuf_renderer_move(PixbufRenderer *pr, PixbufRenderer *source) source->source_tiles = NULL; pr_zoom_sync(pr, source->zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); - pr_redraw(pr, TRUE); } else { @@ -3886,41 +2595,74 @@ 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); } -void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gint src_w, gint src_h) +void pixbuf_renderer_copy(PixbufRenderer *pr, PixbufRenderer *source) { - gint x, y, width, height, x1, y1, x2, y2; + GObject *object; + PixbufRendererScrollResetType scroll_reset; g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + g_return_if_fail(IS_PIXBUF_RENDERER(source)); - pr_coords_map_orientation_reverse(pr, - src_x, src_y, - pr->image_width, pr->image_height, - src_w, src_h, - &x, &y, - &width, &height); + if (pr == source) return; - if (pr->source_tiles_enabled) + object = G_OBJECT(pr); + + g_object_set(object, "zoom_min", source->zoom_min, NULL); + g_object_set(object, "zoom_max", source->zoom_max, NULL); + g_object_set(object, "loading", source->loading, NULL); + + pr->complete = source->complete; + + pr->x_scroll = source->x_scroll; + pr->y_scroll = source->y_scroll; + pr->x_mouse = source->x_mouse; + pr->y_mouse = source->y_mouse; + + scroll_reset = pr->scroll_reset; + pr->scroll_reset = PR_SCROLL_RESET_NOCHANGE; + + pr->orientation = source->orientation; + pr->stereo_data = source->stereo_data; + + if (source->source_tiles_enabled) { - pr_source_tile_changed(pr, x, y, width, height); - } + pr->source_tiles_enabled = source->source_tiles_enabled; + pr->source_tiles_cache_size = source->source_tiles_cache_size; + pr->source_tile_width = source->source_tile_width; + pr->source_tile_height = source->source_tile_height; + pr->image_width = source->image_width; + pr->image_height = source->image_height; + + pr->func_tile_request = source->func_tile_request; + pr->func_tile_dispose = source->func_tile_dispose; + pr->func_tile_data = source->func_tile_data; + + pr->source_tiles = source->source_tiles; + source->source_tiles = NULL; - if (pr->scale != 1.0 && pr->zoom_quality != GDK_INTERP_NEAREST) + pr_zoom_sync(pr, source->zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); + } + else { - /* increase region when using a zoom quality that may access surrounding pixels */ - y -= 1; - height += 2; + pixbuf_renderer_set_pixbuf(pr, source->pixbuf, source->zoom); } - x1 = (gint)floor((double)x * pr->scale); - y1 = (gint)floor((double)y * pr->scale); - x2 = (gint)ceil((double)(x + width) * pr->scale); - y2 = (gint)ceil((double)(y + height) * pr->scale); + pr->scroll_reset = scroll_reset; +} + +void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint x, gint y, gint w, gint h) +{ + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + if (pr->source_tiles_enabled) + { + pr_source_tile_changed(pr, x, y, w, h); + } - pr_queue(pr, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE); + pr->renderer->area_changed(pr->renderer, x, y, w, h); + if (pr->renderer2) pr->renderer2->area_changed(pr->renderer2, x, y, w, h); } void pixbuf_renderer_zoom_adjust(PixbufRenderer *pr, gdouble increment) @@ -3978,12 +2720,170 @@ void pixbuf_renderer_zoom_set_limits(PixbufRenderer *pr, gdouble min, gdouble ma } } -gint pixbuf_renderer_get_image_size(PixbufRenderer *pr, gint *width, gint *height) +static void pr_stereo_set(PixbufRenderer *pr) +{ + if (!pr->renderer) pr->renderer = pr_backend_renderer_new(pr); + + pr->renderer->stereo_set(pr->renderer, pr->stereo_mode & ~PR_STEREO_MIRROR_RIGHT & ~PR_STEREO_FLIP_RIGHT); + + if (pr->stereo_mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED)) + { + if (!pr->renderer2) pr->renderer2 = pr_backend_renderer_new(pr); + pr->renderer2->stereo_set(pr->renderer2, (pr->stereo_mode & ~PR_STEREO_MIRROR_LEFT & ~PR_STEREO_FLIP_LEFT) | PR_STEREO_RIGHT); + } + else + { + if (pr->renderer2) pr->renderer2->free(pr->renderer2); + pr->renderer2 = NULL; + } + if (pr->stereo_mode & PR_STEREO_HALF) + { + if (pr->stereo_mode & PR_STEREO_HORIZ) pr->aspect_ratio = 2.0; + else if (pr->stereo_mode & PR_STEREO_VERT) pr->aspect_ratio = 0.5; + else pr->aspect_ratio = 1.0; + } + else + { + pr->aspect_ratio = 1.0; + } +} + +void pixbuf_renderer_stereo_set(PixbufRenderer *pr, gint stereo_mode) +{ + gboolean redraw = !(pr->stereo_mode == stereo_mode) || pr->stereo_temp_disable; + pr->stereo_mode = stereo_mode; + if ((stereo_mode & PR_STEREO_TEMP_DISABLE) && pr->stereo_temp_disable) return; + + pr->stereo_temp_disable = FALSE; + + pr_stereo_set(pr); + + if (redraw) + { + pr_size_sync(pr, pr->window_width, pr->window_height); /* recalculate new viewport */ + pr_zoom_sync(pr, pr->zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); + } +} + +void pixbuf_renderer_stereo_fixed_set(PixbufRenderer *pr, gint width, gint height, gint x1, gint y1, gint x2, gint y2) +{ + pr->stereo_fixed_width = width; + pr->stereo_fixed_height = height; + pr->stereo_fixed_x_left = x1; + pr->stereo_fixed_y_left = y1; + pr->stereo_fixed_x_right = x2; + pr->stereo_fixed_y_right = y2; +} + +gint pixbuf_renderer_stereo_get(PixbufRenderer *pr) +{ + return pr->stereo_mode; +} + +static void pr_stereo_temp_disable(PixbufRenderer *pr, gboolean disable) +{ + if (pr->stereo_temp_disable == disable) return; + pr->stereo_temp_disable = disable; + if (disable) + { + if (!pr->renderer) pr->renderer = pr_backend_renderer_new(pr); + pr->renderer->stereo_set(pr->renderer, PR_STEREO_NONE); + if (pr->renderer2) pr->renderer2->free(pr->renderer2); + pr->renderer2 = NULL; + pr->aspect_ratio = 1.0; + } + else + { + pr_stereo_set(pr); + } + pr_size_sync(pr, pr->window_width, pr->window_height); /* recalculate new viewport */ +} + +gboolean pixbuf_renderer_get_pixel_colors(PixbufRenderer *pr, gint x_pixel, gint y_pixel, + gint *r_mouse, gint *g_mouse, gint *b_mouse) { + GdkPixbuf *pb = pr->pixbuf; + gint p_alpha, prs; + guchar *p_pix, *pp; + gint map_x, map_y, map_w, map_h; + size_t xoff, yoff; + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); - g_return_val_if_fail(width != NULL && height != NULL, FALSE); + g_return_val_if_fail(r_mouse != NULL && g_mouse != NULL && b_mouse != NULL, FALSE); + + if (!pr->pixbuf && !pr->source_tiles_enabled) + { + *r_mouse = -1; + *g_mouse = -1; + *b_mouse = -1; + return FALSE; + } + + if (!pb) return FALSE; + + pr_tile_region_map_orientation(pr->orientation, + x_pixel, y_pixel, + pr->image_width, pr->image_height, + 1, 1, /*single pixel */ + &map_x, &map_y, + &map_w, &map_h); + + if (map_x < 0 || map_x > gdk_pixbuf_get_width(pr->pixbuf) - 1) return FALSE; + if (map_y < 0 || map_y > gdk_pixbuf_get_height(pr->pixbuf) - 1) return FALSE; + + p_alpha = gdk_pixbuf_get_has_alpha(pb); + prs = gdk_pixbuf_get_rowstride(pb); + p_pix = gdk_pixbuf_get_pixels(pb); + + xoff = (size_t)map_x * (p_alpha ? 4 : 3); + yoff = (size_t)map_y * prs; + pp = p_pix + yoff + xoff; + *r_mouse = *pp; + pp++; + *g_mouse = *pp; + pp++; + *b_mouse = *pp; + + return TRUE; +} + +gboolean pixbuf_renderer_get_mouse_position(PixbufRenderer *pr, gint *x_pixel_return, gint *y_pixel_return) +{ + gint x_pixel, y_pixel, x_pixel_clamped, y_pixel_clamped; + + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); + g_return_val_if_fail(x_pixel_return != NULL && y_pixel_return != NULL, FALSE); if (!pr->pixbuf && !pr->source_tiles_enabled) + { + *x_pixel_return = -1; + *y_pixel_return = -1; + return FALSE; + } + + x_pixel = floor((gdouble)(pr->x_mouse - pr->x_offset + pr->x_scroll) / pr->scale); + y_pixel = floor((gdouble)(pr->y_mouse - pr->y_offset + pr->y_scroll) / pr->scale / pr->aspect_ratio); + x_pixel_clamped = CLAMP(x_pixel, 0, pr->image_width - 1); + y_pixel_clamped = CLAMP(y_pixel, 0, pr->image_height - 1); + + if(x_pixel != x_pixel_clamped || y_pixel != y_pixel_clamped) + { + /* mouse is not on pr */ + x_pixel = y_pixel = -1; + } + + *x_pixel_return = x_pixel; + *y_pixel_return = y_pixel; + + return TRUE; +} + +gboolean pixbuf_renderer_get_image_size(PixbufRenderer *pr, gint *width, gint *height) +{ + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); + g_return_val_if_fail(width != NULL && height != NULL, FALSE); + + if (!pr->pixbuf && !pr->source_tiles_enabled && (!pr->image_width || !pr->image_height)) { *width = 0; *height = 0; @@ -3995,12 +2895,12 @@ gint pixbuf_renderer_get_image_size(PixbufRenderer *pr, gint *width, gint *heigh return TRUE; } -gint pixbuf_renderer_get_scaled_size(PixbufRenderer *pr, gint *width, gint *height) +gboolean pixbuf_renderer_get_scaled_size(PixbufRenderer *pr, gint *width, gint *height) { g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); g_return_val_if_fail(width != NULL && height != NULL, FALSE); - if (!pr->pixbuf && !pr->source_tiles_enabled) + if (!pr->pixbuf && !pr->source_tiles_enabled && (!pr->image_width || !pr->image_height)) { *width = 0; *height = 0; @@ -4012,7 +2912,7 @@ gint pixbuf_renderer_get_scaled_size(PixbufRenderer *pr, gint *width, gint *heig return TRUE; } -gint pixbuf_renderer_get_visible_rect(PixbufRenderer *pr, GdkRectangle *rect) +gboolean pixbuf_renderer_get_visible_rect(PixbufRenderer *pr, GdkRectangle *rect) { g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); g_return_val_if_fail(rect != NULL, FALSE); @@ -4028,13 +2928,13 @@ gint pixbuf_renderer_get_visible_rect(PixbufRenderer *pr, GdkRectangle *rect) } rect->x = (gint)((gdouble)pr->x_scroll / pr->scale); - rect->y = (gint)((gdouble)pr->y_scroll / pr->scale); + rect->y = (gint)((gdouble)pr->y_scroll / pr->scale / pr->aspect_ratio); rect->width = (gint)((gdouble)pr->vis_width / pr->scale); - rect->height = (gint)((gdouble)pr->vis_height / pr->scale); + rect->height = (gint)((gdouble)pr->vis_height / pr->scale / pr->aspect_ratio); return TRUE; } -gint pixbuf_renderer_get_virtual_rect(PixbufRenderer *pr, GdkRectangle *rect) +gboolean pixbuf_renderer_get_virtual_rect(PixbufRenderer *pr, GdkRectangle *rect) { g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); g_return_val_if_fail(rect != NULL, FALSE); @@ -4054,3 +2954,27 @@ gint pixbuf_renderer_get_virtual_rect(PixbufRenderer *pr, GdkRectangle *rect) rect->height = pr->vis_height; return TRUE; } + +void pixbuf_renderer_set_size_early(PixbufRenderer *pr, guint width, guint height) +{ +#if 0 + /* FIXME: this function does not consider the image orientation, + so it probably only breaks something */ + gdouble zoom; + gint w, h; + + zoom = pixbuf_renderer_zoom_get(pr); + pr->image_width = width; + pr->image_height = height; + + pr_zoom_clamp(pr, zoom, PR_ZOOM_FORCE, NULL); + + //w = width; + //h = height; + + //pr->width = width; + //pr->height = height; +#endif +} + +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */