aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.org.uk>2009-12-15 05:54:51 -0800
committerThomas White <taw@bitwiz.org.uk>2009-12-16 01:54:12 -0800
commita97808a51b9e15df23307115ca889e01c604c0dc (patch)
tree67c6987fe5f561616d17a548beeb0b05714c2204 /src
parent4d4aa2f6e2a98461d022d6ce494c58b04b586711 (diff)
Introduce 'hdfsee'
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am10
-rw-r--r--src/displaywindow.c602
-rw-r--r--src/displaywindow.h62
-rw-r--r--src/hdf5-file.c179
-rw-r--r--src/hdf5-file.h15
-rw-r--r--src/hdfsee.c85
-rw-r--r--src/hdfsee.h24
-rw-r--r--src/render.c183
-rw-r--r--src/render.h29
9 files changed, 1153 insertions, 36 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 81b52045..758fcc35 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,11 @@
bin_PROGRAMS = pattern_sim process_hkl get_hkl
+if HAVE_GTK
+bin_PROGRAMS += hdfsee
+endif
+
AM_CFLAGS = -Wall
+AM_CPPFLAGS = -DDATADIR=\""$(datadir)"\"
pattern_sim_SOURCES = pattern_sim.c diffraction.c utils.c image.c cell.c \
hdf5-file.c ewald.c detector.c sfac.c intensities.c \
@@ -11,5 +16,10 @@ process_hkl_SOURCES = process_hkl.c sfac.c statistics.c cell.c utils.c \
reflections.c
process_hkl_LDADD = @LIBS@
+if HAVE_GTK
+hdfsee_SOURCES = hdfsee.c displaywindow.c render.c hdf5-file.c
+hdfsee_LDADD = @LIBS@
+endif
+
get_hkl_SOURCES = get_hkl.c sfac.c cell.c utils.c reflections.c
get_hkl_LDADD = @LIBS@
diff --git a/src/displaywindow.c b/src/displaywindow.c
new file mode 100644
index 00000000..2abfdab4
--- /dev/null
+++ b/src/displaywindow.c
@@ -0,0 +1,602 @@
+/*
+ * displaywindow.c
+ *
+ * Quick yet non-crappy HDF viewer
+ *
+ * (c) 2006-2009 Thomas White <thomas.white@desy.de>
+ *
+ * Part of CrystFEL - crystallography with a FEL
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "displaywindow.h"
+#include "render.h"
+#include "hdf5-file.h"
+#include "hdfsee.h"
+
+
+#define INITIAL_BINNING 2
+
+
+void displaywindow_error(DisplayWindow *dw, const char *message)
+{
+ GtkWidget *window;
+
+ window = gtk_message_dialog_new(GTK_WINDOW(dw->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE, message);
+
+ g_signal_connect_swapped(window, "response",
+ G_CALLBACK(gtk_widget_destroy), window);
+ gtk_widget_show(window);
+}
+
+
+void displaywindow_update(DisplayWindow *dw)
+{
+ gint width;
+ GdkGeometry geom;
+
+ if ( dw->hdfile != NULL ) {
+ dw->width = hdfile_get_width(dw->hdfile)/dw->binning;
+ dw->height = hdfile_get_height(dw->hdfile)/dw->binning;
+ } else {
+ dw->width = 320;
+ dw->height = 320;
+ }
+
+ width = dw->width;
+ if ( dw->show_col_scale ) width += 20;
+
+ gtk_widget_set_size_request(GTK_WIDGET(dw->drawingarea), width,
+ dw->height);
+ geom.min_width = -1;
+ geom.min_height = -1;
+ geom.max_width = -1;
+ geom.max_height = -1;
+ gtk_window_set_geometry_hints(GTK_WINDOW(dw->window),
+ GTK_WIDGET(dw->drawingarea), &geom,
+ GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+
+ if ( dw->pixbuf != NULL ) {
+ gdk_pixbuf_unref(dw->pixbuf);
+ }
+ if ( dw->hdfile != NULL ) {
+ dw->pixbuf = render_get_image(dw->hdfile, dw->binning,
+ dw->boostint, dw->monochrome);
+ } else {
+ dw->pixbuf = NULL;
+ }
+
+ if ( dw->col_scale != NULL ) {
+ gdk_pixbuf_unref(dw->col_scale);
+ }
+ dw->col_scale = render_get_colour_scale(20, dw->height, dw->monochrome);
+
+ gdk_window_invalidate_rect(dw->drawingarea->window, NULL, FALSE);
+}
+
+
+/* Window closed - clean up */
+static gint displaywindow_closed(GtkWidget *window, DisplayWindow *dw)
+{
+ if ( dw->hdfile != NULL ) {
+ hdfile_close(dw->hdfile);
+ }
+
+ /* Notify 'main', so it can update the master list */
+ hdfsee_window_closed(dw);
+
+ return 0;
+}
+
+
+static gboolean displaywindow_expose(GtkWidget *da, GdkEventExpose *event,
+ DisplayWindow *dw)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create(da->window);
+
+ /* Blank white background */
+ cairo_rectangle(cr, 0.0, 0.0, da->allocation.width,
+ da->allocation.height);
+ cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
+ cairo_fill(cr);
+
+ cairo_destroy(cr);
+
+ if ( dw->pixbuf != NULL ) {
+ gdk_draw_pixbuf(da->window,
+ da->style->bg_gc[GTK_WIDGET_STATE(da)],
+ dw->pixbuf,
+ 0, 0, 0, 0, dw->width, dw->height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+ }
+
+ if ( (dw->show_col_scale) && (dw->col_scale != NULL) ) {
+ gdk_draw_pixbuf(da->window,
+ da->style->bg_gc[GTK_WIDGET_STATE(da)],
+ dw->col_scale,
+ 0, 0, dw->width, 0, 20, dw->height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+ }
+
+ return FALSE;
+}
+
+
+static gint displaywindow_close(GtkWidget *widget, DisplayWindow *dw)
+{
+ gtk_widget_destroy(dw->window);
+ return 0;
+}
+
+
+static gint displaywindow_set_binning_response(GtkWidget *widget, gint response,
+ DisplayWindow *dw)
+{
+ int done = 1;
+
+ if ( response == GTK_RESPONSE_OK ) {
+
+ const char *sbinning;
+ int binning;
+ int scanval;
+
+ sbinning = gtk_entry_get_text(
+ GTK_ENTRY(dw->binning_dialog->entry));
+ scanval = sscanf(sbinning, "%u", &binning);
+ if ( (scanval != 1) || (binning <= 0) ) {
+ displaywindow_error(dw,
+ "Please enter a positive integer for the "
+ "binning factor.");
+ done = 0;
+ } else {
+ if ((binning < hdfile_get_width(dw->hdfile)/10)
+ && (binning < hdfile_get_height(dw->hdfile)/10)) {
+ dw->binning = binning;
+ displaywindow_update(dw);
+ } else {
+ displaywindow_error(dw,
+ "Please enter a sensible value for "
+ "the binning factor.");
+ done = 0;
+ }
+ }
+ }
+
+ if ( done ) {
+ gtk_widget_destroy(dw->binning_dialog->window);
+ }
+
+ return 0;
+
+}
+
+
+static gint displaywindow_set_binning_destroy(GtkWidget *widget,
+ DisplayWindow *dw)
+{
+ free(dw->binning_dialog);
+ dw->binning_dialog = NULL;
+ return 0;
+}
+
+
+static gint displaywindow_set_binning_response_ac(GtkWidget *widget,
+ DisplayWindow *dw)
+{
+ return displaywindow_set_binning_response(widget, GTK_RESPONSE_OK, dw);
+}
+
+
+/* Create a window to ask the user for a new binning factor */
+static gint displaywindow_set_binning(GtkWidget *widget, DisplayWindow *dw)
+{
+ BinningDialog *bd;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ char tmp[64];
+
+ if ( dw->binning_dialog != NULL ) {
+ return 0;
+ }
+
+ if ( dw->hdfile == NULL ) {
+ return 0;
+ }
+
+ bd = malloc(sizeof(BinningDialog));
+ if ( bd == NULL ) return 0;
+ dw->binning_dialog = bd;
+
+ bd->window = gtk_dialog_new_with_buttons("Set Binning",
+ GTK_WINDOW(dw->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+ hbox = gtk_hbox_new(TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bd->window)->vbox),
+ GTK_WIDGET(hbox), FALSE, FALSE, 7);
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5);
+
+ table = gtk_table_new(3, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 5);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0);
+
+ label = gtk_label_new("Smaller numbers mean larger images on screen");
+ gtk_label_set_markup(GTK_LABEL(label),
+ "<span style=\"italic\" weight=\"light\">"
+ "Smaller numbers mean larger images on screen</span>");
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label),
+ 1, 3, 1, 2);
+
+ snprintf(tmp, 63, "Raw image size: %i by %i pixels",
+ (int)hdfile_get_width(dw->hdfile),
+ (int)hdfile_get_height(dw->hdfile));
+ label = gtk_label_new(tmp);
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label),
+ 1, 3, 2, 3);
+
+ label = gtk_label_new("Binning Factor:");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label),
+ 1, 2, 3, 4);
+
+ bd->entry = gtk_entry_new();
+ snprintf(tmp, 63, "%i", dw->binning);
+ gtk_entry_set_text(GTK_ENTRY(bd->entry), tmp);
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(bd->entry),
+ 2, 3, 3, 4);
+
+ g_signal_connect(G_OBJECT(bd->entry), "activate",
+ G_CALLBACK(displaywindow_set_binning_response_ac), dw);
+ g_signal_connect(G_OBJECT(bd->window), "response",
+ G_CALLBACK(displaywindow_set_binning_response), dw);
+ g_signal_connect(G_OBJECT(bd->window), "destroy",
+ G_CALLBACK(displaywindow_set_binning_destroy), dw);
+ gtk_window_set_resizable(GTK_WINDOW(bd->window), FALSE);
+ gtk_widget_show_all(bd->window);
+ gtk_widget_grab_focus(GTK_WIDGET(bd->entry));
+
+ return 0;
+}
+
+
+static gint displaywindow_set_boostint_response(GtkWidget *widget,
+ gint response,
+ DisplayWindow *dw)
+{
+ int done = 1;
+
+ if ( response == GTK_RESPONSE_OK ) {
+
+ const char *sboostint;
+ int boostint;
+ int scanval;
+
+ sboostint = gtk_entry_get_text(
+ GTK_ENTRY(dw->boostint_dialog->entry));
+ scanval = sscanf(sboostint, "%u", &boostint);
+ if ( (scanval != 1) || (boostint <= 0) ) {
+ displaywindow_error(dw, "Please enter a positive "
+ "integer for the intensity boost "
+ "factor.");
+ done = 0;
+ } else {
+ dw->boostint = boostint;
+ displaywindow_update(dw);
+ }
+ }
+
+ if ( done ) {
+ gtk_widget_destroy(dw->boostint_dialog->window);
+ }
+
+ return 0;
+}
+
+
+static gint displaywindow_set_boostint_destroy(GtkWidget *widget,
+ DisplayWindow *dw)
+{
+ free(dw->boostint_dialog);
+ dw->boostint_dialog = NULL;
+ return 0;
+}
+
+
+static gint displaywindow_set_boostint_response_ac(GtkWidget *widget,
+ DisplayWindow *dw)
+{
+ return displaywindow_set_boostint_response(widget, GTK_RESPONSE_OK, dw);
+}
+
+
+/* Create a window to ask the user for a new intensity boost factor */
+static gint displaywindow_set_boostint(GtkWidget *widget, DisplayWindow *dw)
+{
+ BoostIntDialog *bd;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ char tmp[64];
+
+ if ( dw->boostint_dialog != NULL ) {
+ return 0;
+ }
+
+ if ( dw->hdfile == NULL ) {
+ return 0;
+ }
+
+ bd = malloc(sizeof(BoostIntDialog));
+ if ( bd == NULL ) return 0;
+ dw->boostint_dialog = bd;
+
+ bd->window = gtk_dialog_new_with_buttons("Intensity Boost",
+ GTK_WINDOW(dw->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE,
+ GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+ hbox = gtk_hbox_new(TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(bd->window)->vbox),
+ GTK_WIDGET(hbox), FALSE, FALSE, 7);
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5);
+
+ table = gtk_table_new(3, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 5);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0);
+
+ label = gtk_label_new("Boost Factor:");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label),
+ 1, 2, 3, 4);
+
+ bd->entry = gtk_entry_new();
+ snprintf(tmp, 63, "%i", dw->boostint);
+ gtk_entry_set_text(GTK_ENTRY(bd->entry), tmp);
+ gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(bd->entry),
+ 2, 3, 3, 4);
+
+ g_signal_connect(G_OBJECT(bd->entry), "activate",
+ G_CALLBACK(displaywindow_set_boostint_response_ac),
+ dw);
+ g_signal_connect(G_OBJECT(bd->window), "response",
+ G_CALLBACK(displaywindow_set_boostint_response), dw);
+ g_signal_connect(G_OBJECT(bd->window), "destroy",
+ G_CALLBACK(displaywindow_set_boostint_destroy), dw);
+ gtk_window_set_resizable(GTK_WINDOW(bd->window), FALSE);
+ gtk_widget_show_all(bd->window);
+ gtk_widget_grab_focus(GTK_WIDGET(bd->entry));
+
+ return 0;
+}
+
+
+static gint displaywindow_about(GtkWidget *widget, DisplayWindow *dw)
+{
+ GtkWidget *window;
+
+ const gchar *authors[] = {
+ "Thomas White <taw@physics.org>",
+ "Erica Bithell <egb10@cam.ac.uk>",
+ "Alex Eggeman <ase25@cam.ac.uk>",
+ NULL
+ };
+
+ window = gtk_about_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(window),
+ GTK_WINDOW(dw->window));
+
+ gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(window), "hdfsee");
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(window), PACKAGE_VERSION);
+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(window),
+ "(c) 2006-2009 Thomas White <taw@physics.org> and others");
+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window),
+ "Quick viewer for HDF files");
+ gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window),
+ "(c) 2006-2009 Thomas White <taw@physics.org>\n");
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window),
+ "http://www.bitwiz.org.uk/");
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors);
+
+ g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy),
+ NULL);
+
+ gtk_widget_show_all(window);
+
+ return 0;
+}
+
+
+static gint displaywindow_set_colscale(GtkWidget *widget, DisplayWindow *dw)
+{
+ dw->show_col_scale = 1 - dw->show_col_scale;
+ displaywindow_update(dw);
+ return 0;
+}
+
+
+static gint displaywindow_set_mono(GtkWidget *widget, DisplayWindow *dw)
+{
+ dw->monochrome = 1 - dw->monochrome;
+ displaywindow_update(dw);
+ return 0;
+}
+
+
+static void displaywindow_addui_callback(GtkUIManager *ui, GtkWidget *widget,
+ GtkContainer *container)
+{
+ gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0);
+
+ /* Enable overflow menu if this is a toolbar */
+ if ( GTK_IS_TOOLBAR(widget) ) {
+ gtk_toolbar_set_show_arrow(GTK_TOOLBAR(widget), TRUE);
+ }
+}
+
+
+static void displaywindow_addmenubar(DisplayWindow *dw, GtkWidget *vbox)
+{
+ GError *error = NULL;
+ GtkActionEntry entries[] = {
+
+ { "FileAction", NULL, "_File", NULL, NULL, NULL },
+ { "CloseAction", GTK_STOCK_CLOSE, "_Close", NULL, NULL,
+ G_CALLBACK(displaywindow_close) },
+
+ { "ViewAction", NULL, "_View", NULL, NULL, NULL },
+ { "BinningAction", NULL, "Set Binning...", "F3", NULL,
+ G_CALLBACK(displaywindow_set_binning) },
+ { "BoostIntAction", NULL, "Boost Intensity...", "F5", NULL,
+ G_CALLBACK(displaywindow_set_boostint) },
+
+ { "HelpAction", NULL, "_Help", NULL, NULL, NULL },
+ { "AboutAction", GTK_STOCK_ABOUT, "_About hdfileView...",
+ NULL, NULL,
+ G_CALLBACK(displaywindow_about) },
+
+ };
+ guint n_entries = G_N_ELEMENTS(entries);
+
+ GtkToggleActionEntry toggles[] = {
+ { "ColScaleAction", NULL, "Show Colour Scale", NULL, NULL,
+ G_CALLBACK(displaywindow_set_colscale), FALSE },
+ { "MonoAction", NULL, "Monochrome", NULL, NULL,
+ G_CALLBACK(displaywindow_set_mono), FALSE },
+ };
+ guint n_toggles = G_N_ELEMENTS(toggles);
+
+ dw->action_group = gtk_action_group_new("hdfseedisplaywindow");
+ gtk_action_group_add_actions(dw->action_group, entries, n_entries, dw);
+ gtk_action_group_add_toggle_actions(dw->action_group, toggles,
+ n_toggles, dw);
+
+ dw->ui = gtk_ui_manager_new();
+ gtk_ui_manager_insert_action_group(dw->ui, dw->action_group, 0);
+ g_signal_connect(dw->ui, "add_widget",
+ G_CALLBACK(displaywindow_addui_callback), vbox);
+ if ( gtk_ui_manager_add_ui_from_file(dw->ui,
+ DATADIR"/hdfsee/displaywindow.ui", &error) == 0 ) {
+ fprintf(stderr, "Error loading message window menu bar: %s\n",
+ error->message);
+ return;
+ }
+
+ gtk_window_add_accel_group(GTK_WINDOW(dw->window),
+ gtk_ui_manager_get_accel_group(dw->ui));
+ gtk_ui_manager_ensure_update(dw->ui);
+}
+
+
+static void displaywindow_disable(DisplayWindow *dw)
+{
+ GtkWidget *w;
+
+ w = gtk_ui_manager_get_widget(dw->ui,
+ "/ui/displaywindow/view/binning");
+ gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+
+ w = gtk_ui_manager_get_widget(dw->ui,
+ "/ui/displaywindow/view/boostint");
+ gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+
+ w = gtk_ui_manager_get_widget(dw->ui,
+ "/ui/displaywindow/tools/histogram");
+ gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+}
+
+
+DisplayWindow *displaywindow_open(const char *filename)
+{
+ DisplayWindow *dw;
+ char *title;
+ GtkWidget *vbox;
+
+ dw = malloc(sizeof(DisplayWindow));
+ if ( dw == NULL ) return NULL;
+ dw->pixbuf = NULL;
+ dw->binning_dialog = NULL;
+ dw->show_col_scale = 0;
+ dw->col_scale = NULL;
+ dw->monochrome = 0;
+ dw->boostint_dialog = NULL;
+ dw->boostint = 1;
+
+ dw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ if ( filename == NULL ) {
+ title = strdup("No file - hdfsee");
+ } else {
+ title = malloc(strlen(basename(filename))+14);
+ sprintf(title, "%s - hdfsee", basename(filename));
+ }
+ gtk_window_set_title(GTK_WINDOW(dw->window), title);
+ free(title);
+
+ g_signal_connect(G_OBJECT(dw->window), "destroy",
+ G_CALLBACK(displaywindow_closed), dw);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(dw->window), vbox);
+ displaywindow_addmenubar(dw, vbox);
+
+ dw->drawingarea = gtk_drawing_area_new();
+ gtk_box_pack_start(GTK_BOX(vbox), dw->drawingarea, TRUE, TRUE, 0);
+
+ g_signal_connect(GTK_OBJECT(dw->drawingarea), "expose-event",
+ G_CALLBACK(displaywindow_expose), dw);
+
+ /* Open the file, if any */
+ if ( filename != NULL ) {
+
+ dw->hdfile = hdfile_open(filename);
+ if ( dw->hdfile == NULL ) {
+ fprintf(stderr, "Couldn't open file '%s'\n", filename);
+ displaywindow_disable(dw);
+ }
+ if ( hdfile_set_image(dw->hdfile, "/data/data") ) {
+ fprintf(stderr, "Couldn't select path\n");
+ displaywindow_disable(dw);
+ }
+
+ } else {
+ dw->hdfile = NULL;
+ displaywindow_disable(dw);
+ }
+
+ gtk_window_set_resizable(GTK_WINDOW(dw->window), FALSE);
+ gtk_widget_show_all(dw->window);
+
+ dw->binning = INITIAL_BINNING;
+ displaywindow_update(dw);
+
+ return dw;
+}
diff --git a/src/displaywindow.h b/src/displaywindow.h
new file mode 100644
index 00000000..587cdb23
--- /dev/null
+++ b/src/displaywindow.h
@@ -0,0 +1,62 @@
+/*
+ * displaywindow.h
+ *
+ * Quick yet non-crappy HDF viewer
+ *
+ * (c) 2006-2009 Thomas White <thomas.white@desy.de>
+ *
+ * Part of CrystFEL - crystallography with a FEL
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef DISPLAYWINDOW_H
+#define DISPLAYWINDOW_H
+
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *entry;
+} BinningDialog;
+
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *entry;
+} BoostIntDialog;
+
+
+typedef struct {
+
+ GtkWidget *window;
+ GtkWidget *drawingarea;
+ GtkUIManager *ui;
+ GtkActionGroup *action_group;
+ GdkPixbuf *pixbuf;
+
+ struct hdfile *hdfile;
+
+ /* Dialog boxes */
+ BinningDialog *binning_dialog;
+ BoostIntDialog *boostint_dialog;
+
+ int width;
+ int height; /* Size of the drawing area */
+ int binning;
+ int boostint;
+
+ int show_col_scale;
+ int monochrome;
+ GdkPixbuf *col_scale;
+
+} DisplayWindow;
+
+/* Open an image display window showing the given filename, or NULL */
+extern DisplayWindow *displaywindow_open(const char *filename);
+
+
+#endif /* DISPLAYWINDOW_H */
diff --git a/src/hdf5-file.c b/src/hdf5-file.c
index c29191f4..379bd14e 100644
--- a/src/hdf5-file.c
+++ b/src/hdf5-file.c
@@ -23,6 +23,143 @@
#include "hdf5-file.h"
+struct hdfile {
+
+ const char *path; /* Current data path */
+
+ struct image *image;
+
+ size_t nx; /* Image width */
+ size_t ny; /* Image height */
+
+ hid_t fh; /* HDF file handle */
+ hid_t dh; /* Dataset handle */
+ hid_t sh; /* Dataspace handle */
+};
+
+
+struct hdfile *hdfile_open(const char *filename)
+{
+ struct hdfile *f;
+
+ f = malloc(sizeof(struct hdfile));
+ if ( f == NULL ) return NULL;
+
+ f->fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT);
+ if ( f->fh < 0 ) {
+ ERROR("Couldn't open file: %s\n", filename);
+ free(f);
+ return NULL;
+ }
+
+ return f;
+}
+
+
+int hdfile_set_image(struct hdfile *f, const char *path)
+{
+ hsize_t size[2];
+ hsize_t max_size[2];
+
+ f->dh = H5Dopen(f->fh, path, H5P_DEFAULT);
+ if ( f->dh < 0 ) {
+ ERROR("Couldn't open dataset\n");
+ return -1;
+ }
+
+ f->sh = H5Dget_space(f->dh);
+ if ( H5Sget_simple_extent_ndims(f->sh) != 2 ) {
+ ERROR("Dataset is not two-dimensional\n");
+ return -1;
+ }
+
+ H5Sget_simple_extent_dims(f->sh, size, max_size);
+
+ f->nx = size[0];
+ f->ny = size[1];
+
+ return 0;
+}
+
+
+int hdfile_get_width(struct hdfile *f)
+{
+ return f->nx;
+}
+
+
+int hdfile_get_height(struct hdfile *f)
+{
+ return f->ny;
+}
+
+
+void hdfile_close(struct hdfile *f)
+{
+ H5Fclose(f->fh);
+ free(f->image);
+ free(f);
+}
+
+
+static void *hdfile_bin(uint16_t *in, int inw, int inh,
+ int binning, uint16_t *maxp)
+{
+ uint16_t *data;
+ int x, y;
+ int w, h;
+ uint16_t max;
+
+ w = inw / binning;
+ h = inh / binning; /* Some pixels might get discarded */
+
+ data = malloc(w*h*sizeof(uint16_t));
+ max = 0;
+
+ for ( x=0; x<w; x++ ) {
+ for ( y=0; y<h; y++ ) {
+
+ /* Big enough to hold large values */
+ unsigned long long int total;
+ size_t xb, yb;
+
+ total = 0;
+ for ( xb=0; xb<binning; xb++ ) {
+ for ( yb=0; yb<binning; yb++ ) {
+
+ total += in[inh*(binning*x+xb)+((inh-binning*y)+yb)];
+
+ }
+ }
+
+ data[x+w*y] = total / (binning * binning);
+ if ( data[x+w*y] > max ) max = data[x+w*y];
+
+ }
+ }
+
+ *maxp = max;
+ return data;
+}
+
+
+uint16_t *hdfile_get_image_binned(struct hdfile *f, int binning, uint16_t *max)
+{
+ struct image *image;
+ uint16_t *data;
+
+ image = malloc(sizeof(struct image));
+ if ( image == NULL ) return NULL;
+
+ hdf5_read(f, image);
+ f->image = image;
+
+ data = hdfile_bin(image->data, f->nx, f->ny, binning, max);
+
+ return data;
+}
+
+
int hdf5_write(const char *filename, const uint16_t *data,
int width, int height)
{
@@ -78,54 +215,26 @@ int hdf5_write(const char *filename, const uint16_t *data,
}
-int hdf5_read(struct image *image, const char *filename)
+int hdf5_read(struct hdfile *f, struct image *image)
{
- hid_t fh, sh, dh; /* File, dataspace and data handles */
herr_t r;
- hsize_t size[2];
- hsize_t max_size[2];
uint16_t *buf;
- fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT);
- if ( fh < 0 ) {
- /* TODO: Try other formats here. */
- ERROR("Couldn't open file: %s\n", filename);
- return 1;
- }
-
- dh = H5Dopen(fh, "/data/data", H5P_DEFAULT);
- if ( dh < 0 ) {
- ERROR("Couldn't open dataset\n");
- H5Fclose(fh);
- return 1;
- }
-
- sh = H5Dget_space(dh);
- if ( H5Sget_simple_extent_ndims(sh) != 2 ) {
- ERROR("Dataset is not two-dimensional\n");
- H5Fclose(fh);
- return 1;
- }
-
- H5Sget_simple_extent_dims(sh, size, max_size);
+ buf = malloc(sizeof(float)*f->nx*f->ny);
- buf = malloc(sizeof(float)*size[0]*size[1]);
-
- r = H5Dread(dh, H5T_NATIVE_UINT16, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf);
+ r = H5Dread(f->dh, H5T_NATIVE_UINT16, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, buf);
if ( r < 0 ) {
ERROR("Couldn't read data\n");
- H5Dclose(dh);
- H5Fclose(fh);
+ H5Dclose(f->dh);
return 1;
}
image->data = buf;
- image->height = size[0];
- image->width = size[1];
+ image->height = f->nx;
+ image->width = f->ny;
image->x_centre = image->width/2;
image->y_centre = image->height/2;
- H5Fclose(fh);
-
return 0;
}
diff --git a/src/hdf5-file.h b/src/hdf5-file.h
index a263368a..ec9e3a85 100644
--- a/src/hdf5-file.h
+++ b/src/hdf5-file.h
@@ -18,9 +18,22 @@
#include <stdint.h>
+#include "image.h"
+
+struct hdfile;
+
+
extern int hdf5_write(const char *filename, const uint16_t *data,
int width, int height);
-extern int hdf5_read(struct image *image, const char *filename);
+extern int hdf5_read(struct hdfile *f, struct image *image);
+
+extern struct hdfile *hdfile_open(const char *filename);
+extern int hdfile_set_image(struct hdfile *f, const char *path);
+extern int hdfile_get_width(struct hdfile *f);
+extern int hdfile_get_height(struct hdfile *f);
+extern uint16_t *hdfile_get_image_binned(struct hdfile *hdfile,
+ int binning, uint16_t *maxp);
+extern void hdfile_close(struct hdfile *f);
#endif /* HDF5_H */
diff --git a/src/hdfsee.c b/src/hdfsee.c
new file mode 100644
index 00000000..073ee036
--- /dev/null
+++ b/src/hdfsee.c
@@ -0,0 +1,85 @@
+/*
+ * hdfsee.c
+ *
+ * Quick yet non-crappy HDF viewer
+ *
+ * (c) 2006-2009 Thomas White <thomas.white@desy.de>
+ *
+ * Part of CrystFEL - crystallography with a FEL
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gthread.h>
+
+#include "displaywindow.h"
+#include "utils.h"
+
+
+/* Global program state */
+DisplayWindow *main_window_list[64];
+size_t main_n_windows = 0;
+
+
+/* Called to notify that an image display window has been closed */
+void hdfsee_window_closed(DisplayWindow *dw)
+{
+ size_t i;
+
+ for ( i=0; i<main_n_windows; i++ ) {
+
+ if ( main_window_list[i] == dw ) {
+
+ size_t j;
+
+ for ( j=i+1; j<main_n_windows; j++ ) {
+ main_window_list[j] = main_window_list[j+1];
+ }
+
+ }
+
+ }
+
+ main_n_windows--;
+
+ if ( main_n_windows == 0 ) gtk_exit(0);
+
+}
+
+
+int main(int argc, char *argv[])
+{
+ g_thread_init(NULL);
+ gtk_init(&argc, &argv);
+
+ if ( argc == 1 ) {
+
+ main_n_windows = 1;
+ main_window_list[0] = displaywindow_open(NULL);
+ if ( main_window_list[0] == NULL ) {
+ ERROR("Couldn't open display window\n");
+ }
+
+ } else {
+
+ size_t i;
+
+ main_n_windows = argc - 1;
+ for ( i=0; i<main_n_windows; i++ ) {
+ main_window_list[i] = displaywindow_open(argv[i+1]);
+ if ( main_window_list[i] == NULL ) {
+ ERROR("Couldn't open display window\n");
+ }
+ }
+
+ }
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/src/hdfsee.h b/src/hdfsee.h
new file mode 100644
index 00000000..4aab5f3e
--- /dev/null
+++ b/src/hdfsee.h
@@ -0,0 +1,24 @@
+/*
+ * hdfsee.c
+ *
+ * Quick yet non-crappy HDF viewer
+ *
+ * (c) 2006-2009 Thomas White <thomas.white@desy.de>
+ *
+ * Part of CrystFEL - crystallography with a FEL
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HDFSEE_H
+#define HDFSEE_H
+
+#include "displaywindow.h"
+
+extern void hdfsee_window_closed(DisplayWindow *dw);
+
+#endif /* HDFSEE_H */
diff --git a/src/render.c b/src/render.c
new file mode 100644
index 00000000..96e0b906
--- /dev/null
+++ b/src/render.c
@@ -0,0 +1,183 @@
+/*
+ * render.c
+ *
+ * Render a high dynamic range buffer in some sensible way
+ *
+ * (c) 2008-2009 Thomas White <taw27@cam.ac.uk>
+ *
+ * hdfileview - view Ditabis Micron images
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "hdf5-file.h"
+
+/* Set to 1 to measure mean intensity in a rectangle */
+#define MEASURE_INT 0
+
+#define RENDER_RGB \
+ \
+ int s; \
+ float p; \
+ \
+ s = val / (max/6); \
+ p = fmod(val, max/6); \
+ p /= (max/6); \
+ \
+ r = 0; g = 0; b = 0; \
+ \
+ switch ( s ) { \
+ case 0 : { /* Black to blue */ \
+ r = 0; g = 0; b = p*255; \
+ break; \
+ } \
+ case 1 : { /* Blue to green */ \
+ r = 0; g = 255*p; b = (1-p)*255; \
+ break; \
+ } \
+ case 2 : { /* Green to red */ \
+ r =p*255; g = (1-p)*255; b = 0; \
+ break; \
+ } \
+ case 3 : { /* Red to Orange */ \
+ r = 255; g = 127*p; b = 0; \
+ break; \
+ } \
+ case 4 : { /* Orange to Yellow */ \
+ r = 255; g = 127 + 127*p; b = 0; \
+ break; \
+ } \
+ case 5 : { /* Yellow to White */ \
+ r = 255; g = 255; b = 255*p; \
+ break; \
+ } \
+ case 6 : { /* Pixel has hit the maximum value */ \
+ r = 255; g = 255; b = 255; \
+ break; \
+ } \
+ default : { /* Above saturation */ \
+ r = 255; g = 255; b = 255; \
+ break; \
+ } \
+ }
+
+#define RENDER_MONO \
+ float p; \
+ p = (float)val / (float)max; \
+ r = 255.0*p; g = 255.0*p; b = 255.0*p;
+
+/* NB This function is shared between render_get_image() and
+ * render_get_colour_scale() */
+static void render_free_data(guchar *data, gpointer p)
+{
+ free(data);
+}
+
+/* Return a pixbuf containing a rendered version of the image after binning.
+ * This pixbuf might be scaled later - hopefully mostly in a downward
+ * direction. */
+GdkPixbuf *render_get_image(struct hdfile *hdfile, int binning, int boostint,
+ int monochrome)
+{
+ int mw, mh, w, h;
+ guchar *data;
+ uint16_t *hdr;
+ size_t x, y;
+ uint16_t max;
+
+ mw = hdfile_get_width(hdfile);
+ mh = hdfile_get_height(hdfile);
+ w = mw / binning;
+ h = mh / binning;
+
+ /* High dynamic range version */
+ hdr = hdfile_get_image_binned(hdfile, binning, &max);
+ if ( hdr == NULL ) return NULL;
+
+ /* Rendered (colourful) version */
+ data = malloc(3*w*h);
+ if ( data == NULL ) {
+ free(hdr);
+ return NULL;
+ }
+
+ max /= boostint;
+ if ( max <= 0.0 ) { max = 10.0; }
+ /* These x,y coordinates are measured relative to the bottom-left
+ * corner */
+ for ( y=0; y<h; y++ ) {
+ for ( x=0; x<w; x++ ) {
+
+ int val;
+ guchar r, g, b;
+
+ val = hdr[x+w*y];
+ if ( !monochrome ) {
+ RENDER_RGB
+ } else {
+ RENDER_MONO
+ }
+
+ /* Stuff inside square brackets makes this pixel go to
+ * the expected location in the pixbuf (which measures
+ * from the top-left corner */
+ data[3*( x+w*(h-1-y) )+0] = r;
+ data[3*( x+w*(h-1-y) )+1] = g;
+ data[3*( x+w*(h-1-y) )+2] = b;
+
+ }
+ }
+
+ /* Finished with this */
+ free(hdr);
+
+ /* Create the pixbuf from the 8-bit display data */
+ return gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8,
+ w, h, w*3, render_free_data, NULL);
+}
+
+GdkPixbuf *render_get_colour_scale(size_t w, size_t h, int monochrome)
+{
+ guchar *data;
+ size_t x, y;
+ int max;
+
+ data = malloc(3*w*h);
+ if ( data == NULL ) return NULL;
+
+ max = h;
+
+ for ( y=0; y<h; y++ ) {
+
+ guchar r, g, b;
+ int val;
+
+ val = y;
+ if ( !monochrome ) {
+ RENDER_RGB
+ } else {
+ RENDER_MONO
+ }
+
+ data[3*( 0+w*(h-1-y) )+0] = 0;
+ data[3*( 0+w*(h-1-y) )+1] = 0;
+ data[3*( 0+w*(h-1-y) )+2] = 0;
+ for ( x=1; x<w; x++ ) {
+ data[3*( x+w*(h-1-y) )+0] = r;
+ data[3*( x+w*(h-1-y) )+1] = g;
+ data[3*( x+w*(h-1-y) )+2] = b;
+ }
+
+ }
+
+ return gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8,
+ w, h, w*3, render_free_data, NULL);
+}
diff --git a/src/render.h b/src/render.h
new file mode 100644
index 00000000..85fe76ab
--- /dev/null
+++ b/src/render.h
@@ -0,0 +1,29 @@
+/*
+ * render.h
+ *
+ * Render a high dynamic range buffer in some sensible way
+ *
+ * (c) 2008 Thomas White <taw27@cam.ac.uk>
+ *
+ * micronview - view Ditabis Micron images
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef RENDER_H
+#define RENDER_H
+
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <stddef.h>
+
+extern GdkPixbuf *render_get_image(struct hdfile *micron, int binning,
+ int boostint, int monochrome);
+extern GdkPixbuf *render_get_colour_scale(size_t w, size_t h, int monochrome);
+
+
+#endif /* RENDER_H */