/* Adapted From GtkVuMeter by Todd Goyen Copyright 2003 Todd Goyen wettoad@knighthoodofbuh.or Improved rendering performance (no individual drawline calls) */ #include #include "gtkvumeter.h" #define MIN_HORIZONTAL_VUMETER_WIDTH 40 #define HORIZONTAL_VUMETER_HEIGHT 20 #define VERTICAL_VUMETER_WIDTH 20 #define MIN_VERTICAL_VUMETER_HEIGHT 40 G_DEFINE_TYPE (GtkVuMeter, gtk_vu_meter, GTK_TYPE_DRAWING_AREA); static GdkColor default_f_gradient_keys[3] = {{0,65535,0,0},{0,65535,65535,0},{0,0,65535,0}}; static GdkColor default_b_gradient_keys[3] = {{0,49151,0,0},{0,49151,49151,0},{0,0,49151,0}}; static gboolean gtk_vu_meter_expose (GtkWidget *vumeter, GdkEventExpose *event); static void gtk_vu_meter_setup_colors (GtkVuMeter *vumeter); static void gtk_vu_meter_free_colors (GtkVuMeter *vumeter); static void free_drawbuf(guchar *pixels, gpointer data); static void gtk_vu_meter_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_vu_meter_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gint gtk_vu_meter_sound_level_to_draw_level (GtkVuMeter *vumeter); static void gtk_vu_meter_realize (GtkWidget *widget); static void gtk_vu_meter_class_init (GtkVuMeterClass *class) { GtkWidgetClass *widget_class; widget_class = GTK_WIDGET_CLASS (class); widget_class->realize = gtk_vu_meter_realize; widget_class->expose_event = gtk_vu_meter_expose; widget_class->size_request = gtk_vu_meter_size_request; widget_class->size_allocate = gtk_vu_meter_size_allocate; } static void gtk_vu_meter_init (GtkVuMeter *vumeter) { vumeter->vertical=TRUE; gtk_vu_meter_set_gradient(vumeter, 3, default_f_gradient_keys, 3, default_b_gradient_keys); gtk_vu_meter_setup_colors(vumeter); } static void gtk_vu_meter_realize (GtkWidget *widget) { GtkVuMeter *vumeter; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_VU_METER (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); vumeter = GTK_VU_METER (widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_user_data (widget->window, widget); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); gtk_vu_meter_setup_colors (vumeter); } void gtk_vu_meter_set_gradient (GtkVuMeter *vumeter, gint f_gradient_key_count, GdkColor *f_gradient_keys, gint b_gradient_key_count, GdkColor *b_gradient_keys) { //XXX : memdup is a bad idea ? GdkColor *fgk = g_memdup(f_gradient_keys, f_gradient_key_count*sizeof(GdkColor)); GdkColor *bgk = g_memdup(b_gradient_keys, b_gradient_key_count*sizeof(GdkColor)); g_return_if_fail (fgk != NULL); g_return_if_fail (bgk != NULL); vumeter->f_gradient_keys = fgk; vumeter->f_gradient_key_count=f_gradient_key_count; vumeter->b_gradient_keys = bgk; vumeter->b_gradient_key_count=b_gradient_key_count; } static void gtk_vu_meter_free_colors (GtkVuMeter *vumeter) { // TODO : free pixmaps } static void gtk_vu_meter_setup_colors (GtkVuMeter *vumeter) { gint i,j; guchar *f_drawbuf, *b_drawbuf, f_r, f_g, f_b, b_r, b_g, b_b; gint f_key_len, b_key_len; gint f_key_i, b_key_i; gdouble f_key_pos, b_key_pos; GdkColor *fgk, *bgk; gint h=GTK_WIDGET(vumeter)->allocation.height; gint w=GTK_WIDGET(vumeter)->allocation.width; // Clean every previous colors and buffers gtk_vu_meter_free_colors (vumeter); if (vumeter->vertical == TRUE) { vumeter->colors = MAX(h, 0); } else { vumeter->colors = MAX(w, 0); } // Allocate a memory buffers to hold the gradients f_drawbuf=g_malloc(h*w*3); b_drawbuf=g_malloc(h*w*3); g_return_if_fail (f_drawbuf != NULL); g_return_if_fail (b_drawbuf != NULL); // Compute some values before looping for gradient generation f_key_len = vumeter->colors / (vumeter->f_gradient_key_count-1) + 1; b_key_len = vumeter->colors / (vumeter->b_gradient_key_count-1) + 1; fgk=vumeter->f_gradient_keys; bgk=vumeter->b_gradient_keys; for (i=0; icolors; i++) { // Compute the current position in the gradient keys f_key_i=i/f_key_len; f_key_pos=((gdouble) (i%f_key_len)/f_key_len); b_key_i=i/f_key_len; b_key_pos=((gdouble) (i%b_key_len)/b_key_len); /* Generate the Colours */ /* foreground */ f_r = ( fgk[f_key_i].red*(1.0-f_key_pos) + fgk[f_key_i+1].red*f_key_pos ) / 256; f_g = ( fgk[f_key_i].green*(1.0-f_key_pos) + fgk[f_key_i+1].green*f_key_pos ) / 256; f_b = ( fgk[f_key_i].blue*(1.0-f_key_pos) + fgk[f_key_i+1].blue*f_key_pos ) / 256; /* background */ b_r = ( bgk[b_key_i].red*(1.0-b_key_pos) + bgk[b_key_i+1].red*b_key_pos ) / 256; b_g = ( bgk[b_key_i].green*(1.0-b_key_pos) + bgk[b_key_i+1].green*b_key_pos ) / 256; b_b = ( bgk[b_key_i].blue*(1.0-b_key_pos) + bgk[b_key_i+1].blue*b_key_pos ) / 256; /* Apply the color in the drawbufs */ if (vumeter->vertical == TRUE) { // Vertical mode : draw directly the whole line of identical color for (j=3*w*i; j<3*w*(i+1); ) { f_drawbuf[j++]=f_r; f_drawbuf[j++]=f_g; f_drawbuf[j++]=f_b; } for (j=3*w*i; j<3*w*(i+1); ) { b_drawbuf[j++]=b_r; b_drawbuf[j++]=b_g; b_drawbuf[j++]=b_b; } } else { // Horiziontal mode : draw only the first line (color change at each pixel) // Others line will be duplicated at the end f_drawbuf[i]=f_r; f_drawbuf[i+1]=f_g; f_drawbuf[i+2]=f_b; b_drawbuf[i]=b_r; b_drawbuf[i+1]=b_g; b_drawbuf[i+2]=b_b; } } if (vumeter->vertical != TRUE) { // Duplicate the first line over the others for (j=1; jf_pixbuf = gdk_pixbuf_new_from_data(f_drawbuf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w*3, free_drawbuf, NULL); vumeter->b_pixbuf = gdk_pixbuf_new_from_data(b_drawbuf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w*3, free_drawbuf, NULL); } static void gtk_vu_meter_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkVuMeter *vumeter; g_return_if_fail (GTK_IS_VU_METER (widget)); g_return_if_fail (requisition != NULL); vumeter = GTK_VU_METER (widget); if (vumeter->vertical == TRUE) { requisition->width = VERTICAL_VUMETER_WIDTH; requisition->height = MIN_VERTICAL_VUMETER_HEIGHT; } else { requisition->width = MIN_HORIZONTAL_VUMETER_WIDTH; requisition->height = HORIZONTAL_VUMETER_HEIGHT; } } static void gtk_vu_meter_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkVuMeter *vumeter; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_VU_METER (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; vumeter = GTK_VU_METER (widget); if (GTK_WIDGET_REALIZED (widget)) { if (vumeter->vertical == TRUE) { /* veritcal */ gdk_window_move_resize (widget->window, allocation->x, allocation->y, //VERTICAL_VUMETER_WIDTH, MAX(allocation->height, MIN_VERTICAL_VUMETER_HEIGHT)); MAX(VERTICAL_VUMETER_WIDTH,allocation->width), MAX(allocation->height, MIN_VERTICAL_VUMETER_HEIGHT)); } else { /* horizontal */ gdk_window_move_resize (widget->window, allocation->x, allocation->y, MAX(allocation->width, MIN_HORIZONTAL_VUMETER_WIDTH), HORIZONTAL_VUMETER_HEIGHT); } /* Fix the colours */ gtk_vu_meter_setup_colors (vumeter); } } static void free_drawbuf(guchar *pixels, gpointer data) { g_free(pixels); } static gboolean gtk_vu_meter_expose (GtkWidget *vumeter, GdkEventExpose *event) { //gdk_pixbuf_render_to_drawable(pixbuf, dbuf_pixmap, app->style->fg_gc[GTK_STATE_NORMAL], 0, 0, 0, 0, WIDTH, HEIGHT, GDK_RGB_DITHER_NORMAL, 0, 0); cairo_t *cr; gint draw_level = gtk_vu_meter_sound_level_to_draw_level(GTK_VU_METER(vumeter)); cr = gdk_cairo_create (vumeter->window); // set a clip region for the expose event cairo_rectangle (cr,event->area.x, event->area.y, event->area.width, event->area.height); cairo_clip(cr); // Paint from the stored gradient gdk_cairo_set_source_pixbuf(cr, GTK_VU_METER(vumeter)->b_pixbuf,0,0); cairo_paint(cr); // set a clip region for the expose event if (GTK_VU_METER(vumeter)->vertical==TRUE) { cairo_rectangle (cr,event->area.x, event->area.y+draw_level, event->area.width, event->area.height-draw_level); } else { //TODO cairo_rectangle (cr,event->area.x, event->area.y, event->area.width, 12/*event->area.height*/); } cairo_clip(cr); // Paint from the stored gradient gdk_cairo_set_source_pixbuf(cr, GTK_VU_METER(vumeter)->f_pixbuf,0,0); cairo_paint(cr); cairo_destroy(cr); return FALSE; } static gint gtk_vu_meter_sound_level_to_draw_level (GtkVuMeter *vumeter) { gint draw_level; gdouble level, min, max, height; level = (gdouble)vumeter->level; min = (gdouble)vumeter->min; max = (gdouble)vumeter->max; height = (gdouble)vumeter->colors; draw_level = (1.0 - (level - min)/(max - min)) * height; //printf("1.0 - (%lf - %lf)/(%lf - %lf)) * %lf => %i\n", level, min, max, min, height, draw_level); return draw_level; } GtkWidget * gtk_vu_meter_new (gboolean vertical) { GtkWidget *vumeter = g_object_new (GTK_TYPE_VU_METER, NULL); GTK_VU_METER(vumeter)->vertical=vertical; GTK_VU_METER(vumeter)->level=0; GTK_VU_METER(vumeter)->min=-32767; GTK_VU_METER(vumeter)->max=32767; return vumeter; } void gtk_vu_meter_set_min_max (GtkVuMeter *vumeter, gint min, gint max) { g_return_if_fail (vumeter != NULL); vumeter->max = MAX(max, min); vumeter->min = MIN(min, max); if (vumeter->max == vumeter->min) { vumeter->max++; } vumeter->level = CLAMP (vumeter->level, vumeter->min, vumeter->max); gtk_widget_queue_draw (GTK_WIDGET(vumeter)); } void gtk_vu_meter_set_level(GtkVuMeter *vumeter, gint level) { g_return_if_fail (vumeter != NULL); vumeter->level = CLAMP (level, vumeter->min, vumeter->max); gtk_widget_queue_draw (GTK_WIDGET(vumeter)); }