Drawing in applets. Tell me why this way is wrong.
CHECK MY LAST POST FOR A BETTER CODE EXAMPLE
Hello,
I tried to draw a game on an applet and got huge flickering. The way I am drawing is:
I create an off-screen image of applet size, get its graphics, draw the objects on it I want to display with drawMaze and drawScore methods and in the end draw that off-screen image on the applet.
Code java:
public void paint(Graphics g) {
super.paint(g);
if(gOff == null) {
gOffImage = createImage(d.width, d.height);
gOff = gOffImage.getGraphics();
}
gOff.setColor(Color.BLACK);
gOff.fillRect(0, 0, d.width, d.height);
drawMaze();
drawScore();
g.drawImage(gOffImage, 0, 0, this);
}
The program updates the image with thread. I simply put repaint() in the thread and sleep for a particual delay time. This way I get very big flickering.
Code java:
public void run() {
long timeBefore = System.currentTimeMillis();
Thread thread2 = Thread.currentThread();
while(thread == thread2) {
repaint();
long timeDifference = System.currentTimeMillis() - timeBefore;
int sleep = gameDelay - (int)timeDifference;
if(sleep < 0) sleep = 2;
try {
thread.sleep(sleep);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
timeBefore = System.currentTimeMillis();
}
}
After looking at some examples I notice another way of "repaint'ing", which looks like this:
Code java:
public void run() {
Graphics g = getGraphics();
long timeBefore = System.currentTimeMillis();
Thread thread2 = Thread.currentThread();
while(thread == thread2) {
paint(g);
long timeDifference = System.currentTimeMillis() - timeBefore;
int sleep = gameDelay - (int)timeDifference;
if(sleep < 0) sleep = 2;
try {
thread.sleep(gameDelay);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
timeBefore = System.currentTimeMillis();
}
}
I tried it and it worked. No flickering at all.
So I want to understand: why this way there is no flickering? What is the difference between calling repaint() and paint(g) methods? Why repaint() produces flickering and paint(g) not? Doesn't repaint() just simply calls paint() method?
Re: Drawing in applets. Tell me why this way is wrong.
I don't know why it does what it does.
Can you create a small program that demos the problem and post it here?
Could it be that the sleep duration is different in the two pieces of code you posted.
What is the difference in values between the variables: sleep and gameDelay
Re: Drawing in applets. Tell me why this way is wrong.
The gameDelay is 40. I tryed to put System.out.println(sleep) and it showed most of the time 30+. But it is interesting that if I type repaint() instead of paint(g), sleep is always equals to gameDelay - 40, but produces the flickering. The full code consists of nearly 1000 lines, so I don't think that you want to look at all this :)
The thing is, lets say in the run method:
Code java:
public void run() {
Graphics g = getGraphics();
long timeBefore = System.currentTimeMillis();
Thread thread2 = Thread.currentThread();
while(thread == thread2) {
paint(g);
long timeDifference = System.currentTimeMillis() - timeBefore;
int sleep = gameDelay - (int)timeDifference;
if(sleep < 0) sleep = 2;
try {
thread.sleep(sleep);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
timeBefore = System.currentTimeMillis();
}
}
It is enough to replace paint(g) with update(g) or repaint() and I get flickering. So I want to understand why is so? Sleep time has nothing to do with that. I can set it to 1 and with paint(g) in the run method I will see totally smooth animations with 0 flickering, only 40 times faster. But if I change to repaint(), with 1 delay I could hardly see anything in the applet. Huge flickering only.
Re: Drawing in applets. Tell me why this way is wrong.
I'd say it has to do with two things. First, you are directly accessing the Graphics object from another thread, and drawing to that graphics, in effect asynchronously drawing the GUI, while the system is doing its own drawing using the single threaded model, you are stepping in and possibly disrupting that (according to the API, getGraphics 'creates' a Graphics context, which may also result in the creation of additional graphics objects for the component being divided between what the component is drawing, and what you are drawing to) Second, swing components are double buffered by default. Calling repaint takes advantage of this (regardless of whether you are drawing offscreen - see point 1).
Re: Drawing in applets. Tell me why this way is wrong.
Can you create asmall program that demos the problem and post it here?
Re: Drawing in applets. Tell me why this way is wrong.
Quote:
Originally Posted by
copeg
I'd say it has to do with two things. First, you are directly accessing the Graphics object from another thread, and drawing to that graphics, in effect asynchronously drawing the GUI, while the system is doing its own drawing using the single threaded model, you are stepping in and possibly disrupting that. Second, swing components are double buffered by default. Calling repaint takes advantage of this (regardles of whether you are drawing offscreen - see point 1).
1. There is one active thread only. The one I created in the run method is just the way to stop the main one since thread.stop() method is deprecated.
2. This is applet. I use AWT, not Swing.
But what I have noticed is that repaint() happens immediately while calling paint(g) takes nearly 10ms.
Furthermore, what I read in java.sun website:
Code :
repaint():
If this component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.
So I guess in my case it calls update(g) method, which gives me flickering.
Re: Drawing in applets. Tell me why this way is wrong.
a) See the edit to my post about calling getGraphics
b) What do you mean there is only one thread? Does this include the EDT? Are you calling Thread.start at any time during your program? As norm stated, a short program to reproduce this would easily answer all these questions
Re: Drawing in applets. Tell me why this way is wrong.
Quote:
Originally Posted by
Norm
Can you create asmall program that demos the problem and post it here?
Ok, I made the mini demo program the same way I did my game. The result is the same: if in run method I call repaint() - i get huge flickering. And if I call paint(g) - I get smooth applet without any flickering even without sleep.
Code java:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.Graphics;
public class Demo extends Applet implements Runnable {
Graphics gOff;
Image image;
Thread thread;
Dimension d;
public void init() {
d = getSize();
image = createImage(d.width, d.height);
gOff = image.getGraphics();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
gOff.setColor(Color.BLACK);
gOff.fillRect(0, 0, d.width, d.height);
g.drawImage(image, 0, 0, this);
}
public void run() {
Graphics g = getGraphics();
while(true) {
// update(g); //huge flickering
// repaint(); //huge flickering
paint(g); //none flickering
}
}
}
The main reason I am asking it is that I have always used repaint() and first time encounter the problem, even though in my eyes these two ways of repainting are exactly the same. I want to know what difference is there? Where exactly that flickering comes from? I am wondering perhaps to call paint(g) is better instead of repaint() in all cases?
EDIT: I have read that when update methods is called, first it clears the component by filling it with the background color, then sets the color of the graphics context to be the foreground color of the component and finally calls the component's paint method to completely redraw this component. So I guess If I don't want/need to clear the existing draws, simply call paint(g)?
Re: Drawing in applets. Tell me why this way is wrong.
I actually misread your original post to think you were saying the opposite. Sorry bout that. Now that you posted the small example its more clear, however I don't see any flickering with any of the calls (using appletviewer).
Re: Drawing in applets. Tell me why this way is wrong.
Hmm, that is really strange. I managed to make a screen how my screen flickers when repaint is being called. As I said, in my case repaint() calls the method update(), which first clears the component by filling it with the background color, then sets the color of the graphics context to be the foreground color of the component and finally calls the component's paint method to completely redraw this component. So I guess it depends on performance of the PC.
http://img831.imageshack.us/img831/506/flick.png