Hi list, hi Audrius,

I looked a little over the recent JComponent/RepaintManager changes. I
must say they are pretty good overall. I did a little cleanup on this:

- I removed the hack in JComponent that marked a child component clean
when it's been painted as part of the painting of the parent.
- I removed some cruft code in JComponent/RepaintManager that's been
left over from previous implementations.
- I slightly improved Audrius approach (which is indeed really good,
despite what my probably irritating previous mail exchange with Audrius
might suggest). It now uses a HashSet for storing the actual repaint
roots, which allows for slightly quicker (I hope) lookup of the
contains() thingy. Also, the search for the repaint root now only does
one translation of coordinates (it searches for the final root of the
component and then does one translation to this root)
- Fixed a small bug in markCompletelyDirty, which added the wrong dirty
region to the dirtyComponents HashMap.

Ok to commit? Audrius? (btw, while this might look like another rewrite
of the paintDirtyRegions stuff, it is really only an adaption of your
idea, with less translations and iterations, correct me if I'm wrong).

2006-05-18  Roman Kennke <[EMAIL PROTECTED]>

        * javax/swing/JComponent.java
        (isCompletelyDirty): Removed.
        (paint): Don't mark children as clean, this is no longer
necessary.
        (findOverlapFreeParent): Don't stop at Viewports, this breaks
        painting when something overlaps the viewport (like a
popup/menu).
        * javax/swing/RepaintManager.java
        (currentRepaintManagers): Made package private to avoid accessor
        methods.
        (dirtyComponents): Made private.
        (dirtyComponentsWork): Made private.
        (markCompletelyDirty): Fixed bounds of dirtyrect to be
        component-local not parent-local. Do not set flag in JComponent.
        (markCompletelyClean): Don't set JComponent flag.
        (isCompletelyDirty): Rewritten to return true when the complete
        component is marked dirty.
        (paintDirtyRegions): Improved parent-merging so that the
merged-in
        components don't get painted too. 'Outsourced' the compilation
        of the repaint root components.
        (compileRepaintRoots): New helper method.

-- 
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.120
diff -u -1 -0 -r1.120 JComponent.java
--- javax/swing/JComponent.java	15 May 2006 17:03:05 -0000	1.120
+++ javax/swing/JComponent.java	18 May 2006 13:34:09 -0000
@@ -753,27 +753,20 @@
 
   /**
    * Constant used to indicate that an action should be performed only when 
    * the component is in the window which has focus.
    *
    * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
    */
   public static final int WHEN_IN_FOCUSED_WINDOW = 2;
 
   /**
-   * Indicates if this component is completely dirty or not. This is used
-   * by the RepaintManager's
-   * [EMAIL PROTECTED] RepaintManager#isCompletelyDirty(JComponent)} method.
-   */
-  boolean isCompletelyDirty = false;
-
-  /**
    * Indicates if the opaque property has been set by a client program or by
    * the UI.
    *
    * @see #setUIProperty(String, Object)
    * @see LookAndFeel#installProperty(JComponent, String, Object)
    */
   private boolean clientOpaqueSet = false;
 
   /**
    * Indicates if the autoscrolls property has been set by a client program or
@@ -1756,25 +1749,20 @@
         if (dragBuffer != null && dragBufferInitialized)
           {
             g.drawImage(dragBuffer, 0, 0, this);
           }
         else
           {
             Graphics g2 = getComponentGraphics(g);
             paintComponent(g2);
             paintBorder(g2);
             paintChildren(g2);
-            Rectangle clip = g2.getClipBounds();
-            if (clip == null
-                || (clip.x == 0 && clip.y == 0 && clip.width == getWidth()
-                && clip.height == getHeight()))
-              RepaintManager.currentManager(this).markCompletelyClean(this);
           }
       }
   }
 
   /**
    * Initializes the drag buffer by creating a new image and painting this
    * component into it.
    */
   private void initializeDragBuffer()
   {
@@ -3549,22 +3537,21 @@
    *
    * @return the paint root, or <code>null</code> if no paint is necessary
    */
   private Component findOverlapFreeParent(Rectangle clip)
   {
     Rectangle currentClip = clip;
     Component found = this;
     Container parent = this; 
     // Path up is stopped at viewports, allowing to use viewport
     // painting optimizations.
-    while (parent != null && !(parent instanceof Window) 
-        && !(parent instanceof JViewport))
+    while (parent != null && !(parent instanceof Window))
       {
         Container newParent = parent.getParent();
         if (newParent == null || newParent instanceof Window)
           break;
         // If the parent is optimizedDrawingEnabled, then its children are
         // tiled and cannot have an overlapping child. Go directly to next
         // parent.
         if ((newParent instanceof JComponent
             && ((JComponent) newParent).isOptimizedDrawingEnabled()))
           
Index: javax/swing/RepaintManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/RepaintManager.java,v
retrieving revision 1.35
diff -u -1 -0 -r1.35 RepaintManager.java
--- javax/swing/RepaintManager.java	15 May 2006 19:43:19 -0000	1.35
+++ javax/swing/RepaintManager.java	18 May 2006 13:34:09 -0000
@@ -40,22 +40,20 @@
 
 import java.applet.Applet;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Image;
 import java.awt.Rectangle;
 import java.awt.Window;
 import java.awt.image.VolatileImage;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 /**
  * <p>The repaint manager holds a set of dirty regions, invalid components,
  * and a double buffer surface.  The dirty regions and invalid components
@@ -70,21 +68,21 @@
  *
  * @author Roman Kennke ([EMAIL PROTECTED])
  * @author Graydon Hoare ([EMAIL PROTECTED])
  * @author Audrius Meskauskas ([EMAIL PROTECTED])
  */
 public class RepaintManager
 {
   /**
    * The current repaint managers, indexed by their ThreadGroups.
    */
-  private static WeakHashMap currentRepaintManagers;
+  static WeakHashMap currentRepaintManagers;
 
   /**
    * A rectangle object to be reused in damaged regions calculation.
    */
   private static Rectangle rectCache = new Rectangle();
 
   /**
    * <p>A helper class which is placed into the system event queue at
    * various times in order to facilitate repainting and layout. There is
    * typically only one of these objects active at any time. When the
@@ -144,27 +142,27 @@
    *
    * This is package private to avoid a synthetic accessor method in inner
    * class.
    *
    * @see #addDirtyRegion
    * @see #getDirtyRegion
    * @see #isCompletelyDirty
    * @see #markCompletelyClean
    * @see #markCompletelyDirty
    */
-  HashMap dirtyComponents;
+  private HashMap dirtyComponents;
 
   /**
    * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary
    * locking.
    */
-  HashMap dirtyComponentsWork;
+  private HashMap dirtyComponentsWork;
 
   /**
    * A single, shared instance of the helper class. Any methods which mark
    * components as invalid or dirty eventually activate this instance. It
    * is added to the event queue if it is not already active, otherwise
    * reused.
    *
    * @see #addDirtyRegion
    * @see #addInvalidComponent
    */
@@ -440,63 +438,65 @@
    *
    * @see #dirtyComponents
    * @see #addDirtyRegion
    * @see #getDirtyRegion
    * @see #isCompletelyDirty
    * @see #markCompletelyClean
    */
   public void markCompletelyDirty(JComponent component)
   {
     Rectangle r = component.getBounds();
-    addDirtyRegion(component, r.x, r.y, r.width, r.height);
-    component.isCompletelyDirty = true;
+    addDirtyRegion(component, 0, 0, r.width, r.height);
   }
 
   /**
    * Remove all dirty regions for a specified component
    *
    * @param component The component to mark as clean
    *
    * @see #dirtyComponents
    * @see #addDirtyRegion
    * @see #getDirtyRegion
    * @see #isCompletelyDirty
    * @see #markCompletelyDirty
    */
   public void markCompletelyClean(JComponent component)
   {
     synchronized (dirtyComponents)
       {
         dirtyComponents.remove(component);
       }
-    component.isCompletelyDirty = false;
   }
 
   /**
    * Return <code>true</code> if the specified component is completely
    * contained within its dirty region, otherwise <code>false</code>
    *
    * @param component The component to check for complete dirtyness
    *
    * @return Whether the component is completely dirty
    *
    * @see #dirtyComponents
    * @see #addDirtyRegion
    * @see #getDirtyRegion
    * @see #isCompletelyDirty
    * @see #markCompletelyClean
    */
   public boolean isCompletelyDirty(JComponent component)
   {
-    if (! dirtyComponents.containsKey(component))
-      return false;
-    return component.isCompletelyDirty;
+    boolean retVal = false;
+    if (dirtyComponents.containsKey(component))
+      {
+        Rectangle dirtyRegion = (Rectangle) dirtyComponents.get(component);
+        retVal = dirtyRegion.equals(SwingUtilities.getLocalBounds(component));
+      }
+    return retVal;
   }
 
   /**
    * Validate all components which have been marked invalid in the [EMAIL PROTECTED]
    * #invalidComponents} vector.
    */
   public void validateInvalidComponents()
   {
     // We don't use an iterator here because that would fail when there are
     // components invalidated during the validation of others, which happens
@@ -526,91 +526,89 @@
       return;
 
     // Swap dirtyRegions with dirtyRegionsWork to avoid locking.
     synchronized (dirtyComponents)
       {
         HashMap swap = dirtyComponents;
         dirtyComponents = dirtyComponentsWork;
         dirtyComponentsWork = swap;
       }
 
-    Object[] components = dirtyComponentsWork.keySet().toArray();
-    boolean someRemoved;
-    do
-      {
-        someRemoved = false;
-        // Where possible, do not repaint the component, extending the
-        // parent repaint region instead.
-        Components: for (int i = 0; i < components.length; i++)
-          if (components[i] != null)
-            {
-              Component c = (Component) components[i];
-              Component p = c.getParent();
-              int x = c.getX();
-              int y = c.getY();
-
-              while (p instanceof JComponent)
-                if (contains(components, p))
-                  {
-                    // The parent of this component is already marked for
-                    // repainting.
-                    // We will not repaint this component.
-                    components[i] = null;
-                    someRemoved = true;
-                    // We will repaint the parent instead.
-                    Rectangle prect = (Rectangle) dirtyComponentsWork.get(p);
-                    Rectangle crect = (Rectangle) dirtyComponentsWork.get(c);
-                    crect.translate(x, y);
-                    prect.add(crect);
-                    continue Components;
-                  }
-                else
-                  {
-                    x += p.getX();
-                    y += p.getY();
-                    p = p.getParent();
-                  }
-            }
+    // Compile a set of repaint roots.
+    HashSet repaintRoots = new HashSet();
+    Set components = dirtyComponentsWork.keySet();
+    for (Iterator i = components.iterator(); i.hasNext();)
+      {
+        JComponent dirty = (JComponent) i.next();
+        compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
       }
-    while (someRemoved);
 
     repaintUnderway = true;
-    for (int i = 0; i < components.length; i++)
+    for (Iterator i = repaintRoots.iterator(); i.hasNext();)
       {
-        JComponent comp = (JComponent) components[i];
-        if (comp != null)
-          {
-            // If a component is marked completely clean in the meantime, then
-            // skip it.
-            Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
-            if (damaged == null || damaged.isEmpty())
-              continue;
-            comp.paintImmediately(damaged);
-          }
+        JComponent comp = (JComponent) i.next();
+        Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
+        if (damaged == null || damaged.isEmpty())
+          continue;
+        comp.paintImmediately(damaged);
       }
-
     dirtyComponentsWork.clear();
     repaintUnderway = false;
     commitRemainingBuffers();
   }
   
   /**
-   * The simple search of the object inside the array.
-   */
-  private static boolean contains(Object [] array, Object x)
-  {
-    for (int i = 0; i < array.length; i++)
-      {
-        if (array [i] == x)
-          return true;
+   * Compiles a list of components that really get repainted. This is called
+   * once for each component in the dirtyComponents HashMap, each time with
+   * another <code>dirty</code> parameter. This searches up the component
+   * hierarchy of <code>dirty</code> to find the highest parent that is also
+   * marked dirty and merges the dirty regions.
+   *
+   * @param dirtyRegions the dirty regions 
+   * @param dirty the component for which to find the repaint root
+   * @param roots the list to which new repaint roots get appended
+   */
+  private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty,
+                                   HashSet roots)
+  {
+    Component current = dirty;
+    Component root = dirty;
+
+    // Search the highest component that is also marked dirty.
+    Component parent;
+    while (true)
+      {
+        parent = current.getParent();
+        if (parent == null || !(parent instanceof JComponent))
+          break;
+
+        current = parent;
+        // We can skip to the next up when this parent is not dirty.
+        if (dirtyRegions.containsKey(parent))
+          {
+            root = current;
+          }
       }
-    return false;
+
+    // Merge the rectangles of the root and the requested component if
+    // the are different.
+    if (root != dirty)
+      {
+        Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty);
+        dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root);
+        Rectangle rootRect = (Rectangle) dirtyRegions.get(root);
+        SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width,
+                                    dirtyRect.height, rootRect);
+      }
+
+    // Adds the root to the roots set.
+    roots.add(root);
   }
 
   /**
    * Get an offscreen buffer for painting a component's image. This image
    * may be smaller than the proposed dimensions, depending on the value of
    * the [EMAIL PROTECTED] #doubleBufferMaximumSize} property.
    *
    * @param component The component to return an offscreen buffer for
    * @param proposedWidth The proposed width of the offscreen buffer
    * @param proposedHeight The proposed height of the offscreen buffer

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

Reply via email to