summaryrefslogtreecommitdiffstats
path: root/tools/gui
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gui')
-rw-r--r--tools/gui/CMakeLists.txt1
-rw-r--r--tools/gui/control.c190
-rw-r--r--tools/gui/control.glade33
-rw-r--r--tools/gui/ring-buffer.c64
-rw-r--r--tools/gui/ring-buffer.h28
5 files changed, 243 insertions, 73 deletions
diff --git a/tools/gui/CMakeLists.txt b/tools/gui/CMakeLists.txt
index ff5e9f6..1000ac6 100644
--- a/tools/gui/CMakeLists.txt
+++ b/tools/gui/CMakeLists.txt
@@ -25,6 +25,7 @@ if (GTK2_FOUND)
add_executable(control
control.c
+ ring-buffer.c
egg-property-cell-renderer.c
egg-property-tree-view.c
egg-histogram-view.c)
diff --git a/tools/gui/control.c b/tools/gui/control.c
index 214e4da..84799cb 100644
--- a/tools/gui/control.c
+++ b/tools/gui/control.c
@@ -22,11 +22,17 @@
#include <math.h>
#include "config.h"
+#include "ring-buffer.h"
#include "uca-camera.h"
#include "uca-plugin-manager.h"
#include "egg-property-tree-view.h"
#include "egg-histogram-view.h"
+typedef enum {
+ IDLE,
+ RUNNING,
+ RECORDING
+} State;
typedef struct {
UcaCamera *camera;
@@ -35,13 +41,14 @@ typedef struct {
GtkWidget *start_button;
GtkWidget *stop_button;
GtkWidget *record_button;
- GtkWidget *histogram_view;
+
+ GtkWidget *histogram_view;
GtkToggleButton *histogram_button;
+ GtkAdjustment *frame_slider;
- guchar *buffer;
+ RingBuffer *buffer;
guchar *pixels;
- gboolean running;
- gboolean store;
+ State state;
int timestamp;
int width;
@@ -53,70 +60,66 @@ static UcaPluginManager *plugin_manager;
static void
-convert_8bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max)
+convert_grayscale_to_rgb (ThreadData *data, gpointer buffer)
{
- gdouble factor = 255.0 / (max - min);
-
- for (int i = 0, j = 0; i < width*height; i++) {
- guchar val = (guchar) ((input[i] - min) * factor);
- output[j++] = val;
- output[j++] = val;
- output[j++] = val;
+ gdouble min;
+ gdouble max;
+ gdouble factor;
+ guint8 *output;
+
+ egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min, &max);
+ factor = 255.0 / (max - min);
+ output = data->pixels;
+
+ if (data->pixel_size == 1) {
+ guint8 *input = (guint8 *) buffer;
+
+ for (int i = 0, j = 0; i < data->width * data->height; i++) {
+ guchar val = (guchar) ((input[i] - min) * factor);
+ output[j++] = val;
+ output[j++] = val;
+ output[j++] = val;
+ }
+ }
+ else if (data->pixel_size == 2) {
+ guint16 *input = (guint16 *) buffer;
+
+ for (int i = 0, j = 0; i < data->width * data->height; i++) {
+ guchar val = (guint8) ((input[i] - min) * factor);
+ output[j++] = val;
+ output[j++] = val;
+ output[j++] = val;
+ }
}
}
static void
-convert_16bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max)
+update_pixbuf (ThreadData *data)
{
- gdouble factor = 255.0 / (max - min);
+ 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);
- for (int i = 0, j = 0; i < width*height; i++) {
- guchar val = (guint8) ((input[i] - min) * factor);
- output[j++] = val;
- output[j++] = val;
- output[j++] = val;
- }
+ if (gtk_toggle_button_get_active (data->histogram_button))
+ gtk_widget_queue_draw (data->histogram_view);
}
-static void *
-grab_thread (void *args)
+static gpointer
+preview_frames (void *args)
{
ThreadData *data = (ThreadData *) args;
- gchar filename[FILENAME_MAX] = {0,};
gint counter = 0;
- while (data->running) {
- gdouble min_value;
- gdouble max_value;
+ while (data->state == RUNNING) {
+ gpointer buffer;
- uca_camera_grab (data->camera, (gpointer) &data->buffer, NULL);
-
- if (data->store) {
- 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);
- }
-
- /* FIXME: We should actually check if this is really a new frame and
- * just do nothing if it is an already displayed one. */
- egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min_value, &max_value);
-
- if (data->pixel_size == 1)
- convert_8bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value);
- else if (data->pixel_size == 2)
- convert_16bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value);
+ buffer = ring_buffer_get_current_pointer (data->buffer);
+ uca_camera_grab (data->camera, &buffer, NULL);
+ convert_grayscale_to_rgb (data, buffer);
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);
-
+ update_pixbuf (data);
gdk_threads_leave ();
counter++;
@@ -124,6 +127,33 @@ grab_thread (void *args)
return NULL;
}
+static gpointer
+record_frames (gpointer args)
+{
+ ThreadData *data;
+ gpointer buffer;
+ guint n_frames = 0;
+
+ data = (ThreadData *) args;
+ ring_buffer_reset (data->buffer);
+
+ while (data->state == RECORDING) {
+ buffer = ring_buffer_get_current_pointer (data->buffer);
+ uca_camera_grab (data->camera, &buffer, NULL);
+ ring_buffer_proceed (data->buffer);
+ n_frames++;
+ }
+
+ n_frames = ring_buffer_get_num_blocks (data->buffer);
+
+ gdk_threads_enter ();
+ gtk_adjustment_set_upper (data->frame_slider, n_frames - 1);
+ gtk_adjustment_set_value (data->frame_slider, n_frames - 1);
+ gdk_threads_leave ();
+
+ return NULL;
+}
+
gboolean
on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
@@ -134,17 +164,39 @@ void
on_destroy (GtkWidget *widget, gpointer data)
{
ThreadData *td = (ThreadData *) data;
- td->running = FALSE;
+
+ td->state = IDLE;
g_object_unref (td->camera);
+ ring_buffer_free (td->buffer);
+
gtk_main_quit ();
}
static void
set_tool_button_state (ThreadData *data)
{
- gtk_widget_set_sensitive (data->start_button, !data->running);
- gtk_widget_set_sensitive (data->stop_button, data->running);
- gtk_widget_set_sensitive (data->record_button, !data->running);
+ gtk_widget_set_sensitive (data->start_button,
+ data->state == IDLE);
+ gtk_widget_set_sensitive (data->stop_button,
+ data->state == RUNNING || data->state == RECORDING);
+ gtk_widget_set_sensitive (data->record_button,
+ data->state == IDLE);
+}
+
+static void
+on_frame_slider_changed (GtkAdjustment *adjustment, gpointer user_data)
+{
+ ThreadData *data = (ThreadData *) user_data;
+
+ if (data->state == IDLE) {
+ gpointer buffer;
+ gint index;
+
+ index = (gint) gtk_adjustment_get_value (adjustment);
+ buffer = ring_buffer_get_pointer (data->buffer, index);
+ convert_grayscale_to_rgb (data, buffer);
+ update_pixbuf (data);
+ }
}
static void
@@ -153,7 +205,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args)
ThreadData *data = (ThreadData *) args;
GError *error = NULL;
- data->running = TRUE;
+ data->state = RUNNING;
set_tool_button_state (data);
uca_camera_start_recording (data->camera, &error);
@@ -163,7 +215,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args)
return;
}
- if (!g_thread_create (grab_thread, data, FALSE, &error)) {
+ if (!g_thread_create (preview_frames, data, FALSE, &error)) {
g_printerr ("Failed to create thread: %s\n", error->message);
return;
}
@@ -175,8 +227,7 @@ on_stop_button_clicked (GtkWidget *widget, gpointer args)
ThreadData *data = (ThreadData *) args;
GError *error = NULL;
- data->running = FALSE;
- data->store = FALSE;
+ data->state = IDLE;
set_tool_button_state (data);
uca_camera_stop_recording (data->camera, &error);
@@ -192,13 +243,12 @@ on_record_button_clicked (GtkWidget *widget, gpointer args)
GError *error = NULL;
data->timestamp = (int) time (0);
- data->store = TRUE;
- data->running = TRUE;
+ data->state = RECORDING;
set_tool_button_state (data);
uca_camera_start_recording (data->camera, &error);
- if (!g_thread_create (grab_thread, data, FALSE, &error))
+ if (!g_thread_create (record_frames, data, FALSE, &error))
g_printerr ("Failed to create thread: %s\n", error->message);
}
@@ -240,18 +290,19 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name)
td.pixel_size = bits_per_sample > 8 ? 2 : 1;
td.image = image;
td.pixbuf = pixbuf;
- td.buffer = (guchar *) g_malloc (td.pixel_size * td.width * td.height);
+ td.buffer = ring_buffer_new (td.pixel_size * td.width * td.height, 256);
td.pixels = gdk_pixbuf_get_pixels (pixbuf);
- td.running = FALSE;
- td.store = FALSE;
+ td.state = IDLE;
td.camera = camera;
td.histogram_view = egg_histogram_view_new ();
td.histogram_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "histogram-checkbutton"));
+ td.frame_slider = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "frames-adjustment"));
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);
+ ring_buffer_get_current_pointer (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);
@@ -261,16 +312,17 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name)
td.record_button = GTK_WIDGET (gtk_builder_get_object (builder, "record-button"));
set_tool_button_state (&td);
- g_object_bind_property (gtk_builder_get_object (builder, "min_bin_value_adjustment"), "value",
+ g_object_bind_property (gtk_builder_get_object (builder, "min-bin-value-adjustment"), "value",
td.histogram_view, "minimum-bin-value",
G_BINDING_DEFAULT);
- max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max_bin_value_adjustment"));
+ max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max-bin-value-adjustment"));
gtk_adjustment_set_value (max_bin_adjustment, pow (2, bits_per_sample) - 1);
g_object_bind_property (max_bin_adjustment, "value",
td.histogram_view, "maximum-bin-value",
G_BINDING_DEFAULT);
+ g_signal_connect (td.frame_slider, "value-changed", G_CALLBACK (on_frame_slider_changed), &td);
g_signal_connect (td.start_button, "clicked", G_CALLBACK (on_start_button_clicked), &td);
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);
diff --git a/tools/gui/control.glade b/tools/gui/control.glade
index ebe2cc8..c3008ec 100644
--- a/tools/gui/control.glade
+++ b/tools/gui/control.glade
@@ -230,7 +230,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="adjustment">min_bin_value_adjustment</property>
+ <property name="adjustment">min-bin-value-adjustment</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -260,7 +260,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
- <property name="adjustment">max_bin_value_adjustment</property>
+ <property name="adjustment">max-bin-value-adjustment</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -309,6 +309,27 @@
<property name="tab_fill">False</property>
</packing>
</child>
+ <child>
+ <object class="GtkHScale" id="hscale1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">frames-adjustment</property>
+ <property name="digits">0</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Preview</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="resize">True</property>
@@ -425,15 +446,19 @@
</object>
</child>
</object>
- <object class="GtkAdjustment" id="min_bin_value_adjustment">
+ <object class="GtkAdjustment" id="min-bin-value-adjustment">
<property name="upper">65535</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
- <object class="GtkAdjustment" id="max_bin_value_adjustment">
+ <object class="GtkAdjustment" id="max-bin-value-adjustment">
<property name="value">256</property>
<property name="upper">65535</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
+ <object class="GtkAdjustment" id="frames-adjustment">
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
</interface>
diff --git a/tools/gui/ring-buffer.c b/tools/gui/ring-buffer.c
new file mode 100644
index 0000000..56c7620
--- /dev/null
+++ b/tools/gui/ring-buffer.c
@@ -0,0 +1,64 @@
+
+#include <math.h>
+#include "ring-buffer.h"
+
+RingBuffer *
+ring_buffer_new (gsize block_size,
+ gsize n_blocks)
+{
+ RingBuffer *buffer;
+
+ buffer = g_new0 (RingBuffer, 1);
+ buffer->block_size = block_size;
+ buffer->n_blocks_total = n_blocks;
+ buffer->n_blocks_used = 0;
+ buffer->start_index = 0;
+ buffer->data = g_malloc0 (n_blocks * buffer->block_size);
+
+ return buffer;
+}
+
+void
+ring_buffer_free (RingBuffer *buffer)
+{
+ g_free (buffer->data);
+ g_free (buffer);
+}
+
+void
+ring_buffer_reset (RingBuffer *buffer)
+{
+ buffer->n_blocks_used = 0;
+ buffer->start_index = 0;
+}
+
+gpointer
+ring_buffer_get_current_pointer (RingBuffer *buffer)
+{
+ return ring_buffer_get_pointer (buffer, 0);
+}
+
+gpointer
+ring_buffer_get_pointer (RingBuffer *buffer,
+ guint index)
+{
+ g_assert (index < buffer->n_blocks_total);
+ return buffer->data + ((buffer->start_index + index) % buffer->n_blocks_total) * buffer->block_size;
+}
+
+guint
+ring_buffer_get_num_blocks (RingBuffer *buffer)
+{
+ return buffer->n_blocks_used;
+}
+
+void
+ring_buffer_proceed (RingBuffer *buffer)
+{
+ buffer->start_index++;
+
+ if (buffer->n_blocks_used < buffer->n_blocks_total)
+ buffer->n_blocks_used++;
+ else
+ buffer->start_index = buffer->start_index % buffer->n_blocks_total;
+}
diff --git a/tools/gui/ring-buffer.h b/tools/gui/ring-buffer.h
new file mode 100644
index 0000000..22cbde7
--- /dev/null
+++ b/tools/gui/ring-buffer.h
@@ -0,0 +1,28 @@
+#ifndef RING_BUFFER_H
+#define RING_BUFFER_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ guchar *data;
+ gsize block_size;
+ guint n_blocks_total;
+ guint n_blocks_used;
+ guint start_index;
+} RingBuffer;
+
+RingBuffer * ring_buffer_new (gsize block_size,
+ gsize n_blocks);
+void ring_buffer_free (RingBuffer *buffer);
+void ring_buffer_reset (RingBuffer *buffer);
+gpointer ring_buffer_get_current_pointer (RingBuffer *buffer);
+gpointer ring_buffer_get_pointer (RingBuffer *buffer,
+ guint index);
+guint ring_buffer_get_num_blocks (RingBuffer *buffer);
+void ring_buffer_proceed (RingBuffer *buffer);
+
+G_END_DECLS
+
+#endif