cairo drawing commands to gdk_invalidate_region
Assume I have a routine: int draw(cairo_t *cr) used to draw an overlay in a GdkWindow. In order to minimize redrawing, I would like to get the minimal (up to some accuracy to be determined) set of GdkRegion's that encompasses all the drawing of draw(). I thought of doing this by creating a low resolution cairo image surface that I pass to draw() and check if pixels are "dirty" in which case it indicates that the corresponding rectangle in the source GdkWindow needs to redrawn. Is there a better method? Thanks! Dov ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
On Tue, 2010-08-17 at 10:35 +0300, Dov Grobgeld wrote: > Assume I have a routine: > > int draw(cairo_t *cr) > > used to draw an overlay in a GdkWindow. > > In order to minimize redrawing, I would like to get the minimal (up to > some accuracy to be determined) set of GdkRegion's that encompasses > all the drawing of draw(). > > I thought of doing this by creating a low resolution cairo image > surface that I pass to draw() and check if pixels are "dirty" in which > case it indicates that the corresponding rectangle in the source > GdkWindow needs to redrawn. > > Is there a better method? Just call gdk_cairo_region (cr, event->region); cairo_clip (cr); right after creating the context, and after that, draw as if you didn't need to care about the dirty regions at all. The clipping will take care of the rest. This assumes that you only draw as a response to your expose-event handler, of course. Claudio -- Claudio Saavedra ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
Thanks, but this is not what I am looking for. Here is a description of my flow, which might e.g. be used to draw a rubber band box: 1. motion event calls update() 2. update() calls draw() in order to get regions to expose. 3. update() calls gdk_invalidate_region() based on output from draw 4. exposure-event callback calls draw() to carry out the drawing for each region I.e. draw() is called twice with different purposes. (That's part of the beauty of the generic cairo_t structure). The first call in 2 should be used to determine where we are drawing. The second call in 4 is used to do the drawing. update() holds a list of rectangle corresponding to the old draw() and the new draw() and expose both of these. (The exposure of the old commands are needed to restore the background). Your example indeed shows how to carry out the drawing as response of a exposure-event (point 4 above), but doesn't answer my question of what regions to invalidate (point 2-3). Meanwhile I already have working code in which the user chooses what regions to expose, but I would like to make it automatic to make this unnecessary. Note that I assume that exposing the entire widget for each motion event is too slow. Dov On Tue, Aug 17, 2010 at 11:14, Claudio Saavedra wrote: > On Tue, 2010-08-17 at 10:35 +0300, Dov Grobgeld wrote: > > Assume I have a routine: > > > > int draw(cairo_t *cr) > > > > used to draw an overlay in a GdkWindow. > > > > In order to minimize redrawing, I would like to get the minimal (up to > > some accuracy to be determined) set of GdkRegion's that encompasses > > all the drawing of draw(). > > > > I thought of doing this by creating a low resolution cairo image > > surface that I pass to draw() and check if pixels are "dirty" in which > > case it indicates that the corresponding rectangle in the source > > GdkWindow needs to redrawn. > > > > Is there a better method? > > Just call > > gdk_cairo_region (cr, event->region); > cairo_clip (cr); > > right after creating the context, and after that, draw as if you didn't > need to care about the dirty regions at all. The clipping will take care > of the rest. This assumes that you only draw as a response to your > expose-event handler, of course. > > Claudio > > > -- > Claudio Saavedra > > ___ > gtk-devel-list mailing list > gtk-devel-list@gnome.org > http://mail.gnome.org/mailman/listinfo/gtk-devel-list > ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
On Tue, Aug 17, 2010 at 4:39 AM, Dov Grobgeld wrote: > Thanks, but this is not what I am looking for. > > Here is a description of my flow, which might e.g. be used to draw a rubber > band box: based on an IRC exchange about this a couple of days ago, the answer is two-fold: 1) you assume that cairo_clip() was called with ev->region before draw() is invokved 2) you call something like cairo_clip_rectangle_lists() to get the list of rects that need to be redrawn there have been some suggestions that one should ignore this and just redraw everything and let cairo clipping deal with it, but i think this is clearly wrong when the drawing is computationally expensive. --p ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
Sorry, I still don't get it. In my scenario there is initially is no external request of a region that should be drawn. The only source of what should be drawn, including what areas should be exposed, are in the draw() routine. The challenge is to translate a set of drawing routines to a set of invalidation areas. In any case I solved it as follows: - Generate a low resolution ARGB32 image surface - Call the draw() routine (which receives the information whether it is in mask mode) - Convert all "dirty" pixels into rectangle tiles. - Invalidate the lits of tiles. I hope to post the entire code on github in the next few days. // Call drawing_cb to and use it to generate the rectangle list DovtkLassoRectangleList *rect_list = NULL; int scale_factor = 32; int low_res_width = (selfp->widget->allocation.width+scale_factor-1) / scale_factor; int low_res_height = (selfp->widget->allocation.height+scale_factor-1) / scale_factor; int i; // This should be created in the creation of DovtkLasso cairo_t *cr = NULL; cairo_surface_t *surf = NULL; if (selfp->do_calc_expose_from_cairo) { surf=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, low_res_width, low_res_height); cr = cairo_create(surf); cairo_set_source_rgba(cr,0,0,0,0); cairo_rectangle(cr, 0,0,low_res_height,low_res_width); cairo_fill(cr); cairo_set_source_rgba(cr,0,0,0,1); } cairo_scale(cr,1.0/scale_factor,1.0/scale_factor); selfp->drawing_cb(cr, TRUE, &rect_list); // Turn surf into a list of rectangles if (selfp->do_calc_expose_from_cairo) { int row_idx, col_idx; // Allocate worse case space rect_list = dovtk_lasso_rectangle_list_new(low_res_width*low_res_height); guint8 *buf = cairo_image_surface_get_data(surf); int rect_idx = 0; int row_stride = cairo_image_surface_get_stride(surf); for (row_idx=0; row_idx 0) { cairo_rectangle_t *rect = &rect_list->rectangles[rect_idx++]; rect->x = col_idx*scale_factor; rect->y = row_idx*scale_factor; rect->width = scale_factor; rect->height = scale_factor; } } } rect_list->num_rectangles = rect_idx; cairo_destroy(cr); cairo_surface_destroy(surf); } //printf("num_rectangles = %d\n", rect_list->num_rectangles); // Build a list of expose rectangles from the old and the new lists. // Better done as a linked list. DovtkLassoRectangleList *expose_rect_list = dovtk_lasso_rectangle_list_new(selfp->old_rect_list->num_rectangles + rect_list->num_rectangles); int num_old_rects = selfp->old_rect_list->num_rectangles; for (i=0; irectangles[i] = selfp->old_rect_list->rectangles[i]; for (i=0; inum_rectangles; i++) expose_rect_list->rectangles[num_old_rects + i] = rect_list->rectangles[i]; // Expose the old and the new list of rectangles! for (i=0; inum_rectangles; i++) { // Shortcut cairo_rectangle_t *lasso_rect = &expose_rect_list->rectangles[i]; GdkRectangle rect; rect.x = lasso_rect->x; rect.y = lasso_rect->y; rect.width = lasso_rect->width; rect.height = lasso_rect->height; #if 0 printf("Invalidate region (%d,%d,%d,%d).\n", rect.x,rect.y,rect.width,rect.height); #endif gdk_window_invalidate_rect(selfp->widget->window, &rect, TRUE); } On Tue, Aug 17, 2010 at 15:42, Paul Davis wrote: > On Tue, Aug 17, 2010 at 4:39 AM, Dov Grobgeld > wrote: > > Thanks, but this is not what I am looking for. > > > > Here is a description of my flow, which might e.g. be used to draw a > rubber > > band box: > > > based on an IRC exchange about this a couple of days ago, the answer > is two-fold: > > 1) you assume that cairo_clip() was called with ev->region before > draw() is invokved > 2) you call something like cairo_clip_rectangle_lists() to get the > list of rects that need to be redrawn > > there have been some suggestions that one should ignore this and just > redraw everything and let cairo clipping deal with it, but i think > this is clearly wrong when the drawing is computationally expensive. > > --p > ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
On Tue, Aug 17, 2010 at 9:11 AM, Dov Grobgeld wrote: > Sorry, I still don't get it. In my scenario there is initially is no > external request of a region that should be drawn. The only source of what > should be drawn, including what areas should be exposed, are in the draw() > routine. The challenge is to translate a set of drawing routines to a set of > invalidation areas. This is how you described it: >Here is a description of my flow, which might e.g. be used to draw a rubber >band box: > > 1. motion event calls update() > 2. update() calls draw() in order to get regions to expose. > 3. update() calls gdk_invalidate_region() based on output from draw > 4. exposure-event callback calls draw() to carry out the drawing for each > region this is wrong. update() should just call gdk_invalidate_region(). this will cause expose to be invoked with the region passed in. the way you've described it above, your draw() method does two entirely separate tasks. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
How is update() supposed to know what region to invalidate? By a list of GdkRegions as a parameters? The dual functionality of draw is just a way of automizing this. Dov On Tue, Aug 17, 2010 at 18:50, Paul Davis wrote: > On Tue, Aug 17, 2010 at 9:11 AM, Dov Grobgeld > wrote: > > Sorry, I still don't get it. In my scenario there is initially is no > > external request of a region that should be drawn. The only source of > what > > should be drawn, including what areas should be exposed, are in the > draw() > > routine. The challenge is to translate a set of drawing routines to a set > of > > invalidation areas. > > This is how you described it: > > >Here is a description of my flow, which might e.g. be used to draw a > rubber band box: > > > > 1. motion event calls update() > > 2. update() calls draw() in order to get regions to expose. > > 3. update() calls gdk_invalidate_region() based on output from draw > > 4. exposure-event callback calls draw() to carry out the drawing for > each region > > this is wrong. update() should just call gdk_invalidate_region(). this > will cause expose to be invoked with the region passed in. > the way you've described it above, your draw() method does two > entirely separate tasks. > ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
On Tue, 2010-08-17 at 10:35 +0300, Dov Grobgeld wrote: > Assume I have a routine: > > int draw(cairo_t *cr) > > used to draw an overlay in a GdkWindow. > > In order to minimize redrawing, I would like to get the minimal (up to > some accuracy to be determined) set of GdkRegion's that encompasses > all the drawing of draw(). You may have a different case, but let me tell you about a cute trick I had to do for iogrind [1]. One of iogrind's displays is essentially a point-cloud on which you move a crosshair with the mouse: +-+ | . . | . .. . . .| | . . . |.. . . . .| |-+---| |.. .. . |... . . . | | . . |. . .. .. | +-+ The crosshair spans the whole width and height of the widget. Drawing the points is expensive, as there may be thousands of them. With each mouse motion event, you must repaint the crosshair. My solution went like this: 1. The widget keeps a GdkPixmap where it draws the point-cloud at startup. The points don't move (thankfully), so that pixmap is basically read-only after initialization. 2. My motion-notify handler does this: invalidate_vertical_line (w->cursor_x); invalidate_horizontal_line (w->cursor_y); w->cursor_x = event->x; w->cursor_y = event->y; invalidate_vertical_line (w->cursor_x); invalidate_horizontal_line (w->cursor_y); 3. My expose handler does this: bitblt (w->pixmap_with_points, widget->window); paint_vertical_line (w->cursor_x); paint_horizontal_line (w->cursor_y); So, repaints are very fast as I can avoid regenerating the points; I basically just paint the crosshair and that's it. If your drawing can be done with mostly-static contents and an occasional rubberband rectangle, this is a good way to do it. [1] http://live.gnome.org/iogrind Federico ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
I uploaded my solution that I gave the working name dovtk-lasso to github at: http://github.com/dov/dovtk-lasso On the one hand it works beautifully as it allows drawing dynamic overlays with cairo with very little overhead. On the other hand it still has some problems: - The solution won't work if the default expose-event handler returns TRUE. - I would still like to resolve the issues that Paul has with my description of the code. Paul, why don't you like the fact that I call draw() twice? Have a look at the png file at github to see an example of a caliper like measuring tool that I made with dovtk-lasso. Again, the graphics is arbitrary, and could just as well be a rectangle, a cross hair, or whatever the user feels like. Looking forward to comments. Regards, Dov On Tue, Aug 17, 2010 at 22:03, Federico Mena Quintero wrote: > On Tue, 2010-08-17 at 10:35 +0300, Dov Grobgeld wrote: > > Assume I have a routine: > > > > int draw(cairo_t *cr) > > > > used to draw an overlay in a GdkWindow. > > > > In order to minimize redrawing, I would like to get the minimal (up to > > some accuracy to be determined) set of GdkRegion's that encompasses > > all the drawing of draw(). > > You may have a different case, but let me tell you about a cute trick I > had to do for iogrind [1]. > > One of iogrind's displays is essentially a point-cloud on which you move > a crosshair with the mouse: > > +-+ > | . . | . .. . . .| > | . . . |.. . . . .| > |-+---| > |.. .. . |... . . . | > | . . |. . .. .. | > +-+ > > The crosshair spans the whole width and height of the widget. Drawing > the points is expensive, as there may be thousands of them. With each > mouse motion event, you must repaint the crosshair. > > My solution went like this: > > 1. The widget keeps a GdkPixmap where it draws the point-cloud at > startup. The points don't move (thankfully), so that pixmap is > basically read-only after initialization. > > 2. My motion-notify handler does this: > >invalidate_vertical_line (w->cursor_x); >invalidate_horizontal_line (w->cursor_y); >w->cursor_x = event->x; >w->cursor_y = event->y; >invalidate_vertical_line (w->cursor_x); >invalidate_horizontal_line (w->cursor_y); > > 3. My expose handler does this: > > bitblt (w->pixmap_with_points, widget->window); > paint_vertical_line (w->cursor_x); > paint_horizontal_line (w->cursor_y); > > So, repaints are very fast as I can avoid regenerating the points; I > basically just paint the crosshair and that's it. > > If your drawing can be done with mostly-static contents and an > occasional rubberband rectangle, this is a good way to do it. > > [1] http://live.gnome.org/iogrind > > Federico > > > ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
On Tue, Aug 17, 2010 at 4:22 PM, Dov Grobgeld wrote: > The solution won't work if the default expose-event handler returns TRUE. > I would still like to resolve the issues that Paul has with my description > of the code. Paul, why don't you like the fact that I call draw() twice? that's overstating it a little bit. what i was taking issue with is that *unless* draw() has some way to be able to tell that it is being called from an expose event handler (and that would presumably have to be some global variable, since nothing is passed to it as an argument), it is by definition doing the wrong thing *if* it is responsible for all the rendering that occurs in the expose handler. why? because that would imply that it actually renders in both the pre-expose call and the expose-event call, but you should only be rendering (to the window at list) in the expose event call. you asked: How is update() supposed to know what region to invalidate? in your original description, you showed the flow as: # motion event calls update() # update() calls draw() in order to get regions to expose. # update() calls gdk_invalidate_region() based on output from draw # exposure-event callback calls draw() to carry out the drawing for each region so you have logic that knows what regions are affected by the motion event (the above description makes it sound as if that logic is inside draw()). update should have that logic so that it can call gdk_invalidate_region(), and then the expose-event call to draw() should use the event region list to redraw. if your code works, that's fine. i'm just trying to describe to you what i *think* is the right way to do this so that its portable to all backends of GTK and is following the overall drawing model embedded in GTK. --p ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: cairo drawing commands to gdk_invalidate_region
The problem of deciding what areas to expose are imho opinion too hard for the motion handler to deal with. E.g. if the rubberband is e.g. of an ellipse outline then you need quite a bit of mathematics to figure out what areas to expose. You can of course expose the entire bounding box, but that is very expensive for a large areas if it needs to be done several times a second. The idea of painting a low res mask takes care of it. Regarding the draw() routine, as you can see in the actual code, I do pass a boolean flag that indicates whether it is the low res mask that should be drawn or the actual drawing. This can be used to avoid drawing gradients and other expensive drawing operations during mask creation. In any case, thanks for your input. Dov On Wed, Aug 18, 2010 at 14:47, Paul Davis wrote: > On Tue, Aug 17, 2010 at 4:22 PM, Dov Grobgeld > wrote: > > > The solution won't work if the default expose-event handler returns TRUE. > > I would still like to resolve the issues that Paul has with my > description > > of the code. Paul, why don't you like the fact that I call draw() twice? > > that's overstating it a little bit. what i was taking issue with is > that *unless* draw() has some way to be able to tell that it is being > called from an expose event handler (and that would presumably have to > be some global variable, since nothing is passed to it as an > argument), it is by definition doing the wrong thing *if* it is > responsible for all the rendering that occurs in the expose handler. > why? because that would imply that it actually renders in both the > pre-expose call and the expose-event call, but you should only be > rendering (to the window at list) in the expose event call. > > you asked: How is update() supposed to know what region to invalidate? > > in your original description, you showed the flow as: > ># motion event calls update() > # update() calls draw() in order to get regions to expose. > # update() calls gdk_invalidate_region() based on output from draw > # exposure-event callback calls draw() to carry out the drawing > for each region > > so you have logic that knows what regions are affected by the motion > event (the above description makes it sound as if that logic is inside > draw()). update should have that logic so that it can call > gdk_invalidate_region(), and then the expose-event call to draw() > should use the event region list to redraw. > > if your code works, that's fine. i'm just trying to describe to you > what i *think* is the right way to do this so that its portable to all > backends of GTK and is following the overall drawing model embedded in > GTK. > > --p > ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list