1
votes

I am new at drawing with Cairo and GTK, and the program I'm working on needs to draw a circle tiling of 500x500 or 1000x1000. Also, there are some work to do before drawing but right now I am focused on the drawing part which will involve mouse interaction to change the color of any circle.

So, the tiling is the same, and over time the circles have to change their color (all of them). I have to check with each circle and perform an operation, and after I check all circles, I have to display the changes. This process has to be performed any number of times.

Right now I have the tiling with a scrolled window, but just with this it takes a lot of time the scrolling. Thanks in advance. My code is next:

#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>

static void do_drawing(cairo_t *, GtkWidget *);
static int cellRadius=5;
static int cellDiameter=10;
static int latticeSideSize=500;

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data){  
    do_drawing(cr, widget);  
    return FALSE;
}

static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
  int i=0,j=0;
  GtkWidget *win = gtk_widget_get_toplevel(widget);

  int width, height;
  gtk_window_get_size(GTK_WINDOW(win), &width, &height);

  cairo_set_line_width(cr, .5);  
  cairo_set_source_rgb(cr, 0.69, 0.19, 0);
  cairo_save (cr);

  for(i=0;i<latticeSideSize;i++){

    for(j=0;j<latticeSideSize;j++){
      if(i%2 == 0){
        cairo_arc(cr, cellRadius + 2*cellRadius + j*cellDiameter, cellRadius + cellRadius + i*cellDiameter, cellRadius, 0, 2 * M_PI);
        cairo_stroke(cr);
      }else{
        cairo_arc(cr, cellRadius + cellRadius + j*cellDiameter, cellRadius + cellRadius + i*cellDiameter, cellRadius, 0, 2 * M_PI);
        cairo_stroke(cr);
      }

    }

  }
  cairo_restore (cr);
}

static void destroy( GtkWidget *widget, gpointer   data ){
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *scrolled_window;
  GtkWidget *darea;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
  scrolled_window = gtk_scrolled_window_new (NULL, NULL); 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(scrolled_window), darea);
  gtk_container_add(GTK_CONTAINER(window), scrolled_window);

  g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);
  g_signal_connect(G_OBJECT(scrolled_window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_widget_set_size_request( scrolled_window, 500, 500 );
  gtk_window_set_default_size(GTK_WINDOW(window), 1024, 800); 
  gtk_widget_set_hexpand( scrolled_window, TRUE );
  gtk_widget_set_vexpand( scrolled_window, TRUE );
  gtk_window_set_title(GTK_WINDOW(window), "HexaGrid");
  gtk_widget_set_size_request(darea,cellDiameter*latticeSideSize + 20,cellDiameter*latticeSideSize + 20);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_container_set_border_width(GTK_CONTAINER (scrolled_window), 10);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
1

1 Answers

4
votes

There's two problems here. First, you're doing the same fairly demanding calculation (the circle) 250000 times per draw, so 15 million circles per second if scrolling was smooth: that's not a realistic requirement. You should probably do the circle once and then apply the same result as surface pattern with CAIRO_EXTEND_REPEAT extend mode. You set the position of the pattern by using cairo_translate() and use cairo_set_source() to set your circle pattern as the source and then cairo_rectangle() + cairo_fill() to draw it. Cairo samples contain an example using a bitmap.

If some of the circles need to be in different color, you could draw some or all of them "manually" (without the repeating extend mode) but using a pattern is still probably a good idea to avoid calculating the circle many times.

Second, in the case of complex widgets it makes sense to not draw the whole widget but only the dirty region: see draw-signal documentation.