📄 image.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 "gqview.h"#include "image.h"#include "image-load.h"#include "collect.h"#include "exif.h"#include "pixbuf-renderer.h"#include "pixbuf_util.h"#include "ui_fileops.h"#include <math.h>/* size of the image loader buffer (512 bytes x defined number) */#define IMAGE_LOAD_BUFFER_COUNT 8/* define this so that more bytes are read per idle loop on larger images (> 1MB) */#define IMAGE_THROTTLE_LARGER_IMAGES 1/* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */#define IMAGE_THROTTLE_FACTOR 4/* the file size at which throttling take place */#define IMAGE_THROTTLE_THRESHOLD 1048576#define IMAGE_AUTO_REFRESH_TIME 3000static GList *image_list = NULL;static void image_update_title(ImageWindow *imd);/* *------------------------------------------------------------------- * 'signals' *------------------------------------------------------------------- */static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data){ ImageWindow *imd = data; if (imd->func_button) { imd->func_button(imd, event->button, event->time, event->x, event->y, event->state, imd->data_button); }}static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data){ ImageWindow *imd = data; if (imd->func_scroll_notify && pr->scale) { imd->func_scroll_notify(imd, (gint)((gdouble)pr->x_scroll / pr->scale), (gint)((gdouble)pr->y_scroll / pr->scale), (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale), (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale), imd->data_scroll_notify); }}static void image_update_util(ImageWindow *imd){ if (imd->func_update) imd->func_update(imd, imd->data_update);}static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data){ ImageWindow *imd = data; if (imd->title_show_zoom) image_update_title(imd); image_update_util(imd);}static void image_complete_util(ImageWindow *imd, gint preload){ if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return; if (debug) printf("image load completed \"%s\" (%s)\n", (preload) ? imd->read_ahead_path : imd->image_path, (preload) ? "preload" : "current"); if (!preload) imd->completed = TRUE; if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);}static void image_render_complete_cb(PixbufRenderer *pr, gpointer data){ ImageWindow *imd = data; image_complete_util(imd, FALSE);}static void image_new_util(ImageWindow *imd){ if (imd->func_new) imd->func_new(imd, imd->data_new);}/* *------------------------------------------------------------------- * misc *------------------------------------------------------------------- */static void image_update_title(ImageWindow *imd){ gchar *title = NULL; gchar *zoom = NULL; gchar *collection = NULL; if (!imd->top_window) return; if (imd->collection && collection_to_number(imd->collection) >= 0) { const gchar *name; name = imd->collection->name; if (!name) name = _("Untitled"); collection = g_strdup_printf(" (Collection %s)", name); } if (imd->title_show_zoom) { gchar *buf = image_zoom_get_as_text(imd); zoom = g_strconcat(" [", buf, "]", NULL); g_free(buf); } title = g_strdup_printf("%s%s%s%s%s%s", imd->title ? imd->title : "", imd->image_name ? imd->image_name : "", zoom ? zoom : "", collection ? collection : "", imd->image_name ? " - " : "", imd->title_right ? imd->title_right : ""); gtk_window_set_title(GTK_WINDOW(imd->top_window), title); g_free(title); g_free(zoom); g_free(collection);}/* *------------------------------------------------------------------- * rotation, flip, etc. *------------------------------------------------------------------- */static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp){ PixbufRenderer *pr; GdkPixbuf *new = NULL; gint x, y; gint t; pr = (PixbufRenderer *)imd->pr; imd->delay_alter_type = ALTER_NONE; if (!pr->pixbuf) return; x = pr->x_scroll + (pr->vis_width / 2); y = pr->y_scroll + (pr->vis_height / 2); switch (type) { case ALTER_ROTATE_90: new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE); t = x; x = pr->height - y; y = t; break; case ALTER_ROTATE_90_CC: new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE); t = x; x = y; y = pr->width - t; break; case ALTER_ROTATE_180: new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE); x = pr->width - x; y = pr->height - y; break; case ALTER_MIRROR: new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE); x = pr->width - x; break; case ALTER_FLIP: new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE); y = pr->height - y; break; case ALTER_NONE: default: return; break; } if (!new) return; pixbuf_renderer_set_pixbuf(pr, new, pr->zoom); g_object_unref(new); if (clamp && pr->zoom != 0.0) { pixbuf_renderer_scroll(pr, x - (pr->vis_width / 2), y - (pr->vis_height / 2)); }}static void image_post_process(ImageWindow *imd, gint clamp){ if (exif_rotate_enable && image_get_pixbuf(imd)) { ExifData *ed; gint orientation; ed = exif_read(imd->image_path); if (ed && exif_get_integer(ed, "Orientation", &orientation)) { /* see http://jpegclub.org/exif_orientation.html 1 2 3 4 5 6 7 8 888888 888888 88 88 8888888888 88 88 8888888888 88 88 88 88 88 88 88 88 88 88 88 88 8888 8888 8888 8888 88 8888888888 8888888888 88 88 88 88 88 88 88 888888 888888 */ switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ break; case EXIF_ORIENTATION_TOP_RIGHT: /* mirrored */ imd->delay_alter_type = ALTER_MIRROR; break; case EXIF_ORIENTATION_BOTTOM_RIGHT: /* upside down */ imd->delay_alter_type = ALTER_ROTATE_180; break; case EXIF_ORIENTATION_BOTTOM_LEFT: /* flipped */ imd->delay_alter_type = ALTER_FLIP; break; case EXIF_ORIENTATION_LEFT_TOP: /* not implemented -- too wacky to fix in one step */ break; case EXIF_ORIENTATION_RIGHT_TOP: /* rotated -90 (270) */ imd->delay_alter_type = ALTER_ROTATE_90; break; case EXIF_ORIENTATION_RIGHT_BOTTOM: /* not implemented -- too wacky to fix in one step */ break; case EXIF_ORIENTATION_LEFT_BOTTOM: /* rotated 90 */ imd->delay_alter_type = ALTER_ROTATE_90_CC; break; default: /* The other values are out of range */ break; } } exif_free(ed); } if (imd->delay_alter_type != ALTER_NONE) { image_alter_real(imd, imd->delay_alter_type, clamp); }}/* *------------------------------------------------------------------- * read ahead (prebuffer) *------------------------------------------------------------------- */static void image_read_ahead_cancel(ImageWindow *imd){ if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path); image_loader_free(imd->read_ahead_il); imd->read_ahead_il = NULL; if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf); imd->read_ahead_pixbuf = NULL; g_free(imd->read_ahead_path); imd->read_ahead_path = NULL;}static void image_read_ahead_done_cb(ImageLoader *il, gpointer data){ ImageWindow *imd = data; if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path); imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il); if (imd->read_ahead_pixbuf) { g_object_ref(imd->read_ahead_pixbuf); } else { imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN); } image_loader_free(imd->read_ahead_il); imd->read_ahead_il = NULL; image_complete_util(imd, TRUE);}static void image_read_ahead_error_cb(ImageLoader *il, gpointer data){ /* we even treat errors as success, maybe at least some of the file was ok */ image_read_ahead_done_cb(il, data);}static void image_read_ahead_start(ImageWindow *imd){ /* already started ? */ if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return; /* still loading ?, do later */ if (imd->il) return; if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path); imd->read_ahead_il = image_loader_new(imd->read_ahead_path); image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd); if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd)) { image_read_ahead_cancel(imd); image_complete_util(imd, TRUE); }}static void image_read_ahead_set(ImageWindow *imd, const gchar *path){ if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return; image_read_ahead_cancel(imd); imd->read_ahead_path = g_strdup(path); if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path); image_read_ahead_start(imd);}/* *------------------------------------------------------------------- * post buffering *------------------------------------------------------------------- */static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf){ g_free(imd->prev_path); if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf); if (path && pixbuf) { imd->prev_path = g_strdup(path); g_object_ref(pixbuf); imd->prev_pixbuf = pixbuf; } else { imd->prev_path = NULL; imd->prev_pixbuf = NULL; } if (debug) printf("post buffer set: %s\n", path);}static gint image_post_buffer_get(ImageWindow *imd){ gint success; if (imd->prev_pixbuf && imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0) { image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd)); success = TRUE; } else { success = FALSE; } if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf); imd->prev_pixbuf = NULL; g_free(imd->prev_path); imd->prev_path = NULL; return success;}/* *------------------------------------------------------------------- * loading *------------------------------------------------------------------- */static void image_load_pixbuf_ready(ImageWindow *imd){ if (image_get_pixbuf(imd) || !imd->il) return; image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));}static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data){ ImageWindow *imd = data; PixbufRenderer *pr; pr = (PixbufRenderer *)imd->pr; if (imd->delay_flip && pr->pixbuf != image_loader_get_pixbuf(il)) { return; } if (!pr->pixbuf) image_load_pixbuf_ready(imd); pixbuf_renderer_area_changed(pr, x, y, w, h);}static void image_load_done_cb(ImageLoader *il, gpointer data){ ImageWindow *imd = data; if (debug) printf ("image done\n"); g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL); if (imd->delay_flip && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) { g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL); image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd)); } image_loader_free(imd->il); imd->il = NULL; image_post_process(imd, TRUE); image_read_ahead_start(imd);}static void image_load_error_cb(ImageLoader *il, gpointer data){ if (debug) printf ("image error\n"); /* even on error handle it like it was done, * since we have a pixbuf with _something_ */ image_load_done_cb(il, data);}#ifdef IMAGE_THROTTLE_LARGER_IMAGESstatic void image_load_buffer_throttle(ImageLoader *il){ if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return; /* Larger image files usually have larger chunks of data per pixel... * So increase the buffer read size so that the rendering chunks called * are also larger. */ image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -