Based on yesterdays offscreen buffer work, I am now able to implement a
real and efficient blitting algorithm for JViewport. This does directly
blit on the offscreen buffer now (alternativly on the screen if double
buffering is disabled, untested though) instead of blitting inside a
separated backbuffer, and then blitting this to the Swing offscreen
buffer and then to the screen like it is done in the previous
doublebuffered JViewport. This not only increases scrolling performance
(one complete blitting operation saved), it also reduces memory
consumption of JViewports, because they don't need to manage their own
backbuffer anymore.

Note that I had to change the repaint() method from what is specified. I
don't think that this matters much, because it is overridden as some
kind of implementation specific detail anyway. It seems that Sun has a
completely different approach to JViewport blitting, but I don't fully
understand theirs. AFAICS they blit from the screen to the Swing
offscreen buffer somehow, but how that is supposed to work is beyond me
(VolatileImage?). Also, this approach seems difficult to me, since it is
a little hard to determine if a component has been obscured somehow (I
think they added a ComponentPeer method for that).

2006-02-14  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JViewport.java
        (isPaintRoot): New field.
        (repaint): Only call super here. Also added a comment regarding
        the diversion from the JDK.
        (paintBlit): Implemented real blitting.
        (paintImmediately2): New method. Overrides the same package
private
        method in JComponent.

/Roman
Index: javax/swing/JViewport.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JViewport.java,v
retrieving revision 1.41
diff -u -r1.41 JViewport.java
--- javax/swing/JViewport.java	27 Jan 2006 12:37:59 -0000	1.41
+++ javax/swing/JViewport.java	14 Feb 2006 22:13:04 -0000
@@ -252,6 +252,13 @@
   boolean sizeChanged = true;
 
   /**
+   * Indicates if this JViewport is the paint root or not. If it is not, then
+   * we may not assume that the offscreen buffer still has the right content
+   * because parent components may have cleared the background already.
+   */
+  private boolean isPaintRoot = false;
+
+  /**
    * Initializes the default setting for the scrollMode property.
    */
   static
@@ -635,11 +642,19 @@
    */
   public void repaint(long tm, int x, int y, int w, int h)
   {
-    Component parent = getParent();
-    if (parent != null)
-      {
-        parent.repaint(tm, x + getX(), y + getY(), w, h);
-      }
+//    Component parent = getParent();
+//    if (parent != null)
+//      parent.repaint(tm, x + getX(), y + getY(), w, h);
+//    else
+//      super.repaint(tm, x, y, w, h);
+
+    // The specs suggest to implement something like the above. This however
+    // breaks blit painting, because the parent (most likely a JScrollPane)
+    // clears the background of the offscreen area of the JViewport, thus
+    // destroying the pieces that we want to clip. So we simply call super here
+    // instead.
+    super.repaint(tm, x, y, w, h);
+    
   }
 
   protected void addImpl(Component comp, Object constraints, int index)
@@ -879,23 +894,43 @@
    */
   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);
+    // First we move the part that remains visible after scrolling, then
+    // we only need to paint the bit that becomes newly visible.
+    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 && isPaintRoot)
+      {
+        // Copy the part that remains visible during scrolling.
+        g.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.
+        g.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
+                  cachedBlitPaint.width, cachedBlitPaint.height);
+        paintSimple(g);
+      }
+    // If blitting is not possible for some reason, fall back to repainting
+    // everything.
+    else
+      {
+        paintSimple(g);
+      }
+    lastPaintPosition.setLocation(getViewPosition());
+  }
+
+  /**
+   * Overridden from JComponent to set the [EMAIL PROTECTED] #isPaintRoot} flag.
+   *
+   * @param r the rectangle to paint
+   */
+  void paintImmediately2(Rectangle r)
+  {
+    isPaintRoot = true;
+    super.paintImmediately2(r);
+    isPaintRoot = false;
   }
 }

Reply via email to