📄 pixbuf-renderer.c
字号:
/* * GQview * (C) 2005 John Ellis * * Author: John Ellis * * This software is released under the GNU General Public License (GNU GPL). * Please read the included file COPYING for more information. * This software comes with no warranty of any kind, use at your own risk! */#include <stdio.h>#include <stdlib.h>#include <math.h>#include "pixbuf-renderer.h"#include "intl.h"#include <gtk/gtk.h>/* comment this out if not using this from within GQview * defining GQVIEW_BUILD does these things: * - Sets the shift-click scroller pixbuf to a nice icon instead of a black box */#define GQVIEW_BUILD 1#ifdef GQVIEW_BUILD #include "pixbuf_util.h"#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/* distance to drag mouse to disable image flip */#define PR_DRAG_SCROLL_THRESHHOLD 4/* increase pan rate when holding down shift */#define PR_PAN_SHIFT_MULTIPLIER 6/* scroller config */#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 8typedef 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; gint x; gint y; gint relative; /* x,y coordinates are relative, negative values start bottom right */ gint visible; gint always; /* hide temporarily when scrolling */};enum { SIGNAL_ZOOM = 0, SIGNAL_CLICKED, SIGNAL_SCROLL_NOTIFY, SIGNAL_RENDER_COMPLETE, SIGNAL_COUNT};enum { PROP_0, PROP_ZOOM_MIN, PROP_ZOOM_MAX, PROP_ZOOM_QUALITY, PROP_ZOOM_2PASS, PROP_ZOOM_EXPAND, PROP_DITHER_QUALITY, PROP_SCROLL_RESET, PROP_DELAY_FLIP, PROP_LOADING, PROP_COMPLETE, PROP_CACHE_SIZE_DISPLAY, PROP_CACHE_SIZE_TILES, PROP_WINDOW_FIT, PROP_WINDOW_LIMIT, PROP_WINDOW_LIMIT_VALUE};static guint signals[SIGNAL_COUNT] = { 0 };static GtkEventBoxClass *parent_class = NULL;static void pixbuf_renderer_class_init(PixbufRendererClass *class);static void pixbuf_renderer_init(PixbufRenderer *pr);static void pixbuf_renderer_finalize(GObject *object);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_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, gint force, gint blank, gint new, gint center_point, 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);/* *------------------------------------------------------------------- * Pixbuf Renderer object *------------------------------------------------------------------- */GType pixbuf_renderer_get_type(void){ static GType pixbuf_renderer_type = 0; if (!pixbuf_renderer_type) { static const GTypeInfo pixbuf_renderer_info = { sizeof(PixbufRendererClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc)pixbuf_renderer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof(PixbufRenderer), 0, /* n_preallocs */ (GInstanceInitFunc)pixbuf_renderer_init, }; pixbuf_renderer_type = g_type_register_static(GTK_TYPE_EVENT_BOX, "PixbufRenderer", &pixbuf_renderer_info, 0); } return pixbuf_renderer_type;}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); gobject_class->set_property = pixbuf_renderer_set_property; gobject_class->get_property = pixbuf_renderer_get_property; 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", "Zoom minimum", NULL, -1000.0, 1000.0, PR_ZOOM_MIN, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_ZOOM_MAX, g_param_spec_double("zoom_max", "Zoom maximum", NULL, -1000.0, 1000.0, PR_ZOOM_MIN, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_ZOOM_QUALITY, g_param_spec_uint("zoom_quality", "Zoom quality", NULL, GDK_INTERP_NEAREST, GDK_INTERP_HYPER, GDK_INTERP_BILINEAR, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_ZOOM_2PASS, g_param_spec_boolean("zoom_2pass", "2 pass zoom", NULL, TRUE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_ZOOM_EXPAND, g_param_spec_boolean("zoom_expand", "Expand image in autozoom.", 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", "New image scroll reset", NULL, PR_SCROLL_RESET_TOPLEFT, PR_SCROLL_RESET_NOCHANGE, PR_SCROLL_RESET_TOPLEFT, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_DELAY_FLIP, g_param_spec_boolean("delay_flip", "Delay image update", NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_LOADING, g_param_spec_boolean("loading", "Image actively loading", NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_COMPLETE, g_param_spec_boolean("complete", "Image rendering complete", NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_CACHE_SIZE_DISPLAY, g_param_spec_uint("cache_display", "Display cache size MB", NULL, 0, 128, PR_CACHE_SIZE_DEFAULT, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_CACHE_SIZE_TILES, g_param_spec_uint("cache_tiles", "Tile cache count", "Number of tiles to retain in memory at any one time.", 0, 256, PR_CACHE_SIZE_DEFAULT, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_WINDOW_FIT, g_param_spec_boolean("window_fit", "Fit window to image size", NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_WINDOW_LIMIT, g_param_spec_boolean("window_limit", "Limit size of parent window", NULL, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_WINDOW_LIMIT_VALUE, g_param_spec_uint("window_limit_value", "Size limit of parent window", NULL, 10, 150, 100, G_PARAM_READABLE | G_PARAM_WRITABLE)); signals[SIGNAL_ZOOM] = g_signal_new("zoom", G_OBJECT_CLASS_TYPE(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(PixbufRendererClass, zoom), NULL, NULL, g_cclosure_marshal_VOID__DOUBLE, G_TYPE_NONE, 1, G_TYPE_DOUBLE); signals[SIGNAL_CLICKED] = g_signal_new("clicked", G_OBJECT_CLASS_TYPE(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(PixbufRendererClass, clicked), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GDK_TYPE_EVENT); signals[SIGNAL_SCROLL_NOTIFY] = g_signal_new("scroll-notify", G_OBJECT_CLASS_TYPE(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(PixbufRendererClass, scroll_notify), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[SIGNAL_RENDER_COMPLETE] = g_signal_new("render-complete", G_OBJECT_CLASS_TYPE(gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(PixbufRendererClass, render_complete), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);}static void pixbuf_renderer_init(PixbufRenderer *pr){ GtkWidget *box; box = GTK_WIDGET(pr); pr->zoom_min = PR_ZOOM_MIN; pr->zoom_max = PR_ZOOM_MAX; pr->zoom_quality = GDK_INTERP_BILINEAR; pr->zoom_2pass = FALSE; pr->zoom = 1.0; pr->scale = 1.0; pr->dither_quality = GDK_RGB_DITHER_NORMAL; 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_overlay = -1; pr->source_tiles_enabled = FALSE; pr->source_tiles = NULL; gtk_widget_set_double_buffered(box, FALSE); g_signal_connect_after(G_OBJECT(box), "size_allocate", G_CALLBACK(pr_size_cb), pr); pr_signals_connect(pr);}static void pixbuf_renderer_finalize(GObject *object){ PixbufRenderer *pr; pr = PIXBUF_RENDERER(object); pr_queue_clear(pr); pr_tile_free_all(pr); if (pr->pixbuf) g_object_unref(pr->pixbuf); pr_scroller_timer_set(pr, FALSE); pr_overlay_list_clear(pr); pr_source_tile_free_all(pr);}PixbufRenderer* pixbuf_renderer_new(void){ return g_object_new(TYPE_PIXBUF_RENDERER, NULL);}static void pixbuf_renderer_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec){ PixbufRenderer *pr; pr = PIXBUF_RENDERER(object); switch (prop_id) { case PROP_ZOOM_MIN: pr->zoom_min = g_value_get_double(value); break; case PROP_ZOOM_MAX: pr->zoom_max = g_value_get_double(value); break; case PROP_ZOOM_QUALITY: pr->zoom_quality = g_value_get_uint(value); break; case PROP_ZOOM_2PASS: pr->zoom_2pass = g_value_get_boolean(value); break; 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; case PROP_DELAY_FLIP: pr->delay_flip = g_value_get_boolean(value); break; case PROP_LOADING: pr->loading = g_value_get_boolean(value); break; case PROP_COMPLETE: pr->complete = g_value_get_boolean(value); break; case PROP_CACHE_SIZE_DISPLAY: 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); break; case PROP_WINDOW_FIT: pr->window_fit = g_value_get_boolean(value); break; case PROP_WINDOW_LIMIT: pr->window_limit = g_value_get_boolean(value); break; case PROP_WINDOW_LIMIT_VALUE: pr->window_limit_size = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; }}static void pixbuf_renderer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec){ PixbufRenderer *pr; pr = PIXBUF_RENDERER(object); switch (prop_id) { case PROP_ZOOM_MIN: g_value_set_double(value, pr->zoom_min); break; case PROP_ZOOM_MAX: g_value_set_double(value, pr->zoom_max); break; case PROP_ZOOM_QUALITY: g_value_set_uint(value, pr->zoom_quality); break; case PROP_ZOOM_2PASS: g_value_set_boolean(value, pr->zoom_2pass); break; 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; case PROP_DELAY_FLIP: g_value_set_boolean(value, pr->delay_flip); break; case PROP_LOADING:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -