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.
This commit is contained in:
Alexander Larsson 2009-02-09 14:18:27 +01:00 committed by Alexander Larsson
parent f0fb3f450f
commit 05d3fc6bbd

View File

@ -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;
impl_window->outstanding_moves =
g_list_append (impl_window->outstanding_moves, move);
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);
}
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,