Testing shows that for heavyweight components, all pending paint events
get coalesced. The ComponentPeer.coalescePaintEvent() method is used to
track the dirty area of a heavyweight component.
I implemented it that way for the GtkComponentPeer and
SwingComponentPeer.
2006-11-29 Roman Kennke <[EMAIL PROTECTED]>
* java/awt/Component.java
(isShowing): Simplified condition code and avoid unnecessary
if-codepaths.
(coalesceEvents): Always coalesce paint events and let the peer
figure out the expanding of the repaint area.
* gnu/java/awt/peer/swing/SwingComponentPeer.java
(currentPaintEvents): Removed. Replaced by paintArea.
(paintArea): New field. Tracks the dirty area.
(SwingComponentPeer): Removed init of currentPaintEvents.
(coalescePaintEvent): Simplified to only union the dirty regions.
(handleEvent): Paint dirty region that was tracked in paintArea.
* gnu/java/awt/peer/gtk/GtkComponentPeer.java
(paintArea): New field. Tracks the dirty region.
(coalescePaintEvent): Implemented to track the dirty region.
(paintComponent): Use the dirty region in paintArea. Protect
state by putting the paint and dispose code in a try-finally.
(updateComponent): Use the dirty region in paintArea. Protect
state by putting the paint and dispose code in a try-finally.
/Roman
Index: java/awt/Component.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Component.java,v
retrieving revision 1.150
diff -u -1 -5 -r1.150 Component.java
--- java/awt/Component.java 13 Oct 2006 15:15:12 -0000 1.150
+++ java/awt/Component.java 29 Nov 2006 12:56:04 -0000
@@ -811,34 +811,31 @@
public boolean isVisible()
{
return visible;
}
/**
* Tests whether or not this component is actually being shown on
* the screen. This will be true if and only if it this component is
* visible and its parent components are all visible.
*
* @return true if the component is showing on the screen
* @see #setVisible(boolean)
*/
public boolean isShowing()
{
- if (! visible || peer == null)
- return false;
-
- return parent == null ? false : parent.isShowing();
+ return visible && peer != null && (parent == null || parent.isShowing());
}
/**
* Tests whether or not this component is enabled. Components are enabled
* by default, and must be enabled to receive user input or generate events.
*
* @return true if the component is enabled
* @see #setEnabled(boolean)
*/
public boolean isEnabled()
{
return enabled;
}
/**
@@ -3615,30 +3612,36 @@
break;
case PaintEvent.PAINT:
case PaintEvent.UPDATE:
// For heavyweights the EventQueue should ask the peer.
if (peer == null || peer instanceof LightweightPeer)
{
PaintEvent pe1 = (PaintEvent) existingEvent;
PaintEvent pe2 = (PaintEvent) newEvent;
Rectangle r1 = pe1.getUpdateRect();
Rectangle r2 = pe2.getUpdateRect();
if (r1.contains(r2))
coalesced = existingEvent;
else if (r2.contains(r1))
coalesced = newEvent;
}
+ else
+ {
+ // Replace the event and let the heavyweight figure out the expanding
+ // of the repaint area.
+ coalesced = newEvent;
+ }
break;
default:
coalesced = null;
}
return coalesced;
}
/**
* Processes the specified event. In this class, this method simply
* calls one of the more specific event handlers.
*
* @param e the event to process
* @throws NullPointerException if e is null
* @see #processComponentEvent(ComponentEvent)
* @see #processFocusEvent(FocusEvent)
Index: gnu/java/awt/peer/swing/SwingComponentPeer.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/swing/SwingComponentPeer.java,v
retrieving revision 1.6
diff -u -1 -5 -r1.6 SwingComponentPeer.java
--- gnu/java/awt/peer/swing/SwingComponentPeer.java 9 Nov 2006 20:53:24 -0000 1.6
+++ gnu/java/awt/peer/swing/SwingComponentPeer.java 29 Nov 2006 12:56:04 -0000
@@ -52,95 +52,85 @@
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.BufferCapabilities.FlipContents;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.PaintEvent;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.VolatileImage;
import java.awt.peer.ComponentPeer;
import java.awt.peer.ContainerPeer;
import java.awt.peer.LightweightPeer;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
import javax.swing.JComponent;
import javax.swing.RepaintManager;
/**
* The base class for Swing based component peers. This provides the basic
* functionality needed for Swing based component peers. Many methods are
* implemented to forward to the Swing component. Others however forward
* to the component's parent and expect the toplevel component peer to provide
* a real implementation of it. These are for example the key methods
* [EMAIL PROTECTED] #getGraphics()} and [EMAIL PROTECTED] #createImage(int, int)}, as well as
* [EMAIL PROTECTED] #getLocationOnScreen()}.
*
* This class also provides the necesary hooks into the Swing painting and
* event handling system. In order to achieve this, it traps paint, mouse and
* key events in [EMAIL PROTECTED] #handleEvent(AWTEvent)} and calls some special methods
- * ([EMAIL PROTECTED] #peerPaint(Graphics,boolean)}, [EMAIL PROTECTED] #handleKeyEvent(KeyEvent)},
+ * ([EMAIL PROTECTED] #peerPaint(Graphics)}, [EMAIL PROTECTED] #handleKeyEvent(KeyEvent)},
* [EMAIL PROTECTED] #handleMouseEvent(MouseEvent)} and
* [EMAIL PROTECTED] #handleMouseMotionEvent(MouseEvent)}) that call the corresponding
* Swing methods.
*
* @author Roman Kennke ([EMAIL PROTECTED])
*/
public class SwingComponentPeer
implements ComponentPeer
{
/**
* The AWT component for this peer.
*/
protected Component awtComponent;
/**
* The Swing component for this peer.
*/
protected SwingComponent swingComponent;
/**
* The font that is set for this peer.
*/
protected Font peerFont;
/**
- * The repaint requests that will be handled next. The events queued
- * up here are in the exact same order as they appear in
- * [EMAIL PROTECTED] #coalescePaintEvent(PaintEvent)}, that is in event queue order.
- * This is used for coalescing paint events.
- *
- * @see #coalescePaintEvent(PaintEvent)
+ * The current repaint area.
*/
- protected List currentPaintEvents;
+ protected Rectangle paintArea;
/**
* Creates a SwingComponentPeer instance. Subclasses are expected to call
- * this constructor and thereafter call [EMAIL PROTECTED] #init(Component,
- * SwingComponent)}in order to setup the AWT and Swing components properly.
+ * this constructor and thereafter call [EMAIL PROTECTED] #init(Component, JComponent)}
+ * in order to setup the AWT and Swing components properly.
*/
protected SwingComponentPeer()
{
- // Initialize paint event queue.
- currentPaintEvents = new LinkedList();
-
+ // Nothing to do here.
}
/**
* Initializes the AWT and Swing component for this peer. It is expected that
* subclasses call this from within their constructor.
*
* @param awtComp the AWT component for this peer
* @param swingComp the Swing component for this peer
*/
protected void init(Component awtComp, SwingComponent swingComp)
{
awtComponent = awtComp;
swingComponent = swingComp;
if (swingComponent != null)
{
@@ -400,48 +390,46 @@
* [EMAIL PROTECTED] Component#dispatchEvent(AWTEvent)} to give the peer a chance to
* react to events for the component.
*
* @param e the event
*/
public void handleEvent(AWTEvent e)
{
switch (e.getID())
{
case PaintEvent.UPDATE:
case PaintEvent.PAINT:
// Need to synchronize to avoid threading problems on the
// paint event list.
// We must synchronize on the tree lock first to avoid deadlock,
// because Container.paint() will grab it anyway.
- synchronized (awtComponent.getTreeLock())
+ synchronized (this)
{
- synchronized (currentPaintEvents)
+ assert paintArea != null;
+ if (awtComponent.isShowing())
{
- if (currentPaintEvents.contains(e))
+ Graphics g = awtComponent.getGraphics();
+ try
{
- Graphics g = awtComponent.getGraphics();
- try
- {
- Rectangle clip = ((PaintEvent) e).getUpdateRect();
- g.clipRect(clip.x, clip.y, clip.width, clip.height);
- peerPaint(g, e.getID() == PaintEvent.UPDATE);
- }
- finally
- {
- g.dispose();
- }
- currentPaintEvents.remove(e);
+ Rectangle clip = paintArea;
+ g.clipRect(clip.x, clip.y, clip.width, clip.height);
+ peerPaint(g, e.getID() == PaintEvent.UPDATE);
+ }
+ finally
+ {
+ g.dispose();
+ paintArea = null;
}
}
}
break;
case MouseEvent.MOUSE_PRESSED:
case MouseEvent.MOUSE_RELEASED:
case MouseEvent.MOUSE_CLICKED:
case MouseEvent.MOUSE_ENTERED:
case MouseEvent.MOUSE_EXITED:
handleMouseEvent((MouseEvent) e);
break;
case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_DRAGGED:
handleMouseMotionEvent((MouseEvent) e);
break;
@@ -820,59 +808,37 @@
* @return <code>true</code> if this component peer can determine if the
* component has been obscured, <code>false</code> otherwise
*/
public boolean canDetermineObscurity()
{
return false;
}
/**
* Coalesces the specified paint event.
*
* @param e the paint event
*/
public void coalescePaintEvent(PaintEvent e)
{
- synchronized (currentPaintEvents)
+ synchronized (this)
{
Rectangle newRect = e.getUpdateRect();
- boolean coalesced = false;
- for (Iterator i = currentPaintEvents.iterator(); i.hasNext() && ! coalesced;)
- {
- PaintEvent e2 = (PaintEvent) i.next();
- if (e.getID() == e2.getID())
- {
- Rectangle oldRect = e2.getUpdateRect();
- if (oldRect.contains(newRect))
- {
- // Merge newRect into oldRect. We have to discard the old request
- // so that the events are still in the correct order.
- i.remove();
- newRect.setBounds(oldRect);
- coalesced = true;
- }
- else if (newRect.contains(oldRect))
- {
- // Merge oldRect into newRect. We have to discard the old request
- // so that the events are still in the correct order.
- i.remove();
- coalesced = true;
- }
- }
- // TODO: Maybe do something more clever here.
- }
- currentPaintEvents.add(e);
+ if (paintArea == null)
+ paintArea = newRect;
+ else
+ Rectangle.union(paintArea, newRect, paintArea);
}
}
/**
* Updates the cursor. This is not yet implemented.
*/
public void updateCursorImmediately()
{
// Nothing to do here yet.
}
/**
* Returns true, if this component can handle wheel scrolling,
* <code>false</code> otherwise.
*
Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v
retrieving revision 1.121
diff -u -1 -5 -r1.121 GtkComponentPeer.java
--- gnu/java/awt/peer/gtk/GtkComponentPeer.java 8 Aug 2006 22:23:36 -0000 1.121
+++ gnu/java/awt/peer/gtk/GtkComponentPeer.java 29 Nov 2006 12:56:04 -0000
@@ -78,30 +78,35 @@
import java.awt.peer.LightweightPeer;
import java.awt.peer.WindowPeer;
import java.util.Timer;
import java.util.TimerTask;
public class GtkComponentPeer extends GtkGenericPeer
implements ComponentPeer
{
VolatileImage backBuffer;
BufferCapabilities caps;
Component awtComponent;
Insets insets;
+ /**
+ * The current repaint area.
+ */
+ protected Rectangle paintArea;
+
/* this isEnabled differs from Component.isEnabled, in that it
knows if a parent is disabled. In that case Component.isEnabled
may return true, but our isEnabled will always return false */
native boolean isEnabled ();
static native boolean modalHasGrab();
native int[] gtkWidgetGetForeground ();
native int[] gtkWidgetGetBackground ();
native void gtkWidgetGetDimensions (int[] dim);
native void gtkWidgetGetPreferredDimensions (int[] dim);
native void gtkWindowGetLocationOnScreen (int[] point);
native void gtkWidgetGetLocationOnScreen (int[] point);
native void gtkWidgetSetCursor (int type, GtkImage image, int x, int y);
native void gtkWidgetSetCursorUnlocked (int type, GtkImage image,
int x, int y);
@@ -296,56 +301,70 @@
// This method and its overrides are the only methods in the peers
// that should call awtComponent.paint.
protected void paintComponent (PaintEvent event)
{
// Do not call Component.paint if the component is not showing or
// if its bounds form a degenerate rectangle.
if (!awtComponent.isShowing()
|| (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1))
return;
// Creating and disposing a GdkGraphics every time paint is called
// seems expensive. However, the graphics state does not carry
// over between calls to paint, and resetting the graphics object
// may even be more costly than simply creating a new one.
- Graphics g = getGraphics();
-
- g.setClip(event.getUpdateRect());
-
- awtComponent.paint(g);
-
- g.dispose();
+ synchronized (paintArea)
+ {
+ Graphics g = getGraphics();
+ try
+ {
+ g.setClip(paintArea);
+ awtComponent.paint(g);
+ }
+ finally
+ {
+ g.dispose();
+ paintArea = null;
+ }
+ }
}
// This method and its overrides are the only methods in the peers
// that should call awtComponent.update.
protected void updateComponent (PaintEvent event)
{
// Do not call Component.update if the component is not showing or
// if its bounds form a degenerate rectangle.
if (!awtComponent.isShowing()
|| (awtComponent.getWidth() < 1 || awtComponent.getHeight() < 1))
return;
- Graphics g = getGraphics();
-
- g.setClip(event.getUpdateRect());
-
- awtComponent.update(g);
-
- g.dispose();
+ synchronized (paintArea)
+ {
+ Graphics g = getGraphics();
+ try
+ {
+ g.setClip(paintArea);
+ awtComponent.update(g);
+ }
+ finally
+ {
+ g.dispose();
+ paintArea = null;
+ }
+ }
}
public boolean isFocusTraversable ()
{
return true;
}
public Dimension minimumSize ()
{
int dim[] = new int[2];
gtkWidgetGetPreferredDimensions (dim);
return new Dimension (dim[0], dim[1]);
}
@@ -742,31 +761,38 @@
return comp == awtComponent;
}
public boolean isObscured ()
{
return false;
}
public boolean canDetermineObscurity ()
{
return false;
}
public void coalescePaintEvent (PaintEvent e)
{
-
+ synchronized (this)
+ {
+ Rectangle newRect = e.getUpdateRect();
+ if (paintArea == null)
+ paintArea = newRect;
+ else
+ Rectangle.union(paintArea, newRect, paintArea);
+ }
}
public void updateCursorImmediately ()
{
if (awtComponent.getCursor() != null)
setCursor(awtComponent.getCursor());
}
public boolean handlesWheelScrolling ()
{
return false;
}
// Convenience method to create a new volatile image on the screen
// on which this component is displayed.