This site is currently read-only as we are migrating to Oracle Forums for an improved community experience. You will not be able to initiate activity until January 31st, when you will be able to use this site as normal.

    Forum Stats

  • 3,890,899 Users
  • 2,269,649 Discussions
  • 7,916,821 Comments

Discussions

eliminate last bit of flicker during Frame re-sizing

843807
843807 Member Posts: 46,582
edited Aug 9, 2010 2:03PM in Abstract Window Toolkit (AWT)
The following code prototypes screen flicker. The only flicker (noticeable to me at least) is while the Frame is being re-sized.

Is there a technique to eliminate flicker during Frame resizing? Given how the paint(Graphics g) method works (at least how I think it works), I don't think there is...? When I start using light-weight components, I wonder if the flickering while re-sizing will disappear? Will that be something to look for when testing JComponent instead of Canvas? As this is my first serious attempt at AWT, any general comments about how the code is written is welcomed.
public class Main {
  private static final Frame frame = new Frame("Alpha GUI v1.3");
  static {
    frame.setSize(800, 600);
    frame.addWindowListener(new MyWindowListener());
  }

  public static void main(String[] args) {
    Four four = new Four();
    frame.add(four);
    frame.setVisible(true);
    new Thread(four).start();
  }
      // I don't like the looks of anonymous classes yet. too confusing to debug right now.
      static class MyWindowListener extends WindowAdapter implements WindowListener  {
        @Override
        public void windowClosing(WindowEvent ev) { System.exit(1); }
      }
}
public class Four extends Canvas implements Runnable {
  private final Canvas canvas;
  private final StrokeJP[] strokes = new StrokeJP[4];

  Four() {
    strokes[0] = new Stroke1();
    strokes[1] = new Stroke2();
    strokes[2] = new Stroke3();
    strokes[3] = new Stroke4();
    canvas = this;
  }

  @Override
  public void run() {
    for(int i = 0; i < 500; i++) {
      try { Thread.currentThread().sleep(10); } catch(InterruptedException err) { }
      EventQueue.invokeLater(strokes[0]);
      EventQueue.invokeLater(strokes[1]);
      EventQueue.invokeLater(strokes[2]);
      EventQueue.invokeLater(strokes[3]);
    }
  }

  @Override
  public void paint(Graphics g) {
    Image img = createImage(getSize().width, getSize().height);
    Graphics og = img.getGraphics();
    strokes[0].paintFull(og);
    strokes[1].paintFull(og);
    strokes[2].paintFull(og);
    strokes[3].paintFull(og);

    g.drawImage(img, 0, 0, null);
  }
        // Still designing a shared parent class for all strokes
        private class Stroke1 extends StrokeJP {
          private BasicStroke stroke = new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
          private final float x1 = 10;
          private final float y1 = 10;
          private float x2 = x1; private float x3 = x1;
          private float y2 = y1; private float y3 = y1;

          @Override
          public void run() {
            paintIncrimental(canvas.getGraphics());
            x2++;
          }
          public void paintIncrimental(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(stroke);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.draw(new Line2D.Float(x3, y3, x2, y2));
            x3 = x2;
          }
          public void paintFull(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(stroke);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.draw(new Line2D.Float(x1, y1, x2, y2));
          }
        }

        // Still designing a shared parent class for all strokes
        private class Stroke2 extends StrokeJP {
          private BasicStroke stroke = new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
          private final float x1 = 10;
          private final float y1 = 10;
          private float x2 = x1; private float x3 = x1;
          private float y2 = y1; private float y3 = y1;

          @Override
          public void run() {
            paintIncrimental(canvas.getGraphics());
            x2++;
            y2++;
          }
          public void paintIncrimental(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(stroke);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.draw(new Line2D.Float(x3, y3, x2, y2));
            x3 = x2;
            y3 = y2;
          }
          public void paintFull(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(stroke);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.draw(new Line2D.Float(x1, y1, x2, y2));
          }
        }
        // ...................
}
// Working on this class, but would like closure on all flickering issues before proceeding.
public abstract class StrokeJP implements Runnable {
    abstract void paintIncrimental(Graphics g);
    abstract void paintFull(Graphics g);
}
I paint incrimentally when possible, and also double buffer. Are there other techniques?

Edited by: rerf on Aug 8, 2010 1:49 AM

Edited by: rerf on Aug 8, 2010 7:18 AM

Comments

  • 843807
    843807 Member Posts: 46,582
    edited Aug 8, 2010 11:55PM
    problem solved [sort of]. this detailed discussion from this forum was very useful:

    [canvas in a frame re-sizing flicker|http://forums.sun.com/thread.jspa?threadID=5360272&start=0&tstart=0]

    The solution is to not use a Canvas. Rather, draw directly on the Frame. Someone in that discussion mentioned it might be a bug? My environment is:
    1.6.0_05 on XP-home sp3.
    Maybe I misunderstood the talk about a bug because some of the comments where beyond my understanding right now. In any event, as a Frame is a top-level container I think its a really bad design to draw directly onto then. Thus, my solution going forward is to keep using the Canvas, and pause all rendering during any re-sizing. I assume that is doable.

    -- edit --
    well, well, well. This flickering is/was a bug. It can be fixed (on XP at the least) with this line of code. If only I had googled for more than 5-minutes this morning, I could have had a nice day...
    System.setProperty("sun.awt.noerasebackground", "true");
    I can't test this fix on other operating systems, but am curious.

    Edited by: rerf on Aug 8, 2010 8:37 PM
  • EJP
    EJP Member Posts: 32,943 Gold Crown
    You could clip the image's Graphics to the passed Graphic's clip rectify, that should speed up everything.
  • 843807
    843807 Member Posts: 46,582
    edited Aug 9, 2010 2:03PM
    ejp wrote:
    You could clip the image's Graphics to the passed Graphic's clip rectify, that should speed up everything.
    I don't see how the clip would help. Resizing the Frame makes the clip of the Graphics the exact same width/height as the enclosing Frame.

    Here is the absolute minimal demo that shows the behavior I want:
    public class Main {
      private static final Frame frame;
    
      static {
        // critical!!! remove the next line and there is flicker while resizing
        System.setProperty("sun.awt.noerasebackground", "true");
        frame = new Frame("Alpha GUI v1.5");
        frame.setSize(800, 600);
      }
    
      public static void main(String[] args) {
        Four four = new Four();
        frame.add(four);
        frame.setVisible(true);
      }
    }
    public class Four extends Canvas {
      // critical to buffer!!!
      private Image offscreen;
    
      void demo(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(10));
        Ellipse2D e = new Ellipse2D.Float(130, 50, 40, 200);
        g2.draw(e);
      }
    
      @Override
      public void paint(Graphics g) {
        offscreen = createImage(g.getClipBounds().width, g.getClipBounds().height);
        demo(offscreen.getGraphics());
        g.drawImage(offscreen, 0, 0, null);
      }
    }
    I've read enough to know its bad design to grab a graphics context outside any of:

    paint(Graphics g), update(Graphics g), repaint(Graphics g).

    And when I do, its my responsibility to dispose of the Graphics objects, otherwise I'll eventually exhaust some kind of gui resources.(I will deal with that later). And everyone recommends using Swing components. But first I want to take AWT as far as possible. I want to compare pure AWT with Swing.

    In the end, from the API, this is what I think the problem is:
    public void paint(Graphics g);
    This method is called when the contents of the component should be painted; such as when the component is first being shown or is damaged and in need of repair. The clip rectangle in the Graphics parameter is set to the area which needs to be painted . Subclasses of Component that override this method need not call super.paint(g) .

    My speculation is that somehow, when a Canvas is put in a Frame, a resize of the Frame causes the entire Canvas to be considered "damaged" (and probably this is the correct behavior). Further, as per the API, overriding paint(Graphics g) cannot prevent the invokation of super.paint(g), and I think some how this is related to the problem.
    setIgnoreRepaint(boolean tf);
    might lead to an interesting solution, but I could not take that technique very far.

    I have the behavior I want. Though I feel a little ill about the platform portability of:
    System.setProperty("sun.awt.noerasebackground", "true");
    but its good enough right now.

    ---> bug id # [4803767|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803767]
This discussion has been closed.