Animation flicker
I am developing an applet that contains as its background an animation sequence. For the animation I am using a series of 20 images that contain a random black/white dot pattern. The images are displayed in rapid random succession to give the appearance of "static".
The problem I am encountering is that I am unable to eliminate a flicker that occurs as the images are switched. The flicker occurs regardless of the speed of the animation. At very slow speeds I believe I can detect the screen being cleared between images, even though I am overriding the update() method! I would appreciate any suggestions on how to improve the code. Thanks!
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
publicclass Animationextends JApplet{
int row, col;// current row/col number
int newColor;
staticint gridw;// applet width
staticint gridh;// applet height
staticint r = 20;// number of bufferimages
byte [] fPixels =null;
BufferedImage [] bimage =new BufferedImage[r];
Image theimage;
RenderDots renderdots;
int snooze = 30;
publicvoid init(){
setSize(800,600);
Rectangle bounds = getBounds();// applet size
gridw = bounds.width;// number of cols
gridh = bounds.height;// number of rows
// create buffered images
for (int i = 0; i < r; i++){
int z = 0;
fPixels =newbyte [gridw * gridh];
for ( row = 0; row < gridh; row++ ){
for ( col = 0; col < gridw; col++){
int rn = (int) (Math.random() * 2);
newColor = ( rn == 0 ) ? 255 : 0;
if (row >=0 && row < gridh && col >= 0 && col < gridw){
fPixels[z++] = (byte) newColor;
}
}
}
bimage[i] =new BufferedImage (gridw, gridh, BufferedImage.TYPE_BYTE_GRAY);
WritableRaster wr = bimage[i].getRaster();
wr.setDataElements (0,0, gridw, gridh, fPixels);
}
}// end init
publicvoid start(){
renderdots =new RenderDots();
renderdots.start();
}
class RenderDotsextends Thread{
privatevolatileboolean stop =false;
publicvoid requestStop(){ stop =true;}
publicvoid run(){
int curImg = 0;
int i = 0;
while (!stop){
theimage = bimage[i];
curImg = i;
repaint();
getToolkit().sync();
while(i == curImg){
i = (int)(Math.random() * r);
}
try{
Thread.sleep(snooze);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}// end run
}// end RenderDots class
publicvoid update(Graphics g){
paint(g);
}
publicvoid paint(Graphics g){
g.drawImage(theimage, 0, 0,this);
}
publicvoid stop(){
if(renderdots !=null){
renderdots =null;
}
}// end stop
}///:~
[6369 byte] By [
rhodopsina] at [2007-11-27 11:52:50]

You don't need to do double buffering.
Write a custom drawing-panel class that extends JPanel and properly override the paintComponent() method. Swing component should not flicker much. From your animation controller thread, for which javax.swing.Timer would be handy, call your dPanel.repaint() with appropriate interval after updating drawing parameters for the paintComponent() at each iteration of repaint() call.
Your JApplet should do:
MyDrawingPanel dPanel = new MyDrawingPanel();
getContentPane().add(dPanel, --layout constraints--);
Message was edited by:
hiwa
hiwaa at 2007-7-29 18:46:53 >

ah, all i saw was the Applet i didnt know he was using swing.
jpanels are as you said already double buffered.
Also, you may not need to prepare multiple images before hand if you could use drawing parameters properly at each repaint() call.
hiwaa at 2007-7-29 18:46:53 >

I don't think you should use paint() but rather paintComponent. Also, you shouldn't call paint or paintcomponent directly. you can repaint if you need the app to call paintComponent. Should you be doing your graphics on a panel rather than right on the japplet?
Message was edited by:
petes1234
> ah, all i saw was the Applet i didnt know he was using swing.
Which is why I continually bug people to post Swing related questions in the Swing forum, so we don't waste time guessing what the OP is using.
The techniques used by the OP are all wrong for Swing applications. This code is based on an old AWT applicaton and is not applicable for Swing. For example:
a) there is no need to override the update() method
b) there is not need to override the paint() method. This is probably the cause of the problem since super.paint() is never invoked which I believe means you lose the double buffering and paint optimizations used by Swing.
As mentioned above overriding the paintComponent() method of a JComponent is the way to go.
For e
Thanks to everyone for their constructive comments. I believe that I have implemented every suggestion.
Unfortunately, there has not been any noticeable improvement in the flicker. I have posted the applet at the below link so you may see exactly what I am talking about. I have slowed the animation down to make the flicker behavior more discernible.
http://richardtrevino.net/animation.html
I am beginning to wonder if there is something unusual about my applet that is making it unusually difficult to eliminate the flicker. Could it be, for example, that my images are too large (800x600) to achieve a smooth animation effect?
Please let me know if I have incorrectly implemented the suggested changes, or if you have any other suggestions on how to eliminate the flicker. Thanks.
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Animation extends JApplet implements ActionListener {
int row, col;// current row/col number
int i = 0;
int curImg = 0;
int newColor;
static int gridw; // applet width
static int gridh; // applet height
static int r = 20; // number of bufferimages
byte [] fPixels = null;
BufferedImage [] bimage = new BufferedImage[r];
Image theimage;
private MyJPanel pane = new MyJPanel();
Timer timer;
int snooze = 300;
public void init() {
getContentPane().add(pane, BorderLayout.CENTER);
setSize(800,600);
Rectangle bounds = getBounds(); // applet size
gridw = bounds.width;// number of cols
gridh = bounds.height;// number of rows
// create buffered images
for (int i = 0; i < r; i++) {
int z = 0;
fPixels = new byte [gridw * gridh];
for ( row = 0; row < gridh; row++ ) {
for ( col = 0; col < gridw; col++) {
int rn = (int) (Math.random() * 2);
newColor = ( rn == 0 ) ? 255 : 0;
if (row >=0 && row < gridh && col >= 0 && col < gridw) {
fPixels[z++] = (byte) newColor;
}
}
}
bimage[i] = new BufferedImage (gridw, gridh, BufferedImage.TYPE_BYTE_GRAY);
WritableRaster wr = bimage[i].getRaster();
wr.setDataElements (0,0, gridw, gridh, fPixels);
}
} // end init
public void start() {
timer = new Timer(snooze, this);
timer.start();
}
public void actionPerformed(ActionEvent e) {
while(i == curImg){
i = (int)(Math.random() * r);
}
theimage = bimage[i];
curImg = i;
pane.repaint();
getToolkit().sync();
} // end actionPerformed
class MyJPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(theimage, 0, 0, this);
}
} // end MyJPanel
public void stop() {
timer.stop();
} // end stop
} ///:~
We see no noticeable flicker on your applet loaded from the URL mentioned. And it should not because it is a standard, well-known and well-tried technique for simple animation on Swing. But frankly, your applet, randomizing each single pixel on a modern hi-reso screen, is not beautiful, not impressive nor particulary interesting at all. It's rather a sheer boredom, so to say.
hiwaa at 2007-7-29 18:46:53 >

Thank you hiwa. Your advice has been very helpful.
I agree the static pattern is not very beautiful, but that is not the intent. It is part of a project to design an online vision test for patients with an eye disease called macular degeneration.
After some testing it appears the flicker is more apparent on some monitors than others. Not sure why that is, perhaps refresh rate?
Could you elaborate on your comment regarding not needing to prepare multiple images before hand if I use drawing parameters properly at each repaint() call.
Thanks again.
Using prepared images and g.drawImage() is a typical anti-flickering technique even in the AWT era. Since you are using a Swing component that does default double-buffering, noticeable flicker should not occure even if you do the direct drawing without using prepared images -- that is our years-long experience in Swing animation development. But when you do a pixel-by-pixel painting over large area, i.e. loooots of pixels one by one, you will need a very fast machine to do direct drawing animation. Try this code:
public void actionPerformed(ActionEvent e) {
// generally, you do complex parameters update here
// and paintComponent() use them
pane.repaint();
}
-
class MyJPanel extends JPanel {
Color color;
public void paintComponent(Graphics g) {
super.paintComponent(g);
int wid = getWidth();
int hei = getHeight();
for (int w = 0; w < wid; ++w){
for (int h = 0; h < hei; ++h){
color = Math.random() > 0.5 ? Color.black : Color.white;
g.setColor(color);
g.fillRect(w, h, 1, 1);
}
}
}
}
Message was edited by:
hiwa
hiwaa at 2007-7-29 18:46:53 >

