Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to eliminate java animation flickering

I'm writing a java application which needs to scroll a waveform smoothly over the screen. I've spend ages going through various tutorials to figure out how to make this animation as smooth as possible. As far as I can see I've done all the normal things to eliminate flickering (drawing to offscreen buffer and rendering in a single step, plus overriding update so the screen isn't blanked), but my animation still flickers, and the screen looks like it's being blanked before each update.

I'm sure there's something fundamental (and probably simple) I'm missing, but I'm out of ideas. I'll post a class below which illustrates the problem. Any help would be much appreciated.

import java.awt.*;
import javax.swing.*;

public class FlickerPanel extends JPanel implements Runnable {

    private float [] pixelMap = new float[0];

    /** Cached graphics objects so we can control our animation to reduce flicker **/
    private Image screenBuffer;
    private Graphics bufferGraphics;

    public FlickerPanel () {
        Thread t = new Thread(this);
        t.start();
    }

    private float addNoise () {
        return (float)((Math.random()*2)-1);
    }

    private synchronized void advance () {
        if (pixelMap == null || pixelMap.length == 0) return;
        float [] newPixelMap = new float[pixelMap.length];
        for (int i=1;i<pixelMap.length;i++) {
            newPixelMap[i-1] = pixelMap[i];
        }

        newPixelMap[newPixelMap.length-1] = addNoise();     

        pixelMap = newPixelMap;
    }

    public void run() {
        while (true) {
            advance();
            repaint();

            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {}

        }
    }

    private int getY (float height) {
        double proportion = (1-height)/2;
        return (int)(getHeight()*proportion);
    }

    public void paint (Graphics g) {

        if (screenBuffer == null || screenBuffer.getWidth(this) != getWidth() || screenBuffer.getHeight(this) != getHeight()) {
            screenBuffer = createImage(getWidth(), getHeight());
            bufferGraphics = screenBuffer.getGraphics();
        }

        if (pixelMap == null || getWidth() != pixelMap.length) {
            pixelMap = new float[getWidth()];
        }

        bufferGraphics.setColor(Color.BLACK);

        bufferGraphics.fillRect(0, 0, getWidth(), getHeight());

        bufferGraphics.setColor(Color.GREEN);

        int lastX = 0;
        int lastY = getHeight()/2;

        for (int x=0;x<pixelMap.length;x++) {
            int y = getY(pixelMap[x]);
            bufferGraphics.drawLine(lastX, lastY, x, y);
            lastX = x;
            lastY = y;
        }

        g.drawImage(screenBuffer, 0, 0, this);
    }

    public void update (Graphics g) {
        paint(g);
    }

    public static void main (String [] args) {
        JFrame frame = new JFrame("Flicker test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new FlickerPanel());
        frame.setSize(500,300);
        frame.setVisible(true);
    }


}
like image 849
Simon Andrews Avatar asked Mar 25 '12 09:03

Simon Andrews


2 Answers

  1. In a JPanel, override paintComponent(Graphics) rather than paint(Graphics)
  2. Instead of calling Thread.sleep(n) implement a Swing Timer for repeating tasks or a SwingWorker for long running tasks. See Concurrency in Swing for more details.
like image 159
Andrew Thompson Avatar answered Nov 12 '22 04:11

Andrew Thompson


JPanel is double buffered by default, and "Swing programs should override paintComponent() instead of overriding paint()."—Painting in AWT and Swing: The Paint Methods. Contrast this example that overrides paintComponent() with this example that overrides paint().

Addendum: The shearing is caused by creating and copying an entire image on each update. Instead, accumulate points on a GeneralPath and draw() the Shape on each iteration, as shown here.

like image 44
trashgod Avatar answered Nov 12 '22 05:11

trashgod