Re: Size allocation and redrawing issues



Owen Taylor <otaylor redhat com> writes:

> OK, I've gone ahead and done this, and also made GtkBox, GtkFrame,
> GtkAlignment take advantage of this. We can't do this kind of change
> later as an optimization, asI say in Changes-2.0.txt
> 
> * The GtkBox, GtkTable, and GtkAlignment widgets now call
>   gtk_widget_set_redraw_on_allocate (widget, FALSE); on themselves.
>   If you want to actually draw contents in a widget derived from
>   one of these widgets, you'll probably want to change this
>   in your init() function.

/me mumbles something abusive about inheriting from non-abstract
classes.

> Drawing speed is not really that proportional to the number of pixels
> drawn in most cases, 

Well, redrawing a layout entirely during opaque resize is at least an
order of magnitude more pixels than redrawing only the uncovered
pixels. And, unless the layout is very simple, it is also an order of
magnitude more executed graphics primitives, so I think it is
worthwhile to reduce the invalidated areas as much as possible.

I also think that later on, it would be worthwhile to implement a more
advanced region datastructure that would support a call like

        gdk_region_get_covering (GdkRegion    *region, 
                                 gint          percentage, 
                                 GdkRectangle *rects, 
                                 int          *n_rects)

that would generate a covering of the region, "wasting" no more than
the given percentage of pixels, with as few rectangles as
possible. (With more advanced drawing API's with alpha channels,
pixels could become expensive again because of potentially a lot of
translucent layers).

> so trying to optimize GtkFrame to only redraw
> little areas sounds like a lot of complexity for not so big again.

The attached patch does it without much lot of complexity (it just
invalidates (widget->allocation - child_allocation) twice). The
height_extra change was a subtle bug anyway.

> > >  - Invalidation when a widget is resized or moved is "shallow" as 
> > >    described above - only the windows that need to be invalidated
> > >    are invalidated.
> > 
> > There are still some calls to queue_clear that invalidates
> > _everything_ on opaque resize, including one in
> > gtk_widget_queue_resize, so your patch doesn't actually reduce the
> > amount of redrawing. It is easy to remove this invalidation, though.
> 
> I've fixed some places in my commit for reduced invalidation on
> opaque resize.
> 
>  - make queue_resize() invalidate shallowly, not deeply.

This means that queue_resize() must not be called if the widget's
allocation is not in sync with its window's size. And in fact,
gtkwindow.c, line 4184 does just that. I don't really have much of a
clue what is going on in gtkwindow.c, but I noticed weird
invalidations and traced them to that line.

The patch fixes this by removing the invalidations from
gtk_widget_queue_resize() entirely.

I think this is safe because queue_resize() implies a call to
size_allocate(), which implies a shallow invalidation (in case of
REDRAW_ON_ALLOCATE) or an "uncovered area" allocation (in case of
!REDRAW_ON_ALLOCATE).


Here is what the patch does:

        - makes --gtk-debug=updates uglier and more useful: it now
          show the exact region that was invalidated, not the exact
          region exposed.

        - makes GtkFrame only redraw the old and new frame positions

        - makes GtkPaned !REDRAW_ON_ALLOCATE

        - removes invalidation from gtk_widget_queue_resize()


Søren

? redraw_patch
? redraw.patch
Index: docs/Changes-2.0.txt
===================================================================
RCS file: /cvs/gnome/gtk+/docs/Changes-2.0.txt,v
retrieving revision 1.38
diff -u -p -r1.38 Changes-2.0.txt
--- docs/Changes-2.0.txt	2001/11/04 22:57:01	1.38
+++ docs/Changes-2.0.txt	2001/11/05 22:12:42
@@ -509,8 +509,8 @@ Incompatible Changes from GTK+-1.2 to GT
   longer need to gtk_widget_push_cmap (gtk_preview_get_cmap ()) in
   your code.
 
-* The GtkBox, GtkTable, and GtkAlignment widgets now call
+* The GtkBox, GtkTable, GtkFrame and GtkAlignment widgets now call
   gtk_widget_set_redraw_on_allocate (widget, FALSE); on themselves.
-  If you want to actually draw contents in a widget derived from
-  one of these widgets, you'll probably want to change this
-  in your init() function.
+  If you want to actually draw contents in a widget derived from one
+  of these widgets, you'll probably want to change this in your init()
+  function.
Index: gdk/gdkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v
retrieving revision 1.124
diff -u -p -r1.124 gdkwindow.c
--- gdk/gdkwindow.c	2001/11/04 22:57:02	1.124
+++ gdk/gdkwindow.c	2001/11/05 22:12:42
@@ -2151,6 +2151,81 @@ gdk_window_invalidate_rect   (GdkWindow 
   gdk_region_destroy (region);
 }
 
+static GdkColor
+get_ugly_color (void)
+{
+  static int x;
+  GdkColor ugly_color;
+  
+  x++;
+  if (x == 1)
+    {
+      ugly_color.pixel = 0;
+      ugly_color.red = 50000;
+      ugly_color.green = 20000;
+      ugly_color.blue = 20000;
+    }
+  else if (x == 2)
+    {
+      ugly_color.pixel = 0;
+      ugly_color.red = 20000;
+      ugly_color.green = 20000;
+      ugly_color.blue = 50000;
+    }
+  else if (x == 3)
+    {
+      ugly_color.pixel = 0;
+      ugly_color.red = 65000;
+      ugly_color.green = 65000;
+      ugly_color.blue = 20000;
+    }
+  else
+    {
+      ugly_color.pixel = 0;
+      ugly_color.red = 20000;
+      ugly_color.green = 60000;
+      ugly_color.blue = 20000;
+      
+      x = 0;
+    }
+
+  return ugly_color;
+}
+
+static void
+draw_ugly_color (GdkWindow *window,
+                GdkRegion *region)
+{
+  GdkRectangle *ugly_rects;
+  gint n_ugly_rects;
+  int i;
+
+  gdk_region_get_rectangles (region, &ugly_rects, &n_ugly_rects);
+
+  for (i = 0; i < n_ugly_rects; i++)
+    {
+      GdkColor ugly_color = get_ugly_color ();
+      GdkGC *ugly_gc;
+    
+      ugly_gc = gdk_gc_new (window);
+      gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
+  
+      gdk_draw_rectangle (window,
+                         ugly_gc,
+                         TRUE,
+                         ugly_rects[i].x, ugly_rects[i].y,
+                         ugly_rects[i].width, ugly_rects[i].height);
+
+      g_object_unref (G_OBJECT (ugly_gc));
+    }
+      
+  gdk_flush ();
+
+  g_free (ugly_rects);
+  
+  g_usleep (10000);
+}
+
 /**
  * gdk_window_invalidate_maybe_recurse:
  * @window: a #GdkWindow
@@ -2200,26 +2275,7 @@ gdk_window_invalidate_maybe_recurse (Gdk
   if (!gdk_region_empty (visible_region))
     {
       if (debug_updates)
-        {
-          /* Draw ugly color all over the newly-invalid region */
-          GdkRectangle ugly_rect;
-          GdkGC *ugly_gc;
-          GdkColor ugly_color = { 0, 60000, 10000, 10000 };
-          
-          ugly_gc = gdk_gc_new (window);
-
-          gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
-          
-	  gdk_region_get_clipbox (visible_region, &ugly_rect);
-
-          gdk_draw_rectangle (window,
-                              ugly_gc,
-                              TRUE,
-                              ugly_rect.x, ugly_rect.y,
-                              ugly_rect.width, ugly_rect.height);
-          
-          g_object_unref (G_OBJECT (ugly_gc));
-        }
+	draw_ugly_color (window, visible_region);
       
       if (private->update_area)
 	{
@@ -2256,7 +2312,7 @@ gdk_window_invalidate_maybe_recurse (Gdk
 		  child_region = gdk_region_copy (visible_region);
 		  gdk_region_offset (child_region, -x, -y);
 		  
-		  gdk_window_invalidate_region ((GdkWindow *)child, child_region, TRUE);
+		  gdk_window_invalidate_maybe_recurse ((GdkWindow *)child, child_region, child_func, user_data);
 		  
 		  gdk_region_destroy (child_region);
 		}
Index: gtk/gtkframe.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkframe.c,v
retrieving revision 1.41
diff -u -p -r1.41 gtkframe.c
--- gtk/gtkframe.c	2001/11/04 22:57:03	1.41
+++ gtk/gtkframe.c	2001/11/05 22:12:42
@@ -184,6 +184,8 @@ gtk_frame_class_init (GtkFrameClass *cla
 static void
 gtk_frame_init (GtkFrame *frame)
 {
+  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (frame), FALSE);
+
   frame->label_widget = NULL;
   frame->shadow_type = GTK_SHADOW_ETCHED_IN;
   frame->label_xalign = 0.0;
@@ -522,8 +524,10 @@ gtk_frame_paint (GtkWidget    *widget,
 	    xalign = 1 - frame->label_xalign;
 
 	  height_extra = MAX (0, child_requisition.height - widget->style->xthickness);
-	  y -= height_extra * (1 - frame->label_yalign);
-	  height += height_extra * (1 - frame->label_yalign);
+	  height_extra *= (1 - frame->label_yalign);
+
+	  y -= height_extra; 
+	  height += height_extra;
 	  
 	  x2 = widget->style->xthickness + (frame->child_allocation.width - child_requisition.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_SIDE_PAD;
 
@@ -594,6 +598,29 @@ gtk_frame_size_request (GtkWidget      *
 }
 
 static void
+gtk_frame_invalidate_frame (GtkFrame *frame)
+{
+  GtkWidget *widget;
+
+  widget = GTK_WIDGET (frame);
+  
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      GdkRegion *invalidate, *tmp;
+  
+      invalidate = gdk_region_rectangle (&(widget->allocation));
+      tmp = gdk_region_rectangle (&(frame->child_allocation)); 
+
+      gdk_region_subtract (invalidate, tmp);
+
+      gdk_region_destroy (tmp);
+      
+      gdk_window_invalidate_region (widget->window, invalidate, FALSE);
+      gdk_region_destroy (invalidate);
+    }
+}
+
+static void
 gtk_frame_size_allocate (GtkWidget     *widget,
 			 GtkAllocation *allocation)
 {
@@ -604,21 +631,15 @@ gtk_frame_size_allocate (GtkWidget     *
   widget->allocation = *allocation;
 
   gtk_frame_compute_child_allocation (frame, &new_allocation);
-  
-  /* If the child allocation changed, that means that the frame is drawn
-   * in a new place, so we must redraw the entire widget.
-   */
-  if (GTK_WIDGET_MAPPED (widget) &&
-      (new_allocation.x != frame->child_allocation.x ||
-       new_allocation.y != frame->child_allocation.y ||
-       new_allocation.width != frame->child_allocation.width ||
-       new_allocation.height != frame->child_allocation.height))
-    gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
-  
+
+  gtk_frame_invalidate_frame (frame);
+
   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
     gtk_widget_size_allocate (bin->child, &new_allocation);
   
   frame->child_allocation = new_allocation;
+
+  gtk_frame_invalidate_frame (frame);
   
   if (frame->label_widget && GTK_WIDGET_VISIBLE (frame->label_widget))
     {
Index: gtk/gtkhpaned.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkhpaned.c,v
retrieving revision 1.31
diff -u -p -r1.31 gtkhpaned.c
--- gtk/gtkhpaned.c	2001/07/18 23:39:22	1.31
+++ gtk/gtkhpaned.c	2001/11/05 22:12:42
@@ -197,6 +197,8 @@ gtk_hpaned_size_allocate (GtkWidget     
 				  paned->handle_ypos,
 				  handle_size,
 				  paned->handle_height);
+	  
+	  gdk_window_invalidate_rect (paned->handle, NULL, FALSE);
 	}
       
       child1_allocation.height = child2_allocation.height = MAX (1, (gint) allocation->height - border_width * 2);
Index: gtk/gtkpaned.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkpaned.c,v
retrieving revision 1.38
diff -u -p -r1.38 gtkpaned.c
--- gtk/gtkpaned.c	2001/10/27 00:41:11	1.38
+++ gtk/gtkpaned.c	2001/11/05 22:12:42
@@ -150,6 +150,7 @@ static void
 gtk_paned_init (GtkPaned *paned)
 {
   GTK_WIDGET_UNSET_FLAGS (paned, GTK_NO_WINDOW);
+  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (paned), FALSE);
   
   paned->child1 = NULL;
   paned->child2 = NULL;
Index: gtk/gtkvpaned.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkvpaned.c,v
retrieving revision 1.33
diff -u -p -r1.33 gtkvpaned.c
--- gtk/gtkvpaned.c	2001/07/18 23:39:25	1.33
+++ gtk/gtkvpaned.c	2001/11/05 22:12:42
@@ -197,6 +197,8 @@ gtk_vpaned_size_allocate (GtkWidget     
 				  paned->handle_ypos,
 				  paned->handle_width,
 				  handle_size);
+
+	  gdk_window_invalidate_rect (paned->handle, NULL, FALSE);
 	}
 
       child1_allocation.width = child2_allocation.width = MAX (1, (gint) allocation->width - border_width * 2);
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.262
diff -u -p -r1.262 gtkwidget.c
--- gtk/gtkwidget.c	2001/11/04 22:57:03	1.262
+++ gtk/gtkwidget.c	2001/11/05 22:12:42
@@ -2085,13 +2085,6 @@ gtk_widget_queue_resize (GtkWidget *widg
   
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      region = gdk_region_rectangle (&widget->allocation);
-      gtk_widget_invalidate_widget_windows (widget, region);
-      gdk_region_destroy (region);
-    }
-      
   _gtk_size_group_queue_resize (widget);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]