#include #include #include #include #include #include #include #include /* Only available as of GTK 3.4 */ #ifndef GDK_BUTTON_PRIMARY #define GDK_BUTTON_PRIMARY 1 #endif struct ct { GtkWidget *window; GtkWidget *drawing_area; cairo_surface_t *surface; CvCapture *capture; gboolean doing_rubberband; struct { double x1, y1, x2, y2; } rubberband; int idle_source; }; static gboolean capture(gpointer userdata) { struct ct *ct = userdata; IplImage *image; guchar *row1, *row2, *p1, *p2; int x, y; image = cvQueryFrame(ct->capture); if (!image) { fprintf(stderr, "ERROR: frame is null...\n"); exit(EXIT_FAILURE); } if (!ct->surface || cairo_image_surface_get_width(ct->surface) != image->width || cairo_image_surface_get_height(ct->surface) != image->height) { if (ct->surface) cairo_surface_destroy(ct->surface); ct->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, image->width, image->height); } row1 = (guchar *) image->imageData; row2 = cairo_image_surface_get_data(ct->surface); for (y = 0; y < image->height; ++y) { p1 = row1; p2 = row2; for (x = 0; x < image->width; ++x) { /* p2[0] = p1[0]; p2[1] = p1[1]; p2[2] = p1[2]; p2[3] = 0; */ memcpy(p2, p1, 3); p1 += image->nChannels; p2 += 4; } row1 += image->widthStep; row2 += cairo_image_surface_get_stride(ct->surface); } gtk_widget_queue_draw(ct->drawing_area); return TRUE; } static inline void calc_rubberband_rect(struct ct *ct, double *x, double *y, double *width, double *height) { *x = MIN(ct->rubberband.x1, ct->rubberband.x2); *y = MIN(ct->rubberband.y1, ct->rubberband.y2); *width = ABS(ct->rubberband.x1 - ct->rubberband.x2); *height = ABS(ct->rubberband.y1 - ct->rubberband.y2); } static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer userdata) { struct ct *ct = userdata; if (ct->doing_rubberband) { ct->rubberband.x2 = event->x; ct->rubberband.y2 = event->y; gtk_widget_queue_draw(ct->drawing_area); } return TRUE; } static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer userdata) { struct ct *ct = userdata; switch (event->button) { case GDK_BUTTON_PRIMARY: ct->rubberband.x1 = event->x; ct->rubberband.x2 = event->x; ct->rubberband.y1 = event->y; ct->rubberband.y2 = event->y; ct->doing_rubberband = TRUE; return FALSE; default: return TRUE; } } static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer userdata) { struct ct *ct = userdata; if (event->button != GDK_BUTTON_PRIMARY) return TRUE; ct->doing_rubberband = FALSE; gtk_widget_queue_draw(ct->drawing_area); return FALSE; } static gboolean key_event(GtkWidget *widget, GdkEventKey *event, gpointer userdata) { struct ct *ct = userdata; switch (event->type) { case GDK_KEY_PRESS: switch (event->keyval) { case GDK_KEY_s: case GDK_KEY_S: if (ct->idle_source) { g_source_remove(ct->idle_source); ct->idle_source = 0; } break; case GDK_KEY_c: case GDK_KEY_C: if (!ct->idle_source) ct->idle_source = g_idle_add(capture, ct); break; case GDK_KEY_q: case GDK_KEY_Q: gtk_main_quit(); break; } return FALSE; default: return TRUE; } } static void draw_rubberband(struct ct *ct, GtkWidget *widget, cairo_t *cr) { GtkStyleContext *context; double x, y, width, height; context = gtk_widget_get_style_context(widget); gtk_style_context_save(context); gtk_style_context_add_class(context, GTK_STYLE_CLASS_RUBBERBAND); calc_rubberband_rect(ct, &x, &y, &width, &height); gtk_render_background(context, cr, x, y, width, height); gtk_render_frame(context, cr, x, y, width, height); gtk_style_context_restore(context); } static gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer userdata) { struct ct *ct = userdata; #if 0 double sx, sy; #endif cairo_save(cr); #if 0 sx = gtk_widget_get_allocated_width(widget); sx /= cairo_image_surface_get_width(ct->surface); sy = gtk_widget_get_allocated_height(widget); sy /= cairo_image_surface_get_height(ct->surface); cairo_scale(cr, sx, sy); #endif #if 0 cairo_translate(cr, cairo_image_surface_get_width(ct->surface), 0.0); cairo_scale(cr, -1.0, 1.0); #endif cairo_set_source_surface(cr, ct->surface, 0, 0); cairo_paint(cr); cairo_restore(cr); if (ct->doing_rubberband) draw_rubberband(ct, widget, cr); return FALSE; } int main(int argc, char *argv[]) { struct ct ct; int index = CV_CAP_ANY; memset(&ct, 0, sizeof ct); gtk_init(&argc, &argv); if (argc > 1) index = atoi(argv[argc-1]); ct.capture = cvCreateCameraCapture(index); if (!ct.capture) { fprintf(stderr, "ERROR: capture is NULL \n"); exit(EXIT_FAILURE); } cvSetCaptureProperty(ct.capture, CV_CAP_PROP_FRAME_WIDTH, 10000); cvSetCaptureProperty(ct.capture, CV_CAP_PROP_FRAME_HEIGHT, 10000); printf("width: %.f, height: %.f\n", cvGetCaptureProperty(ct.capture, CV_CAP_PROP_FRAME_WIDTH), cvGetCaptureProperty(ct.capture, CV_CAP_PROP_FRAME_HEIGHT)); ct.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); ct.drawing_area = gtk_drawing_area_new(); capture(&ct); gtk_widget_set_size_request(ct.drawing_area, cairo_image_surface_get_width(ct.surface), cairo_image_surface_get_height(ct.surface)); gtk_container_add(GTK_CONTAINER(ct.window), ct.drawing_area); g_signal_connect(ct.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(ct.window, "key-press-event", G_CALLBACK(key_event), &ct); gtk_widget_set_events(ct.drawing_area, GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect(ct.drawing_area, "draw", G_CALLBACK(draw_cb), &ct); g_signal_connect(ct.drawing_area, "button-press-event", G_CALLBACK(button_press_event), &ct); g_signal_connect(ct.drawing_area, "button-release-event", G_CALLBACK(button_release_event), &ct); g_signal_connect(ct.drawing_area, "motion-notify-event", G_CALLBACK(motion_notify_event), &ct); ct.idle_source = g_idle_add(capture, &ct); gtk_widget_show_all(ct.window); gtk_main(); if (ct.idle_source) g_source_remove(ct.idle_source); cvReleaseCapture(&ct.capture); return 0; } /* FIXME: Need to overload perror so that invalid OpenCV messages are hidden. */ void perror(const char *s) { if (strcmp("VIDIOC_QUERYMENU", s) == 0) return; fprintf(stderr, "%s: %s\n", s, strerror(errno)); }