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.

Reply via email to