views:

727

answers:

1

I am using a loop to invoke double buffering painting. This, together with overriding my only Panel's repaint method, is designed to pass complete control of repaint to my loop and only render when it necessary (i.e. some change was made in the GUI).

This is my rendering routine:

    Log.write("renderer painting");

    setNeedsRendering(false);

    Graphics g = frame.getBufferStrategy().getDrawGraphics();

    g.setFont(font);
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, window.getWidth(),window.getHeight());

    if(frame != null)
        window.paint(g);

    g.dispose();

    frame.getBufferStrategy().show();

As you can see, it is pretty standard. I get the grpahics object from the buffer strategy (initialized to 2), make it all black and pass it to the paint method of my "window" object.

After window is done using the graphics object, I dispose of it and invoke show on the buffer strategy to display the contents of the virtual buffer.

It is important to note that window passes the graphics object to many other children components the populate the window and each one, in turn, uses the same instance of the graphics object to draw something onto the screen: text, shapes, or images.

My problem begins to show when the system is running and a large image is rendered. The image appears to be cut into seveeal pieces and drawn again and again (3-4 times) with different offsets inside of where the image is supposed to be rendered. See my attached images:

This is the original image: alt text

This is what I get: alt text

Note that in the second picture, I am rendering shapes over the picture - these are always at the correct position.

Any idea why this is happening? If I save the image to file, as it is in memory, right before the call to g.drawImage(...) it is identical to the original.

+2  A: 

Uh, you are using Swing ?
Normally Swing automatically renders the image, you can't switch it off. The repaint() method is out of bounds because Swing has a very complicated rendering routine due to method compatibility for AWT widgets and several optimizations, inclusive drawing only when necessary ! If you want to use the High-Speed Drawing API, you use a component with a BufferStrategy like JFrame and Window, use

setIgnoreRepaint(false);

to switch off Swing rendering, set up a drawing loop and paint the content itself. Or you can use JOGL for OpenGL rendering. The method you are using seems completely at odds with correct Java2D usage.

Here the correct use:

public final class FastDraw extends JFrame {
  private static final transient double NANO = 1.0e-9;


 private BufferStrategy bs;

 private BufferedImage frontImg;

 private BufferedImage backImg;

 private int PIC_WIDTH,
             PIC_HEIGHT;

  private Timer timer;

  public FastDraw() {
    timer = new Timer(true);
    JMenu menu = new JMenu("Dummy");
    menu.add(new JMenuItem("Display me !"));
    menu.add(new JMenuItem("Display me, too !"));
    JMenuBar menuBar = new JMenuBar();
    menuBar.add(menu);
    setJMenuBar(menuBar);

    setIgnoreRepaint(true);
    setVisible(true);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) {
        super.windowClosing(evt);
        timer.cancel();
        dispose();
        System.exit(0);
      }
    });
    try {
      backImg = javax.imageio.ImageIO.read(new File("MyView"));
      frontImg = javax.imageio.ImageIO.read(new File("MyView"));
    }
    catch (IOException e) {
      System.out.println(e.getMessage());
    }
    PIC_WIDTH = backImg.getWidth();
    PIC_HEIGHT = backImg.getHeight();
    setSize(PIC_WIDTH, PIC_HEIGHT);


    createBufferStrategy(1); // Double buffering
    bs = getBufferStrategy();
    timer.schedule(new Drawer(),0,20);
  }
  public static void main(String[] args) {
    new FastDraw();
  }

  private class Drawer extends TimerTask {

    private VolatileImage img;

    private int count = 0;

    private double time = 0;

    public void run() {
      long begin = System.nanoTime();
      Graphics2D  g  = (Graphics2D) bs.getDrawGraphics();
      GraphicsConfiguration gc = g.getDeviceConfiguration();
      if (img == null)
        img = gc.createCompatibleVolatileImage(PIC_WIDTH, PIC_HEIGHT);
      Graphics2D g2 = img.createGraphics();
      // Zeichenschleife
      do {
        int valStatus = img.validate(gc);
        if (valStatus == VolatileImage.IMAGE_OK)
          g2.drawImage(backImg,0,0,null);
        else {
          g.drawImage(frontImg, 0, 0, null);
        }
        // volatile image is ready
        g.drawImage(img,0,50,null);
        bs.show();
      } while (img.contentsLost());
      time = NANO*(System.nanoTime()-begin);
      count++;
      if (count % 100 == 0)
        System.out.println(1.0/time);
    }
  }
Thorsten S.
Thanks for your response.I don't understand the part whre you check the volatile image's status. If it is OK, you draw to g2, but if not, you draw to g? Why?And I guess that frontImg and backImg are two different images you're using to debug this code?Also, why are you repeating the drawing over and over while the contants of the volatile image are lost?
Warlax
Even though I implemented your suggestion, the problem persists.
Warlax
Could you zip your code/img and send it to [email protected] so I can test it ?
Thorsten S.