From 95a22bf2f02ad11c2602df5ac5ffd04fee93588c Mon Sep 17 00:00:00 2001 From: Matthias Vogelgesang Date: Wed, 10 Oct 2012 17:44:34 +0200 Subject: Implement experimental histogram view --- plugins/mock/uca-mock-camera.c | 4 - plugins/pf/uca-pf-camera.c | 1 + src/uca-camera.c | 10 +- tools/gui/control.c | 47 ++++--- tools/gui/control.glade | 75 ++++++++-- tools/gui/egg-histogram-view.c | 301 +++++++++++++++++++++++++++++++++++++++-- tools/gui/egg-histogram-view.h | 5 + 7 files changed, 400 insertions(+), 43 deletions(-) diff --git a/plugins/mock/uca-mock-camera.c b/plugins/mock/uca-mock-camera.c index 7ab1d4a..65c4240 100644 --- a/plugins/mock/uca-mock-camera.c +++ b/plugins/mock/uca-mock-camera.c @@ -37,7 +37,6 @@ static const gint mock_overrideables[] = { PROP_SENSOR_HORIZONTAL_BINNINGS, PROP_SENSOR_VERTICAL_BINNING, PROP_SENSOR_VERTICAL_BINNINGS, - PROP_TRIGGER_MODE, PROP_EXPOSURE_TIME, PROP_ROI_X, PROP_ROI_Y, @@ -301,9 +300,6 @@ static void uca_mock_camera_get_property(GObject *object, guint property_id, GVa case PROP_EXPOSURE_TIME: g_value_set_double(value, priv->exposure_time); break; - case PROP_TRIGGER_MODE: - g_value_set_enum(value, UCA_CAMERA_TRIGGER_AUTO); - break; case PROP_ROI_X: g_value_set_uint(value, priv->roi_x); break; diff --git a/plugins/pf/uca-pf-camera.c b/plugins/pf/uca-pf-camera.c index 35b5edd..473ffd3 100644 --- a/plugins/pf/uca-pf-camera.c +++ b/plugins/pf/uca-pf-camera.c @@ -234,6 +234,7 @@ static void uca_pf_camera_get_property(GObject *object, guint property_id, GValu g_value_set_boolean(value, FALSE); break; case PROP_EXPOSURE_TIME: + g_value_set_double(value, 1. / 488.0); break; case PROP_ROI_X: g_value_set_uint(value, 0); diff --git a/src/uca-camera.c b/src/uca-camera.c index 6d92e6a..5684888 100644 --- a/src/uca-camera.c +++ b/src/uca-camera.c @@ -132,15 +132,19 @@ uca_camera_get_property(GObject *object, guint property_id, GValue *value, GPara switch (property_id) { case PROP_IS_RECORDING: - g_value_set_boolean(value, priv->is_recording); + g_value_set_boolean (value, priv->is_recording); break; case PROP_IS_READOUT: - g_value_set_boolean(value, priv->is_readout); + g_value_set_boolean (value, priv->is_readout); break; case PROP_TRANSFER_ASYNCHRONOUSLY: - g_value_set_boolean(value, priv->transfer_async); + g_value_set_boolean (value, priv->transfer_async); + break; + + case PROP_TRIGGER_MODE: + g_value_set_enum (value, UCA_CAMERA_TRIGGER_AUTO); break; case PROP_FRAMES_PER_SECOND: diff --git a/tools/gui/control.c b/tools/gui/control.c index f7d3618..b36d188 100644 --- a/tools/gui/control.c +++ b/tools/gui/control.c @@ -28,6 +28,7 @@ #include "uca-camera.h" #include "uca-plugin-manager.h" #include "egg-property-tree-view.h" +#include "egg-histogram-view.h" typedef struct { @@ -37,6 +38,8 @@ typedef struct { GtkWidget *start_button; GtkWidget *stop_button; GtkWidget *record_button; + GtkWidget *histogram_view; + GtkToggleButton *histogram_button; guchar *buffer; guchar *pixels; @@ -49,15 +52,9 @@ typedef struct { int pixel_size; } ThreadData; -enum { - COLUMN_NAME = 0, - COLUMN_VALUE, - COLUMN_EDITABLE, - NUM_COLUMNS -}; - static UcaPluginManager *plugin_manager; + static void convert_8bit_to_rgb (guchar *output, guchar *input, int width, int height) { @@ -106,7 +103,7 @@ grab_thread (void *args) uca_camera_grab (data->camera, (gpointer) &data->buffer, NULL); if (data->store) { - snprintf (filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter++); + snprintf (filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter); FILE *fp = fopen (filename, "wb"); fwrite (data->buffer, data->width*data->height, data->pixel_size, fp); fclose (fp); @@ -121,11 +118,18 @@ grab_thread (void *args) } gdk_threads_enter (); + gdk_flush (); gtk_image_clear (GTK_IMAGE (data->image)); gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height); + + if (gtk_toggle_button_get_active (data->histogram_button)) + gtk_widget_queue_draw (data->histogram_view); + gdk_threads_leave (); + + counter++; } return NULL; } @@ -211,6 +215,12 @@ on_record_button_clicked (GtkWidget *widget, gpointer args) static void create_main_window (GtkBuilder *builder, const gchar* camera_name) { + GtkWidget *window; + GtkWidget *image; + GtkWidget *property_tree_view; + GdkPixbuf *pixbuf; + GtkBox *histogram_box; + GtkContainer *scrolled_property_window; static ThreadData td; GError *error = NULL; @@ -228,15 +238,12 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name) "sensor-bitdepth", &bits_per_sample, NULL); - GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); - GtkWidget *image = GTK_WIDGET (gtk_builder_get_object (builder, "image")); - GtkWidget *property_tree_view = egg_property_tree_view_new (G_OBJECT (camera)); - GtkContainer *scrolled_property_window = GTK_CONTAINER (gtk_builder_get_object (builder, "scrolledwindow2")); - + property_tree_view = egg_property_tree_view_new (G_OBJECT (camera)); + scrolled_property_window = GTK_CONTAINER (gtk_builder_get_object (builder, "scrolledwindow2")); gtk_container_add (scrolled_property_window, property_tree_view); - gtk_widget_show_all (GTK_WIDGET (scrolled_property_window)); - GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, td.width, td.height); + image = GTK_WIDGET (gtk_builder_get_object (builder, "image")); + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, td.width, td.height); gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); td.pixel_size = bits_per_sample > 8 ? 2 : 1; @@ -247,7 +254,14 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name) td.running = FALSE; td.store = FALSE; td.camera = camera; + td.histogram_view = egg_histogram_view_new (); + td.histogram_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "histogram-checkbutton")); + + histogram_box = GTK_BOX (gtk_builder_get_object (builder, "histogram-box")); + gtk_box_pack_start (histogram_box, td.histogram_view, TRUE, TRUE, 6); + egg_histogram_view_set_data (EGG_HISTOGRAM_VIEW (td.histogram_view), td.buffer, td.width * td.height, bits_per_sample, 256); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); g_signal_connect (window, "destroy", G_CALLBACK (on_destroy), &td); td.start_button = GTK_WIDGET (gtk_builder_get_object (builder, "start-button")); @@ -259,8 +273,7 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name) g_signal_connect (td.stop_button, "clicked", G_CALLBACK (on_stop_button_clicked), &td); g_signal_connect (td.record_button, "clicked", G_CALLBACK (on_record_button_clicked), &td); - gtk_widget_show (image); - gtk_widget_show (window); + gtk_widget_show_all (window); } static void diff --git a/tools/gui/control.glade b/tools/gui/control.glade index ee888e8..6d6d791 100644 --- a/tools/gui/control.glade +++ b/tools/gui/control.glade @@ -164,28 +164,83 @@ True 6 - - 300 + True True - automatic - automatic - + + 640 + 480 True - queue + True + automatic + automatic - + True - gtk-missing-image + queue + + + 640 + 480 + True + gtk-missing-image + + + + True + True + + + + + True + True + + + True + + + + + + Enable Live Update + True + True + False + 6 + True + True + + + end + 1 + + + + + + + True + Histogram + + + False + + + + + True + True + - True - False + False + True diff --git a/tools/gui/egg-histogram-view.c b/tools/gui/egg-histogram-view.c index 809a2d9..d3cb18b 100644 --- a/tools/gui/egg-histogram-view.c +++ b/tools/gui/egg-histogram-view.c @@ -15,17 +15,33 @@ with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA */ -#include +#include #include "egg-histogram-view.h" -G_DEFINE_TYPE (EggHistogramView, egg_histogram_view, GTK_TYPE_CELL_RENDERER) +G_DEFINE_TYPE (EggHistogramView, egg_histogram_view, GTK_TYPE_DRAWING_AREA) #define EGG_HISTOGRAM_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), EGG_TYPE_HISTOGRAM_VIEW, EggHistogramViewPrivate)) +#define MIN_WIDTH 128 +#define MIN_HEIGHT 128 +#define BORDER 2 struct _EggHistogramViewPrivate { - guint foo; + GdkCursorType cursor_type; + gboolean grabbing; + + /* This could be moved into a real histogram class */ + guint n_bins; + gint *bins; + gint min_value; + gint max_value; + gint min_border; + gint max_border; + + gpointer data; + gint n_elements; + gint n_bits; }; enum @@ -46,9 +62,178 @@ egg_histogram_view_new (void) return GTK_WIDGET (view); } +void +egg_histogram_view_set_data (EggHistogramView *view, + gpointer data, + guint n_elements, + guint n_bits, + guint n_bins) +{ + EggHistogramViewPrivate *priv; + + priv = view->priv; + + if (priv->bins != NULL) + g_free (priv->bins); + + priv->data = data; + priv->bins = g_malloc0 (n_bins * sizeof (guint)); + priv->n_bins = n_bins; + priv->n_bits = n_bits; + priv->n_elements = n_elements; + + priv->min_value = 0; + priv->max_value = (gint) pow(2, n_bits) - 1; + + priv->min_border = 20; + priv->max_border = priv->max_value - 20; +} + +static void +compute_histogram (EggHistogramViewPrivate *priv) +{ + for (guint i = 0; i < priv->n_bins; i++) + priv->bins[i] = 0; + + if (priv->n_bits == 8) { + guint8 *data = (guint8 *) priv->data; + + for (guint i = 0; i < priv->n_elements; i++) { + guint8 v = data[i]; + guint index = (guint) round (((gdouble) v) / priv->max_value * (priv->n_bins - 1)); + priv->bins[index]++; + } + } + else if (priv->n_bits == 16) { + guint16 *data = (guint16 *) priv->data; + + for (guint i = 0; i < priv->n_elements; i++) { + guint16 v = data[i]; + guint index = (guint) floor (((gdouble ) v) / priv->max_value * (priv->n_bins - 1)); + priv->bins[index]++; + } + } + else + g_warning ("%i number of bits unsupported", priv->n_bits); +} + +static void +set_cursor_type (EggHistogramView *view, GdkCursorType cursor_type) +{ + if (cursor_type != view->priv->cursor_type) { + GdkCursor *cursor = gdk_cursor_new (cursor_type); + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET(view)), cursor); + gdk_cursor_unref (cursor); + view->priv->cursor_type = cursor_type; + } +} + +static void +egg_histogram_view_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = MIN_WIDTH; + requisition->height = MIN_HEIGHT; +} + +static gboolean +egg_histogram_view_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + EggHistogramViewPrivate *priv; + GtkAllocation allocation; + GtkStyle *style; + cairo_t *cr; + gint width, height; + gint max_value = 0; + + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (widget); + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + style = gtk_widget_get_style (widget); + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]); + cairo_paint (cr); + + /* Draw the background */ + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]); + cairo_paint (cr); + + gtk_widget_get_allocation (widget, &allocation); + width = allocation.width - 2 * BORDER; + height = allocation.height - 2 * BORDER; + + gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]); + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + cairo_translate (cr, 0.5, 0.5); + cairo_rectangle (cr, BORDER, BORDER, width - 1, height - 1); + cairo_stroke (cr); + + if (priv->bins == NULL) + goto cleanup; + + compute_histogram (priv); + + /* Draw border areas */ + gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]); + + if (priv->min_border > 0) { + cairo_rectangle (cr, BORDER, BORDER, priv->min_border + 0.5, height - 1); + cairo_fill (cr); + } + + /* Draw spikes */ + for (guint i = 0; i < priv->n_bins; i++) { + if (priv->bins[i] > max_value) + max_value = priv->bins[i]; + } + + if (max_value == 0) + goto cleanup; + + gdk_cairo_set_source_color (cr, &style->black); + + if (width > priv->n_bins) { + gdouble skip = ((gdouble) width) / priv->n_bins; + gdouble x = 1; + + for (guint i = 0; i < priv->n_bins; i++, x += skip) { + if (priv->bins[i] == 0) + continue; + + gint y = (gint) ((height - 2) * priv->bins[i]) / max_value; + cairo_move_to (cr, round (x + BORDER), height + BORDER - 1); + cairo_line_to (cr, round (x + BORDER), height + BORDER - 1 - y); + cairo_stroke (cr); + } + } + +cleanup: + cairo_destroy (cr); + return FALSE; +} + +static void +egg_histogram_view_finalize (GObject *object) +{ + EggHistogramViewPrivate *priv; + + priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (object); + + if (priv->bins) + g_free (priv->bins); + + G_OBJECT_CLASS (egg_histogram_view_parent_class)->finalize (object); +} + static void egg_histogram_view_dispose (GObject *object) { + G_OBJECT_CLASS (egg_histogram_view_parent_class)->dispose (object); } static void @@ -81,20 +266,118 @@ egg_histogram_view_get_property (GObject *object, } } +static gint +get_mouse_distance (EggHistogramViewPrivate *priv, + gint x) +{ + return (priv->min_border + BORDER) - x; +} + +static gboolean +egg_histogram_view_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + EggHistogramView *view; + EggHistogramViewPrivate *priv; + + view = EGG_HISTOGRAM_VIEW (widget); + priv = view->priv; + + if (priv->grabbing) { + priv->min_border = event->x + BORDER; + gtk_widget_queue_draw (widget); + } + else { + gint distance = get_mouse_distance (priv, event->x); + + if (ABS(distance) < 6) + set_cursor_type (view, GDK_FLEUR); + else + set_cursor_type (view, GDK_ARROW); + } + + return TRUE; +} + +static gboolean +egg_histogram_view_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + EggHistogramView *view; + EggHistogramViewPrivate *priv; + GtkAllocation allocation; + gint width; + + gtk_widget_get_allocation (widget, &allocation); + width = allocation.width - 2 * BORDER; + + view = EGG_HISTOGRAM_VIEW (widget); + priv = view->priv; + + set_cursor_type (view, GDK_ARROW); + priv->grabbing = FALSE; + + return TRUE; +} + +static gboolean +egg_histogram_view_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + EggHistogramView *view; + EggHistogramViewPrivate *priv; + gint distance; + + view = EGG_HISTOGRAM_VIEW (widget); + priv = view->priv; + distance = get_mouse_distance (priv, event->x); + + if (ABS (distance) < 6) { + priv->grabbing = TRUE; + set_cursor_type (view, GDK_FLEUR); + } + + return TRUE; +} + static void egg_histogram_view_class_init (EggHistogramViewClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - gobject_class->set_property = egg_histogram_view_set_property; - gobject_class->get_property = egg_histogram_view_get_property; - gobject_class->dispose = egg_histogram_view_dispose; + object_class->set_property = egg_histogram_view_set_property; + object_class->get_property = egg_histogram_view_get_property; + object_class->dispose = egg_histogram_view_dispose; + object_class->finalize = egg_histogram_view_finalize; + + widget_class->size_request = egg_histogram_view_size_request; + widget_class->expose_event = egg_histogram_view_expose; + widget_class->button_press_event = egg_histogram_view_button_press; + widget_class->button_release_event = egg_histogram_view_button_release; + widget_class->motion_notify_event = egg_histogram_view_motion_notify; g_type_class_add_private (klass, sizeof (EggHistogramViewPrivate)); } static void -egg_histogram_view_init (EggHistogramView *renderer) +egg_histogram_view_init (EggHistogramView *view) { - renderer->priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (renderer); + EggHistogramViewPrivate *priv; + + view->priv = priv = EGG_HISTOGRAM_VIEW_GET_PRIVATE (view); + + priv->bins = NULL; + priv->data = NULL; + priv->n_bins = 0; + priv->n_elements = 0; + + priv->cursor_type = GDK_ARROW; + priv->grabbing = FALSE; + + gtk_widget_add_events (GTK_WIDGET (view), + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_POINTER_MOTION_MASK); } diff --git a/tools/gui/egg-histogram-view.h b/tools/gui/egg-histogram-view.h index 23581dc..a1df0ba 100644 --- a/tools/gui/egg-histogram-view.h +++ b/tools/gui/egg-histogram-view.h @@ -48,6 +48,11 @@ struct _EggHistogramViewClass GType egg_histogram_view_get_type (void); GtkWidget * egg_histogram_view_new (void); +void egg_histogram_view_set_data (EggHistogramView *view, + gpointer data, + guint n_elements, + guint n_bits, + guint n_bins); G_END_DECLS -- cgit v1.2.3