Hi,

I've done some optimization work to address this performance problem
reported by Norman Hendrich:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26486

The problem was that we were calling gdk_flush after every graphics
operation.  It is necessary to flush the graphics at least periodically,
since the AWT allows graphics calls from threads other than the GDK main
thread (See http://www.gtk.org/faq/#AEN492).However, Norman's test cases
demonstrate that flushing after every graphics operation negatively
affects performance.

The attached patch uses the lighter-weight XFlush in place of gdk_flush
and limits the flushing frequency to a maximum of 50 times per second.

I used GCJ for testing, for maximum performance of the non-native code.
I tested four different approaches:

1) the current approach: gdk_flush after every graphics operation
2) XFlush after every graphics operation
3) XFlush calls at a maximum of 50 times per second.
4) No calls to flush.

I ran two tests: Norman's FillRect benchmark and my AnimationApplet
which is an example of a Graphics-call-intense applet.  Approaches 1 and
2 had unacceptable performance on the FillRect benchmark but produced
smooth animation in AnimationApplet.  Approaches 3 and 4 had equal
performance on FillRect but 3 produced smooth animation in
AnimationApplet whereas 4 produced extremely choppy animation.  The
patch I'm committing implements approach 3.

I'm adding FillRect and AnimationApplet to the Swing and AWT demos
respectively so that we can keep an objective eye on painting
performance from now on.

The patch also re-arranges the paint- and update- handling parts of the
peers to make the design clearer.

Tom

2006-03-15  Thomas Fitzsimmons  <[EMAIL PROTECTED]>

        * examples/gnu/classpath/examples/awt/AnimationApplet.java: New
        example.
        * examples/gnu/classpath/examples/swing/FillRect.java: Likewise.
        * examples/gnu/classpath/examples/awt/Demo.java: Add
        AnimationApplet demo.
        * examples/gnu/classpath/examples/swing/Demo.java: Add FillRect
        demo.

        * gnu/java/awt/peer/gtk/GtkCanvasPeer.java (getGraphics): Remove
        method.
        (handleEvent): Likewise.
        * gnu/java/awt/peer/gtk/GtkComponentPeer.java (isInRepaint):
        Remove field.
        (beginNativeRepaint): Remove method.
        (endNativeRepaint): Likewise.
        (handleEvent): Call paintComponent and updateComponent.
        (paintComponent): New method.
        (updateComponent): Likewise.
        (repaint): Return early if width or height is less than one.
        (postExposeEvent): Remove isInRepaint reference.
        * gnu/java/awt/peer/gtk/GtkContainerPeer.java: (getGraphics):
        Remove method.
        * gnu/java/awt/peer/gtk/GtkDialogPeer.java (getGraphics): Inherit
        from GtkWindowPeer.
        (postMouseEvent): Likewise.
        (postExposeEvent): Likewise.
        * gnu/java/awt/peer/gtk/GtkFileDialogPeer.java (updateComponent):
        Override to do nothing.
        * gnu/java/awt/peer/gtk/GtkFramePeer.java (getGraphics): Inherit
        from GtkWindowPeer.
        (postMouseEvent): Likewise.
        (postExposeEvent): Likewise.
        * gnu/java/awt/peer/gtk/GtkPanelPeer.java (handleEvent): Inherit
        paint and update handling from GtkComponentPeer.
        (updateComponent): Override to call paintComponent.
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics.c: Replace
        gdk_flush calls with schedule_flush calls.
        (flush): New function.
        (schedule_flush): Likewise.

Index: gnu/java/awt/peer/gtk/GtkCanvasPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkCanvasPeer.java,v
retrieving revision 1.15
diff -u -r1.15 GtkCanvasPeer.java
--- gnu/java/awt/peer/gtk/GtkCanvasPeer.java	2 Jul 2005 20:32:12 -0000	1.15
+++ gnu/java/awt/peer/gtk/GtkCanvasPeer.java	16 Mar 2006 02:32:22 -0000
@@ -54,45 +54,8 @@
     super (c);
   }
 
-  public Graphics getGraphics ()
-  {
-    if (GtkToolkit.useGraphics2D ())
-      return new GdkGraphics2D (this);
-    else
-    return new GdkGraphics (this);
-  }
-
-  public void handleEvent (AWTEvent event)
-  {
-    int id = event.getID();
-      
-    switch (id)
-      {
-      case PaintEvent.PAINT:
-      case PaintEvent.UPDATE:
-	{
-	  try 
-	    {
-	      Graphics g = getGraphics ();
-	      g.setClip (((PaintEvent)event).getUpdateRect());
-		
-	      if (id == PaintEvent.PAINT)
-		awtComponent.paint (g);
-	      else
-		awtComponent.update (g);
-	      
-	      g.dispose ();
-	    } 
-	  catch (InternalError e)
-	    { 
-	      System.err.println (e);
-	    }
-	}
-	break;
-      }
-  }
-
-  /* Preferred size for a drawing widget is always what the user requested */
+  // Preferred size for a drawing widget is always what the user
+  // requested.
   public Dimension getPreferredSize ()
   {
     return awtComponent.getSize ();
Index: gnu/java/awt/peer/gtk/GtkComponentPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkComponentPeer.java,v
retrieving revision 1.107
diff -u -r1.107 GtkComponentPeer.java
--- gnu/java/awt/peer/gtk/GtkComponentPeer.java	3 Mar 2006 21:09:00 -0000	1.107
+++ gnu/java/awt/peer/gtk/GtkComponentPeer.java	16 Mar 2006 02:32:22 -0000
@@ -86,8 +86,6 @@
 
   Insets insets;
 
-  boolean isInRepaint;
-
   /* 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 */
@@ -176,16 +174,6 @@
       gtkWidgetSetParent (p);
   }
 
-  void beginNativeRepaint ()
-  {
-    isInRepaint = true;
-  }
-
-  void endNativeRepaint ()
-  {
-    isInRepaint = false;
-  }
-
   /*
    * Set the bounds of this peer's AWT Component based on dimensions
    * returned by the native windowing system.  Most Components impose
@@ -250,6 +238,8 @@
     return getToolkit().getFontMetrics(font);
   }
 
+  // getGraphics may be overridden by derived classes but it should
+  // never return null.
   public Graphics getGraphics ()
   {
     if (GtkToolkit.useGraphics2D ())
@@ -291,30 +281,10 @@
     switch (id)
       {
       case PaintEvent.PAINT:
+        paintComponent((PaintEvent) event);
+        break;
       case PaintEvent.UPDATE:
-      {
-        try
-          {
-            Graphics g = getGraphics();
-
-            if (!awtComponent.isShowing()  || awtComponent.getWidth() < 1 
-                || awtComponent.getHeight() < 1 || g == null)
-              break; 
-
-            g.setClip(((PaintEvent) event).getUpdateRect());
-
-            if (id == PaintEvent.PAINT)
-              awtComponent.paint(g);
-            else
-              awtComponent.update(g);
-                
-            g.dispose();
-          }
-        catch (InternalError e)
-          {
-            System.err.println(e);
-          }
-      }
+        updateComponent((PaintEvent) event);
         break;
       case KeyEvent.KEY_PRESSED:
         ke = (KeyEvent) event;
@@ -328,7 +298,49 @@
         break;
       }
   }
-  
+
+  // 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.
+    GdkGraphics g = (GdkGraphics) getGraphics();
+
+    g.setClip(event.getUpdateRect());
+
+    awtComponent.paint(g);
+
+    g.dispose();
+  }
+
+  // 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;
+
+    GdkGraphics g = (GdkGraphics) getGraphics();
+
+    g.setClip(event.getUpdateRect());
+
+    awtComponent.update(g);
+
+    g.dispose();
+  }
+
   public boolean isFocusTraversable () 
   {
     return true;
@@ -369,7 +381,7 @@
 
   public void repaint (long tm, int x, int y, int width, int height)
   {
-    if (x == 0 && y == 0 && width == 0 && height == 0)
+    if (width < 1 || height < 1)
       return;
 
     if (tm <= 0)
@@ -563,8 +575,7 @@
 
   protected void postExposeEvent (int x, int y, int width, int height)
   {
-    if (!isInRepaint)
-      q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT,
+    q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT,
                                    new Rectangle (x, y, width, height)));
   }
 
Index: gnu/java/awt/peer/gtk/GtkContainerPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkContainerPeer.java,v
retrieving revision 1.34
diff -u -r1.34 GtkContainerPeer.java
--- gnu/java/awt/peer/gtk/GtkContainerPeer.java	3 Mar 2006 21:09:00 -0000	1.34
+++ gnu/java/awt/peer/gtk/GtkContainerPeer.java	16 Mar 2006 02:32:22 -0000
@@ -99,11 +99,6 @@
       }
   }
 
-  public Graphics getGraphics ()
-  {
-    return super.getGraphics();
-  }
-
   public void beginLayout () { }
   public void endLayout () { }
   public boolean isPaintPending () { return false; }
Index: gnu/java/awt/peer/gtk/GtkDialogPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkDialogPeer.java,v
retrieving revision 1.32
diff -u -r1.32 GtkDialogPeer.java
--- gnu/java/awt/peer/gtk/GtkDialogPeer.java	15 Feb 2006 19:29:25 -0000	1.32
+++ gnu/java/awt/peer/gtk/GtkDialogPeer.java	16 Mar 2006 02:32:22 -0000
@@ -51,34 +51,6 @@
   {
     super (dialog);
   }
-  
-  public Graphics getGraphics ()
-  {
-    Graphics g;
-    if (GtkToolkit.useGraphics2D ())
-      g = new GdkGraphics2D (this);
-    else
-      g = new GdkGraphics (this);
-    g.translate (-insets.left, -insets.top);
-    return g;
-  }  
-  
-  protected void postMouseEvent(int id, long when, int mods, int x, int y, 
-				int clickCount, boolean popupTrigger)
-  {
-    super.postMouseEvent (id, when, mods, 
-			  x + insets.left, y + insets.top, 
-			  clickCount, popupTrigger);
-  }
-
-  protected void postExposeEvent (int x, int y, int width, int height)
-  {
-    if (!isInRepaint)
-      q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT,
-                                   new Rectangle (x + insets.left, 
-                                                  y + insets.top, 
-                                                  width, height)));
-  }
 
   void create ()
   {
Index: gnu/java/awt/peer/gtk/GtkFileDialogPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkFileDialogPeer.java,v
retrieving revision 1.31
diff -u -r1.31 GtkFileDialogPeer.java
--- gnu/java/awt/peer/gtk/GtkFileDialogPeer.java	28 Feb 2006 17:55:50 -0000	1.31
+++ gnu/java/awt/peer/gtk/GtkFileDialogPeer.java	16 Mar 2006 02:32:22 -0000
@@ -41,6 +41,7 @@
 import java.awt.Dialog;
 import java.awt.FileDialog;
 import java.awt.Graphics;
+import java.awt.event.PaintEvent;
 import java.awt.peer.FileDialogPeer;
 import java.io.File;
 import java.io.FilenameFilter;
@@ -166,10 +167,10 @@
     return filter.accept(dir, filename);
   }
 
-  public Graphics getGraphics ()
+  // Sun does not call FileDialog.update.
+  protected void updateComponent (PaintEvent event)
   {
-    // GtkFileDialog will repaint by itself
-    return null;
+    // Override GtkComponetPeer.updateComponent to do nothing.
   }
 
   // called back by native side: handle_response_cb
Index: gnu/java/awt/peer/gtk/GtkFramePeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkFramePeer.java,v
retrieving revision 1.44
diff -u -r1.44 GtkFramePeer.java
--- gnu/java/awt/peer/gtk/GtkFramePeer.java	17 Feb 2006 19:53:06 -0000	1.44
+++ gnu/java/awt/peer/gtk/GtkFramePeer.java	16 Mar 2006 02:32:22 -0000
@@ -196,17 +196,6 @@
 	}
   }
 
-  public Graphics getGraphics ()
-  {
-    Graphics g;
-    if (GtkToolkit.useGraphics2D ())
-      g = new GdkGraphics2D (this);
-    else
-      g = new GdkGraphics (this);
-    g.translate (-insets.left, -insets.top);
-    return g;
-  }
-  
   protected void postConfigureEvent (int x, int y, int width, int height)
   {
     int frame_width = width + insets.left + insets.right;
@@ -231,23 +220,6 @@
       }
   }
 
-  protected void postMouseEvent(int id, long when, int mods, int x, int y, 
-				int clickCount, boolean popupTrigger)
-  {
-    super.postMouseEvent (id, when, mods, 
-			  x + insets.left, y + insets.top, 
-			  clickCount, popupTrigger);
-  }
-
-  protected void postExposeEvent (int x, int y, int width, int height)
-  {
-    if (!isInRepaint)
-      q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT,
-                                   new Rectangle (x + insets.left, 
-                                                  y + insets.top, 
-                                                  width, height)));
-  }
-
   public int getState ()
   {
     return 0;
Index: gnu/java/awt/peer/gtk/GtkPanelPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkPanelPeer.java,v
retrieving revision 1.22
diff -u -r1.22 GtkPanelPeer.java
--- gnu/java/awt/peer/gtk/GtkPanelPeer.java	13 Feb 2006 14:32:53 -0000	1.22
+++ gnu/java/awt/peer/gtk/GtkPanelPeer.java	16 Mar 2006 02:32:22 -0000
@@ -59,37 +59,19 @@
   public void handleEvent(AWTEvent event)
   {
     int id = event.getID();
-    switch (id)
-      {
-      case MouseEvent.MOUSE_PRESSED:
-        awtComponent.requestFocusInWindow();
-        break;
-      case PaintEvent.UPDATE:
-      case PaintEvent.PAINT:
-      {
-        try
-          {
-            Graphics g = getGraphics();
-            if (! awtComponent.isShowing() || awtComponent.getWidth() < 1
-                || awtComponent.getHeight() < 1 || g == null)
-              return;
 
-            g.setClip(((PaintEvent) event).getUpdateRect());
+    if (id == MouseEvent.MOUSE_PRESSED)
+      awtComponent.requestFocusInWindow();
 
-            // Do not want to clear anything before painting.);
-            awtComponent.paint(g);
-
-            g.dispose();
-            return;
-          }
-        catch (InternalError e)
-          {
-            System.err.println(e);
-          }
-      }
-      }
     super.handleEvent(event);
   }
 
+  protected void updateComponent (PaintEvent event)
+  {
+    // Do not want to clear anything before painting.  Sun never
+    // calls Panel.update, only Panel.paint.
+    paintComponent(event);
+  }
+
   native void connectSignals ();
 }
Index: gnu/java/awt/peer/gtk/GtkWindowPeer.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkWindowPeer.java,v
retrieving revision 1.46
diff -u -r1.46 GtkWindowPeer.java
--- gnu/java/awt/peer/gtk/GtkWindowPeer.java	15 Feb 2006 19:29:25 -0000	1.46
+++ gnu/java/awt/peer/gtk/GtkWindowPeer.java	16 Mar 2006 02:32:23 -0000
@@ -42,6 +42,7 @@
 import java.awt.Component;
 import java.awt.Frame;
 import java.awt.Graphics;
+import java.awt.Rectangle;
 import java.awt.Window;
 import java.awt.event.PaintEvent;
 import java.awt.event.WindowEvent;
@@ -244,37 +245,55 @@
     // TODO Auto-generated method stub
     
   }
+
+  protected void postExposeEvent (int x, int y, int width, int height)
+  {
+    // Translate GTK co-ordinates, which do not include a window
+    // frame's insets, to AWT co-ordinates, which do include a window
+    // frame's insets.  GtkWindowPeer should always have all-zero
+    // insets but GtkFramePeer and GtkDialogPeer insets will be
+    // non-zero.
+    q().postEvent (new PaintEvent (awtComponent, PaintEvent.PAINT,
+                                   new Rectangle (x + insets.left, 
+                                                  y + insets.top, 
+                                                  width, height)));
+  }
+
   public boolean requestWindowFocus()
   {
     // TODO Auto-generated method stub
     return false;
   }
   
-  public void handleEvent(AWTEvent event)
+  public Graphics getGraphics ()
   {
-    int id = event.getID();
-    if (id == PaintEvent.UPDATE || id == PaintEvent.PAINT)
-      {
-        try
-          {
-            Graphics g = getGraphics();
-            if (! awtComponent.isShowing() || awtComponent.getWidth() < 1
-                || awtComponent.getHeight() < 1 || g == null)
-              return;
-
-            g.setClip(((PaintEvent) event).getUpdateRect());
-
-            // Do not want to clear anything before painting.
-            awtComponent.paint(g);
-
-            g.dispose();
-            return;
-          }
-        catch (InternalError e)
-          {
-            System.err.println(e);
-          }
-      }
-    super.handleEvent(event);
+    Graphics g = super.getGraphics ();
+    // Translate AWT co-ordinates, which include a window frame's
+    // insets, to GTK co-ordinates, which do not include a window
+    // frame's insets.  GtkWindowPeer should always have all-zero
+    // insets but GtkFramePeer and GtkDialogPeer insets will be
+    // non-zero.
+    g.translate (-insets.left, -insets.top);
+    return g;
+  }
+
+  protected void updateComponent (PaintEvent event)
+  {
+    // Do not clear anything before painting.  Sun never calls
+    // Window.update, only Window.paint.
+    paintComponent(event);
+  }
+
+  protected void postMouseEvent(int id, long when, int mods, int x, int y, 
+				int clickCount, boolean popupTrigger)
+  {
+    // Translate AWT co-ordinates, which include a window frame's
+    // insets, to GTK co-ordinates, which do not include a window
+    // frame's insets.  GtkWindowPeer should always have all-zero
+    // insets but GtkFramePeer and GtkDialogPeer insets will be
+    // non-zero.
+    super.postMouseEvent (id, when, mods, 
+			  x + insets.left, y + insets.top, 
+			  clickCount, popupTrigger);
   }
 }
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics.c
===================================================================
RCS file: /sources/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics.c,v
retrieving revision 1.33
diff -u -r1.33 gnu_java_awt_peer_gtk_GdkGraphics.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics.c	14 Dec 2005 21:33:38 -0000	1.33
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkGraphics.c	16 Mar 2006 02:32:26 -0000
@@ -43,6 +43,45 @@
 
 static jmethodID initComponentGraphicsUnlockedID;
 
+/*
+ * AWT applications may call Graphics methods from threads other than
+ * the GDK main thread, so we must call XFlush after each batch of
+ * drawing operations, otherwise animations flicker.  Flushing after
+ * every graphics operation is excessive and negatively affects
+ * performance (PR 26486).  We set the maximum frequency to 50 times
+ * per second, or a minimum period of 20 milliseconds between calls to
+ * XFlush.  See gnu.classpath.examples.awt.AnimationApplet for an
+ * example applet that requires these XFlush calls.
+ */
+
+static short flush_scheduled = 0;
+
+static gboolean flush (gpointer data __attribute__((unused)))
+{
+  gdk_threads_enter ();
+
+  XFlush (GDK_DISPLAY ());
+  flush_scheduled = 0;
+
+  gdk_threads_leave ();
+
+  return FALSE;
+}
+
+/* The minimum time period between calls to XFlush. */
+#define MINIMUM_FLUSH_PERIOD 20
+
+/* schedule_flush must be called with the GDK lock held. */
+static void
+schedule_flush ()
+{
+  if (!flush_scheduled)
+    {
+      g_timeout_add (MINIMUM_FLUSH_PERIOD, flush, NULL);
+      flush_scheduled = 1;
+    }
+}
+
 void
 cp_gtk_graphics_init_jni (void)
 {
@@ -343,7 +382,7 @@
   pango_layout_iter_free (iter);
   pango_layout_set_text (pfont->layout, "", -1);
   
-  gdk_flush ();
+  schedule_flush ();
 
   (*env)->ReleaseStringUTFChars (env, str, cstr);
 
@@ -363,7 +402,7 @@
   gdk_draw_line (g->drawable, g->gc, 
 		 x + g->x_offset, y + g->y_offset, 
 		 x2 + g->x_offset, y2 + g->y_offset);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
@@ -380,7 +419,7 @@
 
   gdk_draw_rectangle (g->drawable, g->gc, TRUE, 
 		      x + g->x_offset, y + g->y_offset, width, height);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
@@ -397,7 +436,7 @@
 
   gdk_draw_rectangle (g->drawable, g->gc, FALSE, 
 		      x + g->x_offset, y + g->y_offset, width, height);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
@@ -419,7 +458,7 @@
 		     x + g->x_offset, y + g->y_offset,
 		     x + g->x_offset + dx, y + g->y_offset + dy,
 		     width, height);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
@@ -461,7 +500,7 @@
       gdk_gc_set_foreground (g->gc, &(saved.foreground));
     }
 
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
@@ -517,7 +556,7 @@
   gdk_draw_arc (g->drawable, g->gc, FALSE, 
 		x + g->x_offset, y + g->y_offset, 
 		width, height, angle1 << 6, angle2 << 6);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }  
@@ -564,7 +603,7 @@
 			     g->x_offset, g->y_offset);
 
   gdk_draw_lines (g->drawable, g->gc, points, npoints);
-  gdk_flush ();
+  schedule_flush ();
 
   g_free (points);
 
@@ -591,7 +630,7 @@
     points[npoints++] = points[0];
 
   gdk_draw_lines (g->drawable, g->gc, points, npoints);
-  gdk_flush ();
+  schedule_flush ();
 
   g_free (points);
 
@@ -612,7 +651,7 @@
   points = translate_points (env, xpoints, ypoints, npoints,
 			     g->x_offset, g->y_offset);
   gdk_draw_polygon (g->drawable, g->gc, TRUE, points, npoints);
-  gdk_flush ();
+  schedule_flush ();
 
   g_free (points);
 
@@ -633,7 +672,7 @@
   gdk_draw_arc (g->drawable, g->gc, TRUE, 
 		x + g->x_offset, y + g->y_offset, 
 		width, height, angle1 << 6, angle2 << 6);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }  
@@ -651,7 +690,7 @@
   gdk_draw_arc (g->drawable, g->gc, FALSE, 
 		x + g->x_offset, y + g->y_offset, 
 		width, height, 0, 23040);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }  
@@ -669,7 +708,7 @@
   gdk_draw_arc (g->drawable, g->gc, TRUE, 
 		x + g->x_offset, y + g->y_offset, 
 		width, height, 0, 23040);
-  gdk_flush ();
+  schedule_flush ();
 
   gdk_threads_leave ();
 }
Index: examples/gnu/classpath/examples/awt/AnimationApplet.java
===================================================================
RCS file: examples/gnu/classpath/examples/awt/AnimationApplet.java
diff -N examples/gnu/classpath/examples/awt/AnimationApplet.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/gnu/classpath/examples/awt/AnimationApplet.java	16 Mar 2006 02:12:52 -0000
@@ -0,0 +1,233 @@
+/* AnimationApplet.java -- An example of an old-style AWT applet
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath examples.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA. */
+
+package gnu.classpath.examples.awt;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.*;
+
+
+/**
+ * AnimationApplet demonstrates the need for Xflush calls in
+ * GdkGraphics.c.  To see how this demo can fail in their absence,
+ * remove the contents of schedule_flush in GdkGraphics.c.  The
+ * animation will be so choppy that it is effectively stopped.
+ */
+public class AnimationApplet
+  extends Applet
+  implements Runnable
+{
+  boolean going = false;
+  Thread animThread = null;
+  int SPEED = 5;
+  int circleX = 0;
+  int circleY = 0;
+  int circleXold = 0;
+  int circleYold = 0;
+  int circleXdelta = 0;
+  int circleYdelta = 0;
+  int circleDiameter = 0;
+  int autoCircleX = 0;
+  int autoCircleY = 0;
+  int autoCircleXold = 0;
+  int autoCircleYold = 0;
+  int autoCircleXdelta = (int) (0.66 * SPEED);
+  int autoCircleYdelta = (int) (1.33 * SPEED);
+  int boardWidth = 0;
+  int boardHeight = 0;
+  int CIRCLE_SIZE = 5;
+
+  private Graphics appletGraphics;
+
+  // Update the circles' location values.
+  private void moveCircles()
+  {
+    circleX += circleXdelta;
+    if (circleX < 0)
+      circleX = 0;
+    if (circleX > boardWidth - circleDiameter)
+      circleX = boardWidth - circleDiameter;
+
+    circleY += circleYdelta;
+    if (circleY < 0)
+      circleY = 0;
+    if (circleY > boardHeight - circleDiameter)
+      circleY = boardHeight - circleDiameter;
+
+    autoCircleX += autoCircleXdelta;
+    if (autoCircleX < 0)
+      {
+        autoCircleX = 0;
+        autoCircleXdelta = -autoCircleXdelta;
+      }
+    if (autoCircleX > boardWidth - circleDiameter)
+      {
+        autoCircleX = boardWidth - circleDiameter;
+        autoCircleXdelta = -autoCircleXdelta;
+      }
+
+    autoCircleY += autoCircleYdelta;
+    if (autoCircleY < 0)
+      {
+        autoCircleY = 0;
+        autoCircleYdelta = -autoCircleYdelta;
+      }
+    if (autoCircleY > boardHeight - circleDiameter)
+      {
+        autoCircleY = boardHeight - circleDiameter;
+        autoCircleYdelta = -autoCircleYdelta;
+      }
+  }
+
+  // Clear the circle in the old location and paint a new circle
+  // in the new location.
+  private void paintCircles()
+  {
+    appletGraphics.setColor(Color.BLUE);
+    appletGraphics.fillOval(circleXold, circleYold, circleDiameter,
+                            circleDiameter);
+    appletGraphics.setColor(Color.YELLOW);
+    appletGraphics.fillOval(circleX, circleY, circleDiameter,
+                            circleDiameter);
+
+    appletGraphics.setColor(Color.BLUE);
+    appletGraphics.fillOval(autoCircleXold, autoCircleYold, circleDiameter,
+                            circleDiameter);
+    appletGraphics.setColor(Color.WHITE);
+    appletGraphics.fillOval(autoCircleX, autoCircleY, circleDiameter,
+                            circleDiameter);
+  }
+
+  // Override Applet.run.
+  public void run()
+  {
+    while (animThread != null)
+      {
+        circleXold = circleX;
+        circleYold = circleY;
+        autoCircleXold = autoCircleX;
+        autoCircleYold = autoCircleY;
+
+        moveCircles();
+        paintCircles();
+
+        if (animThread != null)
+          {
+            try
+              {
+                Thread.sleep(20);
+              }
+            catch (InterruptedException e)
+              {
+              }
+          }
+      }
+  }
+
+  // Override Applet.paint.
+  public void paint(Graphics g)
+  {
+    boardWidth = this.getSize().width;
+    boardHeight = this.getSize().height;
+    g.setColor(Color.BLUE);
+    g.fillRect(0, 0, boardWidth, boardHeight);
+    if (!going)
+      {
+        FontMetrics fm = appletGraphics.getFontMetrics();
+        appletGraphics.setColor(Color.WHITE);
+        String msg = "Click to Start";
+        appletGraphics.drawString(msg,
+                                  (boardWidth >> 1) - (fm.stringWidth(msg) >> 1),
+                                  (boardHeight >> 1) - (fm.getHeight() >> 1));
+      }
+  }
+
+  // Override Applet.destroy.
+  public void destroy()
+  {
+    // animThread.stop();
+    animThread = null;
+  }
+
+  // Override Applet.init.
+  public void init()
+  {
+    boardWidth = this.getSize().width;
+    boardHeight = this.getSize().height;
+    going = false;
+    appletGraphics = getGraphics();
+    appletGraphics.setFont(new Font(appletGraphics.getFont().getName(),
+                                    Font.BOLD, 15));
+  }
+
+  // Override Component.preferredSize for when we're run standalone.
+  public Dimension preferredSize ()
+  {
+    return new Dimension (400, 400);
+  }
+
+  // Override Applet.handleEvent, the old-style AWT-event handler.
+  public boolean handleEvent(Event event)
+  {
+    switch (event.id)
+      {
+      case Event.MOUSE_DOWN:
+        if (!going)
+          {
+            going = true;
+            circleDiameter = boardWidth / CIRCLE_SIZE;
+            circleX = (boardWidth - circleDiameter) >> 1;
+            circleY = (boardHeight - circleDiameter) >> 1;
+            circleXdelta = 0;
+            circleYdelta = 0;
+            repaint();
+            animThread = new Thread(this);
+            animThread.start();
+          }
+        break;
+      case Event.KEY_ACTION:
+      case Event.KEY_PRESS:
+        if (event.key == Event.LEFT)
+          circleXdelta = -SPEED;
+        else if (event.key == Event.RIGHT)
+          circleXdelta = SPEED;
+        else if (event.key == Event.UP)
+          circleYdelta = -SPEED;
+        else if (event.key == Event.DOWN)
+          circleYdelta = SPEED;
+        break;
+      case Event.KEY_ACTION_RELEASE:
+      case Event.KEY_RELEASE:
+        if (event.key == Event.LEFT && circleXdelta < 0)
+          circleXdelta = 0;
+        else if (event.key == Event.RIGHT && circleXdelta > 0)
+          circleXdelta = 0;
+        else if (event.key == Event.UP && circleYdelta < 0)
+          circleYdelta = 0;
+        else if (event.key == Event.DOWN && circleYdelta > 0)
+          circleYdelta = 0;
+        break;
+      default:
+        break;
+      }
+    return false;
+  }
+}
Index: examples/gnu/classpath/examples/awt/Demo.java
===================================================================
RCS file: /sources/classpath/classpath/examples/gnu/classpath/examples/awt/Demo.java,v
retrieving revision 1.4
diff -u -r1.4 Demo.java
--- examples/gnu/classpath/examples/awt/Demo.java	13 Jul 2005 23:22:30 -0000	1.4
+++ examples/gnu/classpath/examples/awt/Demo.java	16 Mar 2006 02:12:52 -0000
@@ -1,5 +1,5 @@
 /* Demo.java -- Shows examples of AWT components
-   Copyright (C) 1998, 1999, 2002, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2002, 2004, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath examples.
 
@@ -153,7 +153,8 @@
       addSubWindow ("TextField", new TextFieldWindow ());
       addSubWindow ("RandomTests", new TestWindow (this));
       addSubWindow ("RoundRect", new RoundRectWindow ());
-      
+      addSubWindow ("Animation", new AnimationWindow ());
+
       Panel sp = new Panel();
       PrettyPanel p = new PrettyPanel();
       p.setLayout (new GridLayout (windows.size(), 1));
@@ -864,4 +865,35 @@
     }
   }
 
+  static class AnimationWindow extends SubFrame
+  {
+    AnimationApplet a;
+    public void init ()
+    {
+      initted = true;
+      setTitle("Animation");
+      Button cb = new Button ("Close");
+      cb.addActionListener(new ActionListener () {
+          public void actionPerformed (ActionEvent e) 
+          {
+            if (a != null)
+              {
+                a.destroy();
+                dispose();
+              }
+          }
+        });
+      a = new AnimationApplet();
+      add(a, "Center");
+      add(cb, "South");
+      pack();
+    }
+
+    public void show()
+    {
+      super.show();
+      a.init();
+      a.run();
+    }
+  }
 }
Index: examples/gnu/classpath/examples/swing/Demo.java
===================================================================
RCS file: /sources/classpath/classpath/examples/gnu/classpath/examples/swing/Demo.java,v
retrieving revision 1.38
diff -u -r1.38 Demo.java
--- examples/gnu/classpath/examples/swing/Demo.java	15 Mar 2006 16:41:02 -0000	1.38
+++ examples/gnu/classpath/examples/swing/Demo.java	16 Mar 2006 02:12:52 -0000
@@ -141,6 +141,8 @@
                                          TabbedPaneDemo.createDemoFactory())));
     examples.add(new JMenuItem(new PopupAction("Tree",
                                                TreeDemo.createDemoFactory())));
+    examples.add(new JMenuItem(new PopupAction("Paint Performance",
+                                               FillRect.createDemoFactory())));
 
     final JMenuItem vmMenu;
     
@@ -484,6 +486,8 @@
                                          TabbedPaneDemo.createDemoFactory())));
     panel.add(new JButton(new PopupAction("Tree",
                                           TreeDemo.createDemoFactory())));
+    panel.add(new JButton(new PopupAction("Paint Performance",
+                                          FillRect.createDemoFactory())));
     JButton exitDisposer = mkDisposerButton(frame);
     panel.add(exitDisposer);
     exitDisposer.addActionListener(new ActionListener()
Index: examples/gnu/classpath/examples/swing/FillRect.java
===================================================================
RCS file: examples/gnu/classpath/examples/swing/FillRect.java
diff -N examples/gnu/classpath/examples/swing/FillRect.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/gnu/classpath/examples/swing/FillRect.java	16 Mar 2006 02:12:52 -0000
@@ -0,0 +1,301 @@
+/* FillRect.java - demonstrator for classpath/gcj fillrect performance issue
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath examples.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA. */
+
+package gnu.classpath.examples.swing;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/** 
+ * @author Norman Hendrich
+ */
+public class FillRect
+  extends JPanel
+  implements ActionListener 
+{
+
+  static FillRect fillRectDemo;
+
+  LCDCanvas lcd;
+  Worker worker;
+  JLabel label;
+
+  int     nx = 64;
+  int     ny = 64;
+  int     matrix[][], future[][];
+  int     generation = 0;
+
+  // 20 msec, or 50 repaints per sec (theoretically)
+  int     sleepMillis = 20;
+  long    lastMillis = System.currentTimeMillis();
+
+  boolean enableRepaints = true;
+
+  public void actionPerformed(ActionEvent e) 
+  {
+    if (e.getActionCommand().equals("CLOSE"))
+    {
+      System.exit(0);
+    }
+  }
+
+  public FillRect()
+  {
+    setSize(64, 64);
+    createContent();
+  }
+
+  public void createContent()
+  {
+    setLayout(new BorderLayout());
+
+    JPanel p = new JPanel(new BorderLayout());
+    lcd   = new LCDCanvas();
+    label = new JLabel();
+    label.setText("paintComponent took 00 msec. (00000 fillRect calls)");
+    p.add(lcd, BorderLayout.CENTER);
+    p.add(label, BorderLayout.SOUTH);
+    add(p);
+  }
+
+  public void setSize(int _nx,int _ny )
+  {
+    nx = _nx;
+    ny = _ny;
+    matrix = new int[nx][ny];
+    future = new int[nx][ny];
+  }
+
+  public void initFrameContent()
+  {
+    JPanel closePanel = new JPanel();
+    JButton closeButton = new JButton("Close");
+    closeButton.setActionCommand("CLOSE");
+    closeButton.addActionListener(this);
+    closePanel.add(closeButton);
+    add(closePanel, BorderLayout.SOUTH);
+  }
+
+  public void setSleepMillis(int millis)
+  {
+    sleepMillis = millis;
+  }
+
+  public class LCDCanvas extends JPanel
+  {
+    private int   sx, sy;
+    private Color activePixel  = new Color(30, 30, 40);
+    private Color passivePixel = new Color(200, 180, 240);
+    private Color gridPixel    = new Color(255, 240, 240);
+
+    public LCDCanvas()
+    {
+      super();
+      sx = 4 * nx;
+      sy = 4 * ny;
+    }
+
+    public void paintComponent(Graphics g)
+    {
+      // for buffered drawing - not used atm
+      // g.drawImage( buffer, 0, 0, null );
+      long t1 = System.currentTimeMillis();
+
+      g.setColor(gridPixel);
+      g.fillRect(0, 0, sx, sy);
+
+      Color pixelColor = null;
+      for (int ix = 0; ix < nx; ix++)
+        {
+          for (int iy = 0; iy < ny; iy++)
+            {
+              if (matrix[ix][iy] != 0)
+                pixelColor = activePixel;
+              else
+                pixelColor = passivePixel;
+
+              g.setColor(pixelColor);
+              g.fillRect(4 * ix, 4 * iy, 3, 3);
+            }
+        }
+
+      long t2 = System.currentTimeMillis();
+
+      label.setText("paintComponent took " + (t2 - t1) + " msec. "
+                    +  "(" + (nx * ny + 1) + " fillRect calls)");
+    }
+
+    public Dimension getPreferredSize()
+    {
+      return new Dimension(sx,sy);
+    }
+
+    public Dimension getMinimumSize()
+    {
+      return new Dimension(sx,sy);
+    }
+  }
+
+  public class Worker extends Thread
+  {
+    public void run()
+    {
+      boolean running = true;
+      while(running)
+        {
+          iteration();
+
+          if (enableRepaints)
+            display();
+
+          if (sleepMillis > 0)
+            {
+              try
+                {
+                  Thread.sleep( sleepMillis );
+                }
+              catch(InterruptedException ie)
+                {
+                  running = false;
+                }
+            }
+        }
+    }
+  }
+
+  /** 
+   * stupid animation algorithm: show binary representation of current
+   * iteration.
+   */
+  public void iteration()
+  {
+    generation++;
+
+    for (int i = 0; i < nx; i++)
+      {
+        long tmp1 = 1L << i;
+        for (int j = 0; j < ny; j++)
+          {
+            // count neighbors
+            long tmp2 = (1L << j);
+
+        
+            long tmp3 = generation & tmp1 & tmp2;
+            if (tmp3 != 0) 
+              matrix[i][j] = 1;
+            else
+              matrix[i][j] = 0;
+          }
+    }
+
+    if ((generation % 100) == 0)
+      {
+        long t = System.currentTimeMillis();
+        //        System.out.println(
+        //           " generation= " + generation +
+        //           " iterations/sec= " + 100.0*1000/(t-lastMillis) );
+        lastMillis = t;
+      }
+  }
+
+  public void display()
+  {
+    lcd.repaint();
+  }
+
+  public static void usage()
+  {
+    System.out.println( 
+      "Usage: <java> FillRect2 [-sleep <millis>] [-size <int>] [-nopaint]\n"
+    + "Example: jamvm FillRect2 -sleep 10 -size 100\n"
+    );
+    System.exit(0);
+  }
+
+  public static void main(String args[])
+    throws Exception
+  {
+    fillRectDemo = new FillRect();
+    for (int i = 0; i < args.length; i++)
+      {
+        if ("-help".equals(args[i]))
+          {
+            usage();
+          }
+        if ("-sleep".equals(args[i]))
+          {
+            fillRectDemo.setSleepMillis( Integer.parseInt(args[i + 1]));
+            i++;
+          }
+        if ("-size".equals(args[i]))
+          {
+            int size = Integer.parseInt(args[i + 1]);
+            fillRectDemo.setSize(size, size);
+            i++;
+          }
+        if ("-nopaint".equals(args[i]))
+          {
+            fillRectDemo.enableRepaints = false; 
+          }
+      }
+
+    SwingUtilities.invokeLater (new Runnable()
+     {
+       public void run()
+       {
+
+         fillRectDemo.initFrameContent();
+         JFrame frame = new JFrame("FillRect performance test");
+         frame.getContentPane().add(fillRectDemo);
+         frame.pack();
+         frame.show();
+         fillRectDemo.worker = fillRectDemo.new Worker();
+         fillRectDemo.worker.start();
+       }
+      });
+  }
+
+  /**
+   * Returns a DemoFactory that creates a SliderDemo.
+   *
+   * @return a DemoFactory that creates a SliderDemo
+   */
+  public static DemoFactory createDemoFactory()
+  {    
+    return new DemoFactory()
+    {
+      public JComponent createDemo()
+      {
+        fillRectDemo = new FillRect();
+        SwingUtilities.invokeLater
+        (new Runnable()
+         {
+           public void run()
+           {
+             fillRectDemo.worker = fillRectDemo.new Worker();
+             fillRectDemo.worker.start();
+           }
+         });
+        return fillRectDemo;
+      }
+    };
+  }
+}

Reply via email to