/* Create a new backing pixmap of the appropriate size */ static gint graph_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { graph_priv_t *priv = g_object_get_data(G_OBJECT(data), "priv"); g_assert(priv); if(priv->pixmap) { int width, height; gdk_drawable_get_size(priv->pixmap, &width, &height); if((width != widget->allocation.width) || (height != widget->allocation.height)) { gdk_pixmap_unref(priv->pixmap); priv->pixmap = NULL; } } if(!priv->pixmap) { priv->pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); graph_draw(GTK_WIDGET(data)); } return TRUE; }
int main(int argc, char **argv) { int width, height; double colour[3]; struct wav_file *wav; float samples[CHUNK_SIZE]; int sample_count; struct graph *gr; cairo_surface_t *surface; /* Validate and parse command-line arguments */ if(parse_arguments(argc, argv, &width, &height, colour) != 0) return 1; /* Open input file */ if( !(wav = wav_open(argv[1]))) { fprintf(stderr, "Unable to open input audio file %s\n", argv[1]); return 1; } /* Buffer all samples from input file. */ gr = graph_init(); while((sample_count = wav_read_samples(wav, samples, CHUNK_SIZE)) > 0) graph_buffer_samples(gr, samples, sample_count); /* Close input file */ wav_close(wav); /* Draw graph and output to PNG file using Cairo */ surface = graph_draw(gr, width, height, colour); if(cairo_surface_write_to_png(surface, argv[2]) != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "Error writing graph to PNG file\n"); return 1; } graph_surface_destroy(surface); graph_destroy(gr); return 0; }
/* We double buffer the image. It looks nice and it enables * grabbing the graph with the pointer and translating it. * We draw on our own larger surface and then copy part of that * to the gdk surface. * * We assume that this function is drawing to an exposed/showing * drawing area, so the status update will reflect the current * exposed/showing drawing area. */ void qp_graph_draw(struct qp_graph *gr, cairo_t *gdk_cr) { GtkAllocation allocation; if(gr->waiting_to_resize_draw && !gr->qp->shape) { //WARN("gr=%p gr->name=\"%s\" gr->ref_count=%d\n", gr, gr->name, gr->ref_count); cairo_set_source_rgba(gdk_cr, gr->background_color.r, gr->background_color.g, gr->background_color.b, gr->background_color.a); cairo_paint(gdk_cr); g_idle_add_full(G_PRIORITY_LOW, idle_callback, gr, NULL); /* fight qp_graph_destroy() race condition with flag */ ++gr->ref_count; /* We draw after the other widgets are drawn, in case drawing * takes a long time. This waiting also gives a chance * for the watch cursor to show. But that seems to only * show if the window had focus at the right time. */ return; } gtk_widget_get_allocation(gr->drawing_area, &allocation); if(gr->pixbuf_needs_draw) { cairo_t *db_cr; /* double buffer cr */ db_cr = cairo_create(gr->pixbuf_surface); graph_draw(gr, db_cr, gr->pixbuf_x, gr->pixbuf_y, gr->pixbuf_width, gr->pixbuf_height); cairo_destroy(db_cr); // debuging //cairo_surface_write_to_png(gr->pixbuf_surface, "x.png"); qp_win_set_status(gr->qp); } /* the GTK cairo_t *gdk_cr has no alpha bits so all the * alpha drawn to it will be smushed. */ //WARN("content=0x%lx\n", (unsigned long)cairo_get_target(gdk_cr)); if(!gr->qp->shape) { /* Not using the shape X11 extension */ /* This is where we go from the back buffer to the drawing area */ draw_from_pixbuf(gdk_cr, gr, allocation.width, allocation.height); if(gr->draw_zoom_box == 1) draw_zoom_box(gdk_cr, gr); if(gr->draw_value_pick) draw_value_pick_line(gdk_cr, gr, allocation.width, allocation.height); if(gr->pixbuf_needs_draw) { gdk_window_set_cursor(gtk_widget_get_window(gr->qp->window), NULL); gr->pixbuf_needs_draw = 0; // gr->qp->wait_warning_showing = 0; } } else { /* Use the X11 shape extension */ /* TODO: This is a resource pig. Fix it. */ cairo_region_t *reg_draw_area, *window_region; /* empty flag */ int empty; cairo_surface_t *mask_surface; GtkAllocation all; /* Make sure the surface is up to date */ //cairo_surface_flush(gr->pixbuf_surface); /* make a sub surface that is the size of the graph drawing area */ mask_surface = cairo_surface_create_for_rectangle(gr->pixbuf_surface, INT(gr->pixbuf_x+gr->grab_x), INT(gr->pixbuf_y+gr->grab_y), allocation.width, allocation.height); reg_draw_area = get_cairo_region_create_from_surface(gr, mask_surface, allocation.width, allocation.height); cairo_surface_destroy(mask_surface); cairo_region_translate(reg_draw_area, allocation.x, allocation.y); gtk_widget_get_allocation(gr->qp->window, &all); all.x = all.y = 0; window_region = cairo_region_create_rectangle(&all); cairo_region_subtract_rectangle(window_region, &allocation); empty = cairo_region_is_empty(reg_draw_area); if(!empty) cairo_region_union(window_region, reg_draw_area); cairo_region_destroy(reg_draw_area); /* window_region is a region with a hole in it the * size of the drawing area with the graph and grid added back. */ if(gr->draw_zoom_box && !empty) { cairo_rectangle_int_t rec; rec.x = allocation.x + gr->z_x; rec.y = allocation.y + gr->z_y; rec.width = gr->z_w; rec.height = gr->z_h; /* regions do not like negitive values or * maybe shapes do not like negitive values * in any case we keep width and height * positive */ if(rec.width < 0) { rec.width *= -1; rec.x -= rec.width; } if(rec.height < 0) { rec.height *= -1; rec.y -= rec.height; } cairo_region_union_rectangle(window_region, &rec); /* now we have the zoom box added to window_region */ } /* This is where we go from the back buffer to the drawing area */ draw_from_pixbuf(gdk_cr, gr, allocation.width, allocation.height); if(gr->draw_zoom_box) draw_zoom_box(gdk_cr, gr); if(gr->draw_value_pick) draw_value_pick_line(gdk_cr, gr, allocation.width, allocation.height); if(empty) { /* we have nothing to make a shape with */ if(gr->qp->last_shape_region) { cairo_region_destroy(gr->qp->last_shape_region); gr->qp->last_shape_region = NULL; } cairo_region_destroy(window_region); /* remove the old shape region */ gtk_widget_shape_combine_region(gr->qp->window, NULL); } else if(!gr->qp->last_shape_region || !cairo_region_equal(gr->qp->last_shape_region, window_region)) { // DEBUG("creating new shape region\n"); /* We need to undo the old shape first */ gtk_widget_shape_combine_region(gr->qp->window, NULL); gtk_widget_shape_combine_region(gr->qp->window, window_region); if(gr->qp->last_shape_region) cairo_region_destroy(gr->qp->last_shape_region); gr->qp->last_shape_region = window_region; } else cairo_region_destroy(window_region); gr->pixbuf_needs_draw = 0; gdk_window_set_cursor(gtk_widget_get_window(gr->qp->window), NULL); // debuging //cairo_surface_write_to_png(cairo_get_target(gdk_cr), "y.png"); } if(gr->qp->update_graph_detail && gr->qp->graph_detail) { gr->qp->update_graph_detail = 0; /* make the graph configure window show stuff about this graph */ qp_win_graph_detail_init(gr->qp); } }
int graph_redraw (graph_t *graph, void *context) { return graph_draw(graph, context); }