diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 12 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/displaywindow.c | 602 | ||||
-rw-r--r-- | src/displaywindow.h | 62 | ||||
-rw-r--r-- | src/hdf5-file.c | 179 | ||||
-rw-r--r-- | src/hdf5-file.h | 15 | ||||
-rw-r--r-- | src/hdfsee.c | 85 | ||||
-rw-r--r-- | src/hdfsee.h | 24 | ||||
-rw-r--r-- | src/render.c | 183 | ||||
-rw-r--r-- | src/render.h | 29 |
11 files changed, 1163 insertions, 40 deletions
diff --git a/Makefile.am b/Makefile.am index b5e1b3fe..c99ab44a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ EXTRA_DIST = configure src/cell.h src/hdf5-file.h src/image.h src/relrod.h \ src/utils.h src/diffraction.h src/detector.h src/ewald.h \ src/sfac.h src/intensities.h src/reflections.h -SUBDIRS = src +SUBDIRS = src data diff --git a/configure.ac b/configure.ac index e5c8383d..1005c613 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,13 @@ AC_ARG_WITH(hdf5, HDF5_LIBS="-L$withval/lib -lhdf5"], [HDF5_LIBS="-lhdf5"]) -CFLAGS="$CFLAGS $HDF5_CFLAGS" -LIBS="$LIBS $HDF5_LIBS -lm -lz -lgsl -lgslcblas" +havegtk=false +AM_PATH_GTK_2_0(2.0.0, havegtk=true, +AC_MSG_WARN([GTK not found. hdfsee will not be built.])) -AC_OUTPUT(Makefile src/Makefile) +AM_CONDITIONAL([HAVE_GTK], test x$havegtk = xtrue) + +CFLAGS="$CFLAGS $HDF5_CFLAGS $GTK_CFLAGS" +LIBS="$LIBS $HDF5_LIBS -lm -lz -lgsl -lgslcblas $GTK_LIBS -lgthread-2.0" + +AC_OUTPUT(Makefile src/Makefile data/Makefile) 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 */ |