Swing Animation - CoalescingRepaintCollector
(Page 4 of 8 )
Using SimpleRepaintCollector can be inappropriate when you have requests to repaint small areas of the component, mixed with requests to repaint the entire component. In this case, you will end up repainting the small areas and then immediately repainting over them again in the same frame when you repaint the entire component one or more times. It would be better to use BooleanRepaint-Collector in this case to coalesce all of the repaint requests into one.
Using BooleanRepaintCollector, however, can be inappropriate when you only have requests to repaint a few small areas of the component. In this case you will repaint the entire component unnecessarily, which can slow the animation frame rate down. What is needed, then, is a strategy that is smart enough to behave like SimpleRepaintCollector when there are just a few small repaint areas, and like BooleanRepaintCollector when there is at least one request to repaint the entire component.
At the beginning of each animation loop iteration, CoalescingRepaint-Collector stores the repaint requests individually. If it receives a request to repaint the entire component, however, it will collapse the multiple individual requests into a single request. This makes CoalescingRepaintCollector a good implementation to use when it is not known beforehand whether the Component-Animator will make a request to repaint the entire component during every animation loop iteration.
package com.croftsoft.core.animation.collector;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import com.croftsoft.core.animation.RepaintCollector;
import com.croftsoft.core.util.ArrayLib;
public class CoalescingRepaintCollector
implements RepaintCollector
////////////////////////////////////////////////////
///////////////////////////////////////////////////
{
private static final Rectangle [ ] ALL_REGIONS = new
Rectangle [ ] { new Rectangle ( Integer.MAX_VALUE,
Integer.MAX_VALUE ) };
//
private int count;
private boolean repaintAll;
private Rectangle [ ] repaintRegions
//////////////////////////////////////////////////
/////////////////////////////////////////////////
public CoalescingRepaintCollector( )
////////////////////////////////////////////////
{
repaintRegions = new Rectangle [ 0 ];
}
Boolean repaintAll is the flag that indicates whether the entire component should be repainted during this animation loop iteration. As you can see,
CoalescingRepaintCollector has most of the same instance variables as both SimpleRepaintCollector and BooleanRepaintCollector.
public int getCount ( )
//////////////////////////////////////////////////
{
if ( repaintAll )
{
return 1;
}
boolean hasIntersections = true;
while ( hasIntersections )
{
hasIntersections = false;
iLoop:
for ( int i = 0; i < count - 1; i++ )
{
Rectangle iRectangle = repaintRegions [ i ];
for ( int j = i + 1; j < count; j++ )
{
Rectangle jRectangle = repaintRegions [ j ];
if ( iRectangle.intersects ( jRectangle ) )
{
hasIntersections = true;
Rectangle2D.union ( iRectangle, jRectangle,
iRectangle );
repaintRegions [ j ] = repaintRegions [ count - 1 ];
repaintRegions [ count - 1 ] = jRectangle;
count--;
break iLoop;
}
}
}
}
return count;
}
public Rectangle [ ] getRepaintRegions ( )
//////////////////////////////////////////////////
{
return repaintAll ? ALL_REGIONS : repaintRegions;
}
If the repaintAll flag is set, the accessor methods of CoalescingRepaint-Collector return the same values that the BooleanRepaintCollector would. If the repaintAll flag is false, the accessor methods return the same values that the SimpleRepaintCollector would, with the exception that repaint regions that overlap are coalesced when the getCount() method is called. Whereas the standard Swing RepaintManager will coalesce all repaint regions on the screen, no matter how far apart they are separated into a single large repaint region, Coalescing-RepaintCollector coalesces only those repaint regions that overlap.
public void repaint (
int x,
int y,
int width,
int height )
////////////////////////////////////////////////////
{
if ( repaintAll )
{
return;
}
if ( count == repaintRegions.length )
{
repaintRegions = ( Rectangle [ ] ) ArrayLib.append (
repaintRegions, new Rectangle ( x, y, width,
height ) );
}
else
{
repaintRegions [ count ].setBounds ( x, y, width,
height );
}
count++;
}
Any requests that come in after the repaintAll flag has been set are simply ignored.
public void repaint ( )
/////////////////////////////////////////////////////
{
repaintAll = true;
}
public void reset ( )
///////////////////////////////////////////////////
{
count = 0;
repaintAll = false; }
The repaintAll flag is set the first time a request to repaint the entire component is made during an animation loop iteration and reset at the end of the iteration.
Other Implementations You should feel free to create your own implementations of interface Repaint-Collector if you feel it would improve animation performance. Another implementation that I have tried, for example, is one that coalesces multiple small repaint requests into a single request after a maximum count is reached. This could be useful when you have a large number of sprites swarming about the entire scene. Since you know the intimate details of your own game, you may be able to come up with a custom implementation of RepaintCollector that is more efficient than general-purpose implementations.
This article is excerpted from Advanced Java Game Programming by David Wallace Croft (Apress, 2004; ISBN 1590591232). Check it out at your favorite bookstore today. Buy this book now. |
Next: LoopGovernor >>
More Java Articles
More By Apress Publishing