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 JViewport
.
+ * 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 address@hidden 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 address@hidden 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. dx
+ * and dy
specifiy the X and Y offset by which the viewport
+ * is scrolled. All other arguments are output parameters and are filled by
+ * this method.
+ *
+ * blitFrom
holds the position of the blit rectangle in the
+ * viewport rectangle before scrolling, blitTo
where the blitArea
+ * is copied to.
+ *
+ * blitSize
holds the size of the blit area and
+ * blitPaint
is the area of the view that needs to be painted.
+ *
+ * This method returns true
if blitting is possible and
+ * false
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 true
if blitting is possible,
+ * false
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
+ * address@hidden #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
+ * address@hidden #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
+ * address@hidden #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);
}
}