#include #include #include using namespace std; #include #include #include "haader.h" #include "image.h" struct Gui { GtkWidget *window; GtkWidget *canvas; GtkWidget *log_checkbox; GtkWidget *scale_exposure; GtkWidget *scale_compression; haader::HdrImageStack stack; haader::HdrImage hdr_image; haader::ResponseFunction response_function; haader::Image ldr_image; haader::Histogram hdr_histogram; haader::LdrHistogram ldr_histogram; cairo_surface_t *surface; double exposure_time; double compression; }; static cairo_surface_t* image_to_cairo_surface(const haader::Image &image) { unsigned int width = image.get_width(); unsigned int height = image.get_height(); const unsigned char *image_data = image.get_const_image_data(); cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); int stride = cairo_image_surface_get_stride(surf); unsigned char *surf_data = cairo_image_surface_get_data(surf); unsigned int i = 0; unsigned int k = 0; for (unsigned int y = 0; y < height; y++) { for (unsigned int x = 0; x < width; x++) { surf_data[i + x * 4] = image_data[k + x * 3 + 2]; surf_data[i + x * 4 + 1] = image_data[k + x * 3 + 1]; surf_data[i + x * 4 + 2] = image_data[k + x * 3]; } i += stride; k += width * 3; } return surf; } static void expose(Gui &gui) { gui.ldr_image = gui.hdr_image.expose(gui.exposure_time, gui.response_function, gui.compression); gui.ldr_histogram = gui.ldr_image.histogram(); if (gui.surface) { cairo_surface_destroy(gui.surface); gui.surface = NULL; } gui.surface = image_to_cairo_surface(gui.ldr_image); } static void create_log_image(Gui &gui) { gui.ldr_image = gui.hdr_image.get_log_image(); gui.ldr_histogram = gui.ldr_image.histogram(); if (gui.surface) { cairo_surface_destroy(gui.surface); gui.surface = NULL; } gui.surface = image_to_cairo_surface(gui.ldr_image); } static gboolean canvas_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_paint(cr); Gui *gui = (Gui*) user_data; if (!gui->surface) { return false; } if (gui->hdr_histogram.m_bins.size() != 256) { return false; } if (gui->ldr_histogram.m_bins.size() != 256) { return false; } cairo_set_source_surface(cr, gui->surface, 5, 5); cairo_paint(cr); // canvas rect double cx = gui->ldr_image.get_width() + 10.0; double cy = 5.0; double cw = 256; double ch = 256; cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); cairo_rectangle(cr, cx, cy, cw, ch); cairo_fill(cr); { double hist_scale = (256.0 / (double)(gui->hdr_histogram.m_max_freq)); for (unsigned int i = 0; i < 256; i++) { double y = gui->hdr_histogram.m_bins[i] * hist_scale; cairo_rectangle(cr, cx + i, cy + ch - y, 1, y); } cairo_set_source_rgba(cr, 1.0, 0.0, 0.2, 0.4); cairo_fill(cr); } { double hist_scale = (256.0 / (double)(gui->ldr_histogram.m_max_freq)); for (unsigned int i = 0; i < 256; i++) { double y = gui->ldr_histogram.m_bins[i] * hist_scale; cairo_rectangle(cr, cx + i, cy + ch - y, 1, y); } cairo_set_source_rgba(cr, 1.0, 0.0, 0.2, 0.9); cairo_fill(cr); } if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui->log_checkbox))) { // Draw response function double minl = gui->hdr_histogram.m_min_value; double maxl = gui->hdr_histogram.m_max_value; double log_exp = log(gui->exposure_time); double scale = (maxl - minl) / 256.0; for (int i = 0; i < 256; i++) { double lum = minl + i * scale; int ly = gui->response_function.lookup((lum + log_exp) * gui->compression); cairo_line_to(cr, cx + i, cy + ch - ly); } cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.8); cairo_set_line_width(cr, 1.0); cairo_stroke_preserve(cr); cairo_line_to(cr, cx + cw, cy + ch); cairo_line_to(cr, cx, cy + ch); cairo_close_path(cr); cairo_set_source_rgba(cr, 0.0, 0.5, 1.0, 0.2); cairo_fill(cr); } return FALSE; } void log_toggled(GtkToggleButton *button, gpointer user_data) { Gui *gui = (Gui*) user_data; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) { gtk_widget_set_sensitive(gui->scale_exposure, false); gtk_widget_set_sensitive(gui->scale_compression, false); create_log_image(*gui); } else { gtk_widget_set_sensitive(gui->scale_exposure, true); gtk_widget_set_sensitive(gui->scale_compression, true); expose(*gui); } gtk_widget_queue_draw(gui->canvas); } void scale_exposure_changed(GtkRange *range, gpointer user_data) { Gui *gui = (Gui*) user_data; gui->exposure_time = pow(2.0, gtk_range_get_value(range)); expose(*gui); gtk_widget_queue_draw(gui->canvas); } void scale_compression_changed(GtkRange *range, gpointer user_data) { Gui *gui = (Gui*) user_data; gui->compression = pow(2.0, gtk_range_get_value(range)); expose(*gui); gtk_widget_queue_draw(gui->canvas); } void file_select_clicked(GtkButton *button, gpointer user_data) { Gui *gui = (Gui*) user_data; GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open File", GTK_WINDOW(gui->window), GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(dialog), true); bool ok = true; if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { gui->stack = haader::HdrImageStack(); vector path_list; { GSList *list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); GSList *element = list; while (element != NULL) { path_list.push_back(string((const char*)(element->data))); g_free(element->data); element = g_slist_next(element); if (!ok) { break; } } g_slist_free(list); } if (path_list.size() == 1) { ok = gui->stack.read_from_csv_file(path_list[0].c_str()); } else { for (unsigned int i = 0; i < path_list.size(); i++) { ok = ok && gui->stack.add_from_file_path(path_list[i].c_str()); } } } if (ok) { // create HDR-image haader::InverseResponseFunction irf; if (gui->stack.get_inverse_response_function(irf, 4096)) { gui->hdr_image = gui->stack.get_hdr_image(irf); gui->ldr_image = gui->hdr_image.get_log_image(); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gui->log_checkbox), true); gui->hdr_histogram = gui->hdr_image.histogram(256); gui->ldr_histogram = gui->ldr_image.histogram(); if (gui->surface) { cairo_surface_destroy(gui->surface); gui->surface = NULL; } gui->surface = image_to_cairo_surface(gui->ldr_image); gui->response_function = irf.to_response_function(1024); } gtk_widget_queue_draw(gui->canvas); } gtk_widget_destroy (dialog); } int main(int argc, char *argv[]) { gtk_init(&argc, &argv); Gui gui; gui.surface = NULL; gui.exposure_time = 0.125; gui.compression = 1.0; gui.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(gui.window), "haader"); gtk_window_set_default_size(GTK_WINDOW(gui.window), 800, 600); GtkWidget *file_button = gtk_button_new_with_label("Select files"); gui.canvas = gtk_drawing_area_new(); gui.log_checkbox = gtk_check_button_new_with_label("Logarithmic"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gui.log_checkbox), true); gui.scale_exposure = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, -20.0, 4.0, 0.001); gtk_range_set_value(GTK_RANGE(gui.scale_exposure), log(gui.exposure_time) / log(2)); gui.scale_compression = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, -4, 4.0, 0.001); gtk_scale_add_mark(GTK_SCALE(gui.scale_compression), 0.0, GTK_POS_BOTTOM, "0"); gtk_range_set_value(GTK_RANGE(gui.scale_compression), log(gui.compression) / log(2)); gtk_widget_set_sensitive(gui.scale_exposure, false); gtk_widget_set_sensitive(gui.scale_compression, false); GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_container_add(GTK_CONTAINER(gui.window), vbox); gtk_box_pack_start(GTK_BOX(vbox), file_button, false, false, 0); gtk_box_pack_start(GTK_BOX(vbox), gui.canvas, true, true, 0); GtkWidget *grid = gtk_grid_new(); gtk_box_pack_start(GTK_BOX(vbox), grid, false, false, 0); gtk_grid_attach(GTK_GRID(grid), gui.log_checkbox, 1, 0, 1, 1); { GtkWidget *label = gtk_label_new("Exposure"); gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1); gtk_widget_set_size_request(gui.scale_exposure, 600, -1); gtk_grid_attach(GTK_GRID(grid), gui.scale_exposure, 1, 1, 1, 1); } { GtkWidget *label = gtk_label_new("Compression"); gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1); gtk_widget_set_size_request(gui.scale_compression, 600, -1); gtk_grid_attach(GTK_GRID(grid), gui.scale_compression, 1, 2, 1, 1); } g_signal_connect(gui.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(gui.canvas, "draw", G_CALLBACK(canvas_draw), &gui); g_signal_connect(file_button, "clicked", G_CALLBACK(file_select_clicked), &gui); g_signal_connect(gui.log_checkbox, "toggled", G_CALLBACK(log_toggled), &gui); g_signal_connect(gui.scale_exposure, "value-changed", G_CALLBACK(scale_exposure_changed), &gui); g_signal_connect(gui.scale_compression, "value-changed", G_CALLBACK(scale_compression_changed), &gui); gtk_widget_show_all(gui.window); // Set C-locale number format. // Make strtod work with strings like "0.1" (instead of "0,1") std::setlocale(LC_NUMERIC, "C"); gtk_main (); return 0; }