[gtk+/client-side-windows: 265/284] Try to combine consecutive window moves into one



commit 05d3fc6bbd39cf264145ac3ee5cbccd0d7a774b3
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Feb 9 14:18:27 2009 +0100

    Try to combine consecutive window moves into one
    
    It often happens that we move region A to B and then we move a subset
    of B to C. When possible we'd like to replace this with a move from
    A directly to C, and a suplimentary move from A to the areas of B not
    overwritten by C.
    
    Getting an optimal move combiner seems quite complicated, but this
    simple approach gets most of the interesting cases right and isn't
    all to complicated.
---
 gdk/gdkwindow.c |  112 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 106 insertions(+), 6 deletions(-)

diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 8d02dc1..ab44c72 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -2536,15 +2536,115 @@ gdk_window_region_move_free (GdkWindowRegionMove *move)
 
 static void
 append_move_region (GdkWindowObject *impl_window,
-		    GdkRegion *region,
+		    GdkRegion *new_dest_region,
 		    int dx, int dy)
 {
-  GdkWindowRegionMove *move;
-      
-  move = gdk_window_region_move_new (region, dx, dy);
+  GdkWindowRegionMove *move, *old_move;
+  GdkRegion *new_total_region, *old_total_region;
+  GdkRegion *source_overlaps_destination;
+  GdkRegion *non_overwritten;
+  gboolean added_move;
+  GList *l, *prev;
+
+  if (gdk_region_empty (new_dest_region))
+    return;
+
+  /* In principle this could just append the move to the list of outstanding
+     moves that will be replayed before drawing anything when we're handling
+     exposes. However, we'd like to do a bit better since its commonly the case
+     that we get multiple copies where A is copied to B and then B is copied
+     to C, and we'd like to express this as a simple copy A to C operation. */
+
+  /* We approach this by taking the new move and pushing it ahead of moves
+     starting at the end of the list and stopping when its not safe to do so.
+     Its not safe to push past a move is either the source of the new move
+     is in the destination of the old move, or if the destination of the new
+     move is in the source of the new move, or if the destination of the new
+     move overlaps the destination of the old move. We simplify this by
+     just comparing the total regions (src + dest) */
+  new_total_region = gdk_region_copy (new_dest_region);
+  gdk_region_offset (new_total_region, -dx, -dy);
+  gdk_region_union (new_total_region, new_dest_region);
+
+  added_move = FALSE;
+  for (l = g_list_last (impl_window->outstanding_moves); l != NULL; l = prev)
+    {
+      prev = l->prev;
+      old_move = l->data;
+  
+      old_total_region = gdk_region_copy (old_move->dest_region);
+      gdk_region_offset (old_total_region, -old_move->dx, -old_move->dy);
+      gdk_region_union (old_total_region, old_move->dest_region);
+
+      gdk_region_intersect (old_total_region, new_total_region);
+      /* If these regions intersect then its not safe to push the
+	 new region before the old one */
+      if (!gdk_region_empty (old_total_region))
+	{
+	  /* The area where the new moves source overlaps the old ones
+	     destination */
+	  source_overlaps_destination = gdk_region_copy (new_dest_region);
+	  gdk_region_offset (source_overlaps_destination, -dx, -dy);
+	  gdk_region_intersect (source_overlaps_destination, old_move->dest_region);
+	  gdk_region_offset (source_overlaps_destination, dx, dy);
+
+	  /* We can do all sort of optimizations here, but to do things safely it becomes
+	     quite complicated. However, a very common case is that you copy something first,
+	     then copy all that or a subset of it to a new location (i.e. if you scroll twice
+	     in the same direction). We'd like to detect this case and optimize it to one
+	     copy. */
+	  if (gdk_region_equal (source_overlaps_destination, new_dest_region))
+	    {
+	      /* This means we might be able to replace the old move and the new one
+		 with the new one read from the old ones source, and a second copy of
+		 the non-overwritten parts of the old move. However, such a split
+		 is only valid if the source in the old move isn't overwritten
+		 by the destination of the new one */
+
+	      /* the new destination of old move if split is ok: */
+	      non_overwritten = gdk_region_copy (old_move->dest_region);
+	      gdk_region_subtract (non_overwritten, new_dest_region);
+	      /* move to source region */
+	      gdk_region_offset (non_overwritten, -old_move->dx, -old_move->dy);
+	      
+	      gdk_region_intersect (non_overwritten, new_dest_region);
+	      if (gdk_region_empty (non_overwritten))
+		{
+		  added_move = TRUE;
+		  move = gdk_window_region_move_new (new_dest_region,
+						     dx + old_move->dx,
+						     dy + old_move->dy);
+		  
+		  impl_window->outstanding_moves =
+		    g_list_insert_before (impl_window->outstanding_moves,
+					  l, move);
+		  gdk_region_subtract (old_move->dest_region, new_dest_region);
+		}
+	      gdk_region_destroy (non_overwritten);
+	    }
+
+	  gdk_region_destroy (source_overlaps_destination);
+	  gdk_region_destroy (old_total_region);
+	  break; 
+	}
+      gdk_region_destroy (old_total_region);
+    }
 
-  impl_window->outstanding_moves =
-    g_list_append (impl_window->outstanding_moves, move);
+  gdk_region_destroy (new_total_region);
+  
+  if (!added_move)
+    {
+      move = gdk_window_region_move_new (new_dest_region, dx, dy);
+	  
+      if (l == NULL)
+	impl_window->outstanding_moves =
+	  g_list_prepend (impl_window->outstanding_moves,
+			  move);
+      else
+	impl_window->outstanding_moves =
+	  g_list_insert_before (impl_window->outstanding_moves,
+				l->next, move);
+    }
 }
 
 /* Moves bits and update area by dx/dy in impl window,



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