Here comes my JViewport fixup:

1. All the painting code is moved from BasicViewportUI to JViewport as
specified.
2. The backingstore painting is implemented correctly and set as default
method. This should give great performance improvement when scrolling
(given that the scrolled componenent is reasonably clever, which all the
text components are not unfortunatly)
3. The missing field and method in JViewport are added/implemented.
4. The blitting method in JViewport is not implemented. The reason is
described in the source code. However, AFAICS the backingstore method as
implemented by should be comparable fast and is therefore used as a
fallback.

I very much hope that this solves the ugly clipping problems that have
been seen in scrollpanes and such. After all my tests it does, if not,
please ping me.

2005-09-22  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JViewport.java
        (JViewport): Initialize cached fields for blitting.
        (setViewPosition): Repaint viewport.
        (revalidate): Set damaged flag. Used for blitting.
        (reshape): Likewise.
        (paint): New method. Decide which paint method to use.
        (computeBlit): New method. Computes the parameters for blitting.
        (paintSimple): New method. Performs painting in simple mode.
        (paintBackingStore): New method. Performs painting in backingstore
        mode.
        (paintBlit): New method. Performs painting in blit mode. For
        now this falls back to backing store mode.
        * javax/swing/plaf/basic/BasicViewportUI.java
        (ChangeListener): Removed inner class.
        (installDefaults): Load default for background color. Changed
        signature to protected.
        (installListeners): Removed method.
        (uninstallDefaults): Changed signature to protected.
        (installUI): Call installDefaults().
        (uninstallUI): Call uninstallDefaults().
        (getPreferredSize): Removed.
        (paint): Removed.
        (paintSimple): Removed.
        (paintBackingStore): Removed.

/Roman
Index: javax/swing/JViewport.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JViewport.java,v
retrieving revision 1.27
diff -u -r1.27 JViewport.java
--- javax/swing/JViewport.java	12 Sep 2005 20:59:15 -0000	1.27
+++ javax/swing/JViewport.java	22 Sep 2005 14:34:42 -0000
@@ -41,6 +41,7 @@
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Graphics;
+import java.awt.Image;
 import java.awt.Insets;
 import java.awt.LayoutManager;
 import java.awt.Point;
@@ -181,19 +182,37 @@
     }
   }
 
-  private static final long serialVersionUID = -6925142919680527970L;
-  
   public static final int SIMPLE_SCROLL_MODE = 0;
   public static final int BLIT_SCROLL_MODE = 1;
   public static final int BACKINGSTORE_SCROLL_MODE = 2;
 
+  private static final long serialVersionUID = -6925142919680527970L;
+  
+  protected boolean scrollUnderway;
+  protected boolean isViewSizeSet;
+
+  /**
+   * This flag indicates whether we use a backing store for drawing.
+   *
+   * @deprecated since JDK 1.3
+   */
+  protected boolean backingStore;
+
+  /**
+   * The backingstore image used for the backingstore and blit scroll methods.
+   */
+  protected Image backingStoreImage;
+
+  /**
+   * The position at which the view has been drawn the last time. This is used
+   * to determine the bittable area.
+   */
+  protected Point lastPaintPosition;
+
   ChangeEvent changeEvent = new ChangeEvent(this);
 
   int scrollMode;
 
-  protected boolean scrollUnderway;
-  protected boolean isViewSizeSet;
-
   /** 
    * The width and height of the Viewport's area in terms of view
    * coordinates.  Typically this will be the same as the width and height
@@ -201,27 +220,45 @@
    * width and height, which it may do, for example if it magnifies or
    * rotates its view.
    *
-   * @see #toViewCoordinates
+   * @see #toViewCoordinates(Dimension)
    */
   Dimension extentSize;
 
   /**
    * The width and height of the view in its own coordinate space.
    */
-
   Dimension viewSize;
 
-  Point lastPaintPosition;
-
   /**
    * The ViewListener instance.
    */
   ViewListener viewListener;
 
   /**
-   * The accessible context of this <code>JViewport</code>.
+   * Stores the location from where to blit. This is a cached Point object used
+   * in blitting calculations.
    */
-  AccessibleContext accessibleContext;
+  Point cachedBlitFrom;
+
+  /**
+   * Stores the location where to blit to. This is a cached Point object used
+   * in blitting calculations.
+   */
+  Point cachedBlitTo;
+
+  /**
+   * Stores the width of the blitted area. This is a cached Dimension object
+   * used in blitting calculations.
+   */
+  Dimension cachedBlitSize;
+
+  /**
+   * Stores the bounds of the area that needs to be repainted. This is a cached
+   * Rectangle object used in blitting calculations. 
+   */
+  Rectangle cachedBlitPaint;
+
+  boolean damaged = true;
 
   public JViewport()
   {
@@ -229,6 +266,11 @@
     setScrollMode(BLIT_SCROLL_MODE);
     setLayout(createLayoutManager());
     updateUI();
+    lastPaintPosition = new Point();
+    cachedBlitFrom = new Point();
+    cachedBlitTo = new Point();
+    cachedBlitSize = new Dimension();
+    cachedBlitPaint = new Rectangle();
   }
 
   public Dimension getExtentSize()
@@ -322,6 +364,7 @@
         isViewSizeSet = false;
         fireStateChanged();
       }
+    repaint();
   }
 
   public Rectangle getViewRect()
@@ -390,12 +433,14 @@
 
   public void revalidate()
   {
+    damaged = true;
     fireStateChanged();
     super.revalidate();
   }
 
   public void reshape(int x, int y, int w, int h)
   {
+    damaged = true;
     boolean changed = 
       (x != getX()) 
       || (y != getY()) 
@@ -406,14 +451,6 @@
       fireStateChanged();
   }
     
-  protected void addImpl(Component comp, Object constraints, int index)
-  {
-    if (getComponentCount() > 0)
-      remove(getComponents()[0]);
-    
-    super.addImpl(comp, constraints, index);
-  }
-
   public final Insets getInsets() 
   {
     return new Insets(0,0,0,0);
@@ -437,7 +474,36 @@
 
   public void paint(Graphics g)
   {
-    paintComponent(g);
+    Component view = getView();
+
+    if (view == null)
+      return;
+
+    Point pos = getViewPosition();
+    Rectangle viewBounds = view.getBounds();
+    Rectangle portBounds = getBounds();
+
+    if (viewBounds.width == 0 
+        || viewBounds.height == 0
+        || portBounds.width == 0
+        || portBounds.height == 0)
+      return;
+
+    switch (getScrollMode())
+      {
+
+      case JViewport.BACKINGSTORE_SCROLL_MODE:
+        paintBackingStore(g);
+        break;
+      case JViewport.BLIT_SCROLL_MODE:
+        paintBlit(g);
+        break;
+      case JViewport.SIMPLE_SCROLL_MODE:
+      default:
+        paintSimple(g);
+        break;
+      }
+    damaged = false;
   }
 
   public void addChangeListener(ChangeListener listener)
@@ -455,13 +521,6 @@
     return (ChangeListener[]) getListeners(ChangeListener.class);
   }
 
-  protected void fireStateChanged()
-  {
-    ChangeListener[] listeners = getChangeListeners();
-    for (int i = 0; i < listeners.length; ++i)
-      listeners[i].stateChanged(changeEvent);
-  }
-
   /**
    * This method returns the String ID of the UI class of  Separator.
    *
@@ -507,28 +566,6 @@
   }
 
   /**
-   * Creates a [EMAIL PROTECTED] ViewListener} that is supposed to listen for
-   * size changes on the view component.
-   *
-   * @return a ViewListener instance
-   */
-  protected ViewListener createViewListener()
-  {
-    return new ViewListener();
-  }
-
-  /**
-   * Creates the LayoutManager that is used for this viewport. Override
-   * this method if you want to use a custom LayoutManager.
-   *
-   * @return a LayoutManager to use for this viewport
-   */
-  protected LayoutManager createLayoutManager()
-  {
-    return new ViewportLayout();
-  }
-
-  /**
    * Scrolls the view so that contentRect becomes visible.
    *
    * @param contentRect the rectangle to make visible within the view
@@ -580,5 +617,256 @@
     if (accessibleContext == null)
       accessibleContext = new AccessibleJViewport();
     return accessibleContext;
+  }
+
+  protected void addImpl(Component comp, Object constraints, int index)
+  {
+    if (getComponentCount() > 0)
+      remove(getComponents()[0]);
+    
+    super.addImpl(comp, constraints, index);
+  }
+
+  protected void fireStateChanged()
+  {
+    ChangeListener[] listeners = getChangeListeners();
+    for (int i = 0; i < listeners.length; ++i)
+      listeners[i].stateChanged(changeEvent);
+  }
+
+  /**
+   * Creates a [EMAIL PROTECTED] ViewListener} that is supposed to listen for
+   * size changes on the view component.
+   *
+   * @return a ViewListener instance
+   */
+  protected ViewListener createViewListener()
+  {
+    return new ViewListener();
+  }
+
+  /**
+   * Creates the LayoutManager that is used for this viewport. Override
+   * this method if you want to use a custom LayoutManager.
+   *
+   * @return a LayoutManager to use for this viewport
+   */
+  protected LayoutManager createLayoutManager()
+  {
+    return new ViewportLayout();
+  }
+
+  /**
+   * Computes the parameters for the blitting scroll method. <code>dx</code>
+   * and <code>dy</code> specifiy the X and Y offset by which the viewport
+   * is scrolled. All other arguments are output parameters and are filled by
+   * this method.
+   *
+   * <code>blitFrom</code> holds the position of the blit rectangle in the
+   * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
+   * is copied to.
+   *
+   * <code>blitSize</code> holds the size of the blit area and
+   * <code>blitPaint</code> is the area of the view that needs to be painted.
+   *
+   * This method returns <code>true</code> if blitting is possible and
+   * <code>false</code> if the viewport has to be repainted completetly without
+   * blitting.
+   *
+   * @param dx the horizontal delta
+   * @param dy the vertical delta
+   * @param blitFrom the position from where to blit; set by this method
+   * @param blitTo the position where to blit area is copied to; set by this
+   *        method
+   * @param blitSize the size of the blitted area; set by this method
+   * @param blitPaint the area that needs repainting; set by this method
+   *
+   * @return <code>true</code> if blitting is possible,
+   *         <code>false</code> otherwise
+   */
+  protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo,
+                                Dimension blitSize, Rectangle blitPaint)
+  {
+    if ((dx != 0 && dy != 0) || damaged)
+      // We cannot blit if the viewport is scrolled in both directions at
+      // once.
+      return false;
+
+    Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds());
+
+    // Compute the blitFrom and blitTo parameters.
+    blitFrom.x = portBounds.x;
+    blitFrom.y = portBounds.y;
+    blitTo.x = portBounds.x;
+    blitTo.y = portBounds.y;
+
+    if (dy > 0)
+      {
+        blitFrom.y = portBounds.y + dy;
+      }
+    else if (dy < 0)
+      {
+        blitTo.y = portBounds.y - dy;
+      }
+    else if (dx > 0)
+      {
+        blitFrom.x = portBounds.x + dx;
+      }
+    else if (dx < 0)
+      {
+        blitTo.x = portBounds.x - dx;
+      }
+
+    // Compute size of the blit area.
+    if (dx != 0)
+      {
+        blitSize.width = portBounds.width - Math.abs(dx);
+        blitSize.height = portBounds.height;
+      }
+    else if (dy != 0)
+      {
+        blitSize.width = portBounds.width;
+        blitSize.height = portBounds.height - Math.abs(dy);
+      }
+
+    // Compute the blitPaint parameter.
+    blitPaint.setBounds(portBounds);
+    if (dy > 0)
+      {
+        blitPaint.y = portBounds.y + portBounds.height - dy;
+        blitPaint.height = dy;
+      }
+    else if (dy < 0)
+      {
+        blitPaint.height = -dy;
+      }
+    if (dx > 0)
+      {
+        blitPaint.x = portBounds.x + portBounds.width - dx;
+        blitPaint.width = dx;
+      }
+    else if (dx < 0)
+      {
+        blitPaint.width = -dx;
+      }
+
+    return true;
+  }
+
+  /**
+   * Paints the viewport in case we have a scrollmode of
+   * [EMAIL PROTECTED] #SIMPLE_SCROLL_MODE}.
+   *
+   * This simply paints the view directly on the surface of the viewport.
+   *
+   * @param g the graphics context to use
+   */
+  void paintSimple(Graphics g)
+  {
+    Point pos = getViewPosition();
+    Component view = getView();
+    boolean translated = false;
+    try
+      {
+        g.translate(-pos.x, -pos.y);
+        translated = true;
+        view.paint(g);
+      } 
+    finally 
+      {
+        if (translated)
+          g.translate (pos.x, pos.y);
+      }
+  }
+
+  /**
+   * Paints the viewport in case we have a scroll mode of
+   * [EMAIL PROTECTED] #BACKINGSTORE_SCROLL_MODE}.
+   *
+   * This method uses a backing store image to paint the view to, which is then
+   * subsequently painted on the screen. This should make scrolling more
+   * smooth.
+   *
+   * @param g the graphics context to use
+   */
+  void paintBackingStore(Graphics g)
+  {
+    // If we have no backing store image yet or the size of the component has
+    // changed, we need to rebuild the backing store.
+    if (backingStoreImage == null || damaged)
+      {
+        backingStoreImage = createImage(getWidth(), getHeight());
+        Graphics g2 = backingStoreImage.getGraphics();
+        paintSimple(g2);
+        g2.dispose();
+      }
+    // Otherwise we can perform the blitting on the backing store image:
+    // First we move the part that remains visible after scrolling, then
+    // we only need to paint the bit that becomes newly visible.
+    else
+      {
+        Graphics g2 = backingStoreImage.getGraphics();
+        Point viewPosition = getViewPosition();
+        int dx = viewPosition.x - lastPaintPosition.x;
+        int dy = viewPosition.y - lastPaintPosition.y;
+        boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
+                                      cachedBlitSize, cachedBlitPaint);
+        if (canBlit)
+          {
+            // Copy the part that remains visible during scrolling.
+            g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
+                        cachedBlitSize.width, cachedBlitSize.height,
+                        cachedBlitTo.x - cachedBlitFrom.x,
+                        cachedBlitTo.y - cachedBlitFrom.y);
+            // Now paint the part that becomes newly visible.
+            g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
+                       cachedBlitPaint.width, cachedBlitPaint.height);
+            paintSimple(g2);
+          }
+        // If blitting is not possible for some reason, fall back to repainting
+        // everything.
+        else
+          {
+            paintSimple(g2);
+          }
+        g2.dispose();
+      }
+    // Actually draw the backingstore image to the graphics context.
+    g.drawImage(backingStoreImage, 0, 0, this);
+    // Update the lastPaintPosition so that we know what is already drawn when
+    // we paint the next time.
+    lastPaintPosition.setLocation(getViewPosition());
+  }
+
+  /**
+   * Paints the viewport in case we have a scrollmode of
+   * [EMAIL PROTECTED] #BLIT_SCROLL_MODE}.
+   *
+   * This paints the viewport using a backingstore and a blitting algorithm.
+   * Only the newly exposed area of the view is painted from the view painting
+   * methods, the remainder is copied from the backing store.
+   *
+   * @param g the graphics context to use
+   */
+  void paintBlit(Graphics g)
+  {
+    // We cannot perform blitted painting as it is described in Sun's API docs.
+    // There it is suggested that this painting method should blit directly
+    // on the parent window's surface. This is not possible because when using
+    // Swing's double buffering (at least our implementation), it would
+    // immediatly be painted when the buffer is painted on the screen. For this
+    // to work we would need a kind of hole in the buffer image. And honestly
+    // I find this method not very elegant.
+    // The alternative, blitting directly on the buffer image, is also not
+    // possible because the buffer image gets cleared everytime when an opaque
+    // parent component is drawn on it.
+
+    // What we do instead is falling back to the backing store approach which
+    // is in fact a mixed blitting/backing store approach where the blitting
+    // is performed on the backing store image and this is then drawn to the
+    // graphics context. This is very robust and works independent of the
+    // painting mechanism that is used by Swing. And it should have comparable
+    // performance characteristics as the blitting method.
+    paintBackingStore(g);
   }
 }
Index: javax/swing/plaf/basic/BasicViewportUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicViewportUI.java,v
retrieving revision 1.17
diff -u -r1.17 BasicViewportUI.java
--- javax/swing/plaf/basic/BasicViewportUI.java	11 Aug 2005 19:59:09 -0000	1.17
+++ javax/swing/plaf/basic/BasicViewportUI.java	22 Sep 2005 14:34:42 -0000
@@ -38,61 +38,20 @@
 
 package javax.swing.plaf.basic;
 
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.image.ImageObserver;
-
 import javax.swing.JComponent;
-import javax.swing.JViewport;
-import javax.swing.ViewportLayout;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
+import javax.swing.UIManager;
 import javax.swing.plaf.ComponentUI;
 import javax.swing.plaf.ViewportUI;
 
 public class BasicViewportUI extends ViewportUI 
 {
-
-  ChangeListener changeListener;
-  Image backingStoreImage;
-  int backingStoreWidth = -1;
-  int backingStoreHeight = -1;
-  
-  class ChangeHandler implements ChangeListener
+  protected void installDefaults(JComponent c)
   {
-    public void stateChanged(ChangeEvent event)
-    {
-      JViewport v = (JViewport) event.getSource();
-      v.repaint();
-    }
-  }
-
-  void installDefaults(JComponent c)
-  {    
     c.setOpaque(true);
+    c.setBackground(UIManager.getColor("Viewport.background"));
   }
-
-  void uninstallDefaults(JComponent c)
-  {
-  }
-
-  void installListeners(JComponent c)
+  protected void uninstallDefaults(JComponent c)
   {
-    ((JViewport)c).addChangeListener(changeListener);
-  }
-
-  void uninstallListeners(JComponent c)
-  {
-    ((JViewport)c).removeChangeListener(changeListener);
-  }
-
-  public BasicViewportUI()
-  {
-    changeListener = new ChangeHandler();
   }
 
   public static ComponentUI createUI(JComponent c)
@@ -103,132 +62,12 @@
   public void installUI(JComponent c) 
   {
     super.installUI(c);
-    installListeners(c);
+    installDefaults(c);
   }
 
   public void uninstallUI(JComponent c) 
   {
-    uninstallListeners(c);
-  }
-    
-
-  public Dimension getPreferredSize(JComponent c) 
-  {
-    // let the ViewportLayout decide
-    return null;
-  }
-
-  public void paint(Graphics g, JComponent c)
-  {
-    JViewport port = (JViewport)c;
-    Component view = port.getView();
-
-    if (view == null)
-      return;
-
-    Point pos = port.getViewPosition();
-    Rectangle viewBounds = view.getBounds();
-    Rectangle portBounds = port.getBounds();
-
-    if (viewBounds.width == 0 
-        || viewBounds.height == 0
-        || portBounds.width == 0
-        || portBounds.height == 0)
-      return;
-
-    switch (port.getScrollMode())
-      {
-
-      case JViewport.BACKINGSTORE_SCROLL_MODE:
-        paintBackingStore(g, port, view, pos, viewBounds, portBounds);
-        break;
-
-      case JViewport.BLIT_SCROLL_MODE:
-        // FIXME: implement separate blit mode
-
-      case JViewport.SIMPLE_SCROLL_MODE:
-      default:
-        paintSimple(g, port, view, pos, viewBounds, portBounds);
-        break;
-      }
-  }
-
-  private void paintSimple(Graphics g, 
-                           JViewport v, 
-                           Component view, 
-                           Point pos, 
-                           Rectangle viewBounds, 
-                           Rectangle portBounds)
-  {
-    Rectangle oldClip = g.getClipBounds();
-    g.setClip(new Rectangle(0, 0, portBounds.width, portBounds.height));
-    g.translate (-pos.x, -pos.y);
-    try
-      {
-        view.paint(g);
-      } 
-    finally 
-      {
-        g.translate (pos.x, pos.y);
-        g.setClip (oldClip);
-      }
-  }
-
-  private void paintBackingStore(Graphics g, 
-                                 JViewport v, 
-                                 Component view, 
-                                 Point pos, 
-                                 Rectangle viewBounds, 
-                                 Rectangle portBounds)
-  {      
-    if (backingStoreImage == null 
-        || backingStoreWidth != viewBounds.width
-        || backingStoreHeight != viewBounds.height)
-      {
-        backingStoreImage = v.createImage(viewBounds.width, viewBounds.height);
-        backingStoreWidth = viewBounds.width;
-        backingStoreHeight = viewBounds.height;
-      }
-
-    Graphics g2 = backingStoreImage.getGraphics();
-
-    if (v.getBackground() != null)
-      {
-        // fill the backing store background
-        java.awt.Color save = g2.getColor();
-        g2.setColor(v.getBackground());
-        g2.fillRect (0, 0, backingStoreWidth, backingStoreHeight);
-        g2.setColor(save);
-
-        // fill the viewport background
-        save = g.getColor();
-        g.setColor(v.getBackground());
-        g.fillRect (0, 0, portBounds.width, portBounds.height);
-        g.setColor(save);
-
-      }
-    else
-      {
-        // clear the backing store background
-        g2.clearRect(0, 0, backingStoreWidth, backingStoreHeight);
-
-        // clear the viewport background
-        g.clearRect(0, 0, portBounds.width, portBounds.height);
-      }
-
-    g2.setClip(g.getClipBounds());
-    g2.translate(-pos.x, -pos.y);
-    try 
-      {
-        view.paint(g2);
-      }
-    finally
-      {
-        g2.translate(pos.x, pos.y);
-      }
-    g2 = null;
-    g.drawImage(backingStoreImage, 
-                0, 0, 
-                (ImageObserver)null);
+    super.uninstallUI(c);
+    uninstallDefaults(c);
   }
 }
_______________________________________________
Classpath-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to