summaryrefslogtreecommitdiffstats
path: root/tools/gui
diff options
context:
space:
mode:
authorMatthias Vogelgesang <matthias.vogelgesang@gmail.com>2012-10-10 17:44:34 +0200
committerMatthias Vogelgesang <matthias.vogelgesang@gmail.com>2012-10-10 17:44:53 +0200
commit95a22bf2f02ad11c2602df5ac5ffd04fee93588c (patch)
tree1f26ad046db783bafa4f1e25e04f92d6fc784c9f /tools/gui
parent46a6d028c5591b0775ef5862ff5c9e95fd68e7fd (diff)
downloadlibuca-95a22bf2f02ad11c2602df5ac5ffd04fee93588c.tar.gz
libuca-95a22bf2f02ad11c2602df5ac5ffd04fee93588c.tar.bz2
libuca-95a22bf2f02ad11c2602df5ac5ffd04fee93588c.tar.xz
libuca-95a22bf2f02ad11c2602df5ac5ffd04fee93588c.zip
Implement experimental histogram view
Diffstat (limited to 'tools/gui')
-rw-r--r--tools/gui/control.c47
-rw-r--r--tools/gui/control.glade75
-rw-r--r--tools/gui/egg-histogram-view.c301
-rw-r--r--tools/gui/egg-histogram-view.h5
4 files changed, 392 insertions, 36 deletions
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 @@
<property name="can_focus">True</property>
<property name="border_width">6</property>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="width_request">300</property>
+ <object class="GtkVPaned" id="vpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<child>
- <object class="GtkViewport" id="viewport1">
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">640</property>
+ <property name="height_request">480</property>
<property name="visible">True</property>
- <property name="resize_mode">queue</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
<child>
- <object class="GtkImage" id="image">
+ <object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
- <property name="stock">gtk-missing-image</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="width_request">640</property>
+ <property name="height_request">480</property>
+ <property name="visible">True</property>
+ <property name="stock">gtk-missing-image</property>
+ </object>
+ </child>
</object>
</child>
</object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkHBox" id="histogram-box">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="histogram-checkbutton">
+ <property name="label" translatable="yes">Enable Live Update</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="border_width">6</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Histogram</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
</child>
</object>
<packing>
- <property name="resize">True</property>
- <property name="shrink">False</property>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
</packing>
</child>
<child>
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 <stdlib.h>
+#include <math.h>
#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