Creating a "movie" in Swing.

A

Aaron Fude

Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

public class VideoPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Image img = myImages[myCurrentFrame];
int w = img.getWidth(this);
int h = img.getHeight(this);
g.drawImage(img, 0, 0, w, h, 0, 0, w, h, this);
}
}


My Thread class is this:

public class Animate extends Thread {
public void run() {
while (true) {
myVideoPanel.repaint();
myCurrentFrame = (myCurrentFrame +1)%myImages.length;
try {
sleep(1000/15);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
 
K

Knute Johnson

Aaron said:
Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

public class VideoPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Image img = myImages[myCurrentFrame];
int w = img.getWidth(this);
int h = img.getHeight(this);
g.drawImage(img, 0, 0, w, h, 0, 0, w, h, this);
}
}


My Thread class is this:

public class Animate extends Thread {
public void run() {
while (true) {
myVideoPanel.repaint();
myCurrentFrame = (myCurrentFrame +1)%myImages.length;
try {
sleep(1000/15);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}

Well you have to be sure that your computer can run that fast with
images of that size. If you think it can then there are some things you
can do to prevent the write coalescing with repaint(). First don't use
repaint() because it will write coalesce. Call paintImmediately() from
the EDT.

public void run() {
while (true) {
try {
Thread.sleep(66);
} catch (InterruptedException ie) { }

// increment counter or whatever you are using to point
// to the next picture

try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
paintImmediately(0,0,getWidth(),getHeight());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

Use paintComponent() instead of paint() to do your drawing on Swing
components. If you are drawing the whole component or redrawing the
complete background I would suggest that you NOT call
super.paintComponent().

There are numerous methods to get faster drawing, clipping, active
rendering, using a BufferStrategy or a VolatileImage. The newer the JRE
you use the better video performance you are going to get. Also, video
cards play a big role. Windows is much faster than Linux as well.

Another thing to keep in mind is that 150 images of any significant size
will use a lot of memory.
 
J

John B. Matthews

Knute Johnson said:
Aaron said:
Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

public class VideoPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Image img = myImages[myCurrentFrame];
int w = img.getWidth(this);
int h = img.getHeight(this);
g.drawImage(img, 0, 0, w, h, 0, 0, w, h, this);
}
}


My Thread class is this:

public class Animate extends Thread {
public void run() {
while (true) {
myVideoPanel.repaint();
myCurrentFrame = (myCurrentFrame +1)%myImages.length;
try {
sleep(1000/15);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}

Well you have to be sure that your computer can run that fast with
images of that size. If you think it can then there are some things you
can do to prevent the write coalescing with repaint(). First don't use
repaint() because it will write coalesce. Call paintImmediately() from
the EDT.

public void run() {
while (true) {
try {
Thread.sleep(66);
} catch (InterruptedException ie) { }

// increment counter or whatever you are using to point
// to the next picture

try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
paintImmediately(0,0,getWidth(),getHeight());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

Use paintComponent() instead of paint() to do your drawing on Swing
components. If you are drawing the whole component or redrawing the
complete background I would suggest that you NOT call
super.paintComponent().

There are numerous methods to get faster drawing, clipping, active
rendering, using a BufferStrategy or a VolatileImage. The newer the JRE
you use the better video performance you are going to get. Also, video
cards play a big role. Windows is much faster than Linux as well.

Another thing to keep in mind is that 150 images of any significant size
will use a lot of memory.

I'm Knute's student in this realm, but I'll venture two additions:

First, consider pre-scaling your BufferedImages to the size of the
destination; drawImage() won't have to scale and the observer parameter
can be null.

Second, consider javax.swing.Timer. It invokes the listener on the EDT
and degrades gracefully by coalescing if you overrun the interval on a
slower machine. Here's a simple example:

<http://sites.google.com/site/drjohnbmatthews/subway>

Of course, Knute's other optimizations (e.g. paintImmediately) would
still apply.
 
N

Nigel Wade

Aaron said:
Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

The method I use is to have SwingWorker handle the frame updates. Each frame is
displayed using JLabel.setImage(). To update the frame I display the image in a
method wrapped in EventQueue.InvokeAndWait() to ensure it's performed on the
EDT, and completed before the SwingWorker thread attempts to display the next
image. All the SwingWorker does is loop calling this method, and insert the
appropriate delay between each frame.

In my applet I manage to get about 20 frames per second using this method, but
my objectives are neither speed nor smoothness. Each frame is a 707x500 raw
ionogram, and all its intended to provide is an animated sequence of the data
for quickly assessing the ionosphere during any particular day.

If you want to see how it works there is an applet here:
http://www.ion.le.ac.uk/cgi-bin/ionogram/day_gifs.php?Latest=1
 
K

Knute Johnson

John said:
Knute Johnson said:
Aaron said:
Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

public class VideoPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Image img = myImages[myCurrentFrame];
int w = img.getWidth(this);
int h = img.getHeight(this);
g.drawImage(img, 0, 0, w, h, 0, 0, w, h, this);
}
}


My Thread class is this:

public class Animate extends Thread {
public void run() {
while (true) {
myVideoPanel.repaint();
myCurrentFrame = (myCurrentFrame +1)%myImages.length;
try {
sleep(1000/15);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
Well you have to be sure that your computer can run that fast with
images of that size. If you think it can then there are some things you
can do to prevent the write coalescing with repaint(). First don't use
repaint() because it will write coalesce. Call paintImmediately() from
the EDT.

public void run() {
while (true) {
try {
Thread.sleep(66);
} catch (InterruptedException ie) { }

// increment counter or whatever you are using to point
// to the next picture

try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
paintImmediately(0,0,getWidth(),getHeight());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

Use paintComponent() instead of paint() to do your drawing on Swing
components. If you are drawing the whole component or redrawing the
complete background I would suggest that you NOT call
super.paintComponent().

There are numerous methods to get faster drawing, clipping, active
rendering, using a BufferStrategy or a VolatileImage. The newer the JRE
you use the better video performance you are going to get. Also, video
cards play a big role. Windows is much faster than Linux as well.

Another thing to keep in mind is that 150 images of any significant size
will use a lot of memory.

I'm Knute's student in this realm, but I'll venture two additions:

First, consider pre-scaling your BufferedImages to the size of the
destination; drawImage() won't have to scale and the observer parameter
can be null.

Second, consider javax.swing.Timer. It invokes the listener on the EDT
and degrades gracefully by coalescing if you overrun the interval on a
slower machine. Here's a simple example:

<http://sites.google.com/site/drjohnbmatthews/subway>

Of course, Knute's other optimizations (e.g. paintImmediately) would
still apply.

John's suggestion of the javax.swing.Timer is a good one. It is usually
smoother than java.util.Timer and the code in the event listener is
already running on the EDT.

One of the things that can happen especially when you are redrawing the
whole component is that once it gets started coalescing it will
sometimes coalesce many more frames than you want. I think this is
caused partly by the overhead of the coalesce, while faster than drawing
it isn't free. Throw in some system overhead and you can get a
situation where it draws a couple of frames, skips several, draws a
couple more and skips again.

If you draw the images synchronously, with paintImmediately() for
example, on a slow computer the presentation will be slow but the whole
story will appear.

Remember that paintImediately() needs to be called from the EDT while
repaint() may be called from any thread.
 
K

Knute Johnson

Nigel said:
The method I use is to have SwingWorker handle the frame updates. Each frame is
displayed using JLabel.setImage(). To update the frame I display the image in a
method wrapped in EventQueue.InvokeAndWait() to ensure it's performed on the
EDT, and completed before the SwingWorker thread attempts to display the next
image. All the SwingWorker does is loop calling this method, and insert the
appropriate delay between each frame.

In my applet I manage to get about 20 frames per second using this method, but
my objectives are neither speed nor smoothness. Each frame is a 707x500 raw
ionogram, and all its intended to provide is an animated sequence of the data
for quickly assessing the ionosphere during any particular day.

If you want to see how it works there is an applet here:
http://www.ion.le.ac.uk/cgi-bin/ionogram/day_gifs.php?Latest=1

Nice applet Nigel. I'm curious how you get your soundings?
 
J

John B. Matthews

Knute Johnson said:
John said:
Knute Johnson said:
Aaron Fude wrote:
Hi,

I'm 100% sure that this is an FAQ, but I can't seem to find answer.

Let's say I have 150 BufferedImages and I want to spit them out to a
JPanel at a rate of 15fps so I can have 10 seconds of relatively
smooth animation. I have a class VideoPanel that you can see below and
a separate Thread that calls "repaint" on the VideoPanel 15 times a
second. Now, the VideoPanel actually repaints when it wants to, so I
get about 1 frame a second and I see about every 15th frame. What's
the right way to fix this?

public class VideoPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Image img = myImages[myCurrentFrame];
int w = img.getWidth(this);
int h = img.getHeight(this);
g.drawImage(img, 0, 0, w, h, 0, 0, w, h, this);
}
}


My Thread class is this:

public class Animate extends Thread {
public void run() {
while (true) {
myVideoPanel.repaint();
myCurrentFrame = (myCurrentFrame +1)%myImages.length;
try {
sleep(1000/15);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
Well you have to be sure that your computer can run that fast with
images of that size. If you think it can then there are some things you
can do to prevent the write coalescing with repaint(). First don't use
repaint() because it will write coalesce. Call paintImmediately() from
the EDT.

public void run() {
while (true) {
try {
Thread.sleep(66);
} catch (InterruptedException ie) { }

// increment counter or whatever you are using to point
// to the next picture

try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
paintImmediately(0,0,getWidth(),getHeight());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

Use paintComponent() instead of paint() to do your drawing on Swing
components. If you are drawing the whole component or redrawing the
complete background I would suggest that you NOT call
super.paintComponent().

There are numerous methods to get faster drawing, clipping, active
rendering, using a BufferStrategy or a VolatileImage. The newer the JRE
you use the better video performance you are going to get. Also, video
cards play a big role. Windows is much faster than Linux as well.

Another thing to keep in mind is that 150 images of any significant size
will use a lot of memory.

I'm Knute's student in this realm, but I'll venture two additions:

First, consider pre-scaling your BufferedImages to the size of the
destination; drawImage() won't have to scale and the observer parameter
can be null.

Second, consider javax.swing.Timer. It invokes the listener on the EDT
and degrades gracefully by coalescing if you overrun the interval on a
slower machine. Here's a simple example:

<http://sites.google.com/site/drjohnbmatthews/subway>

Of course, Knute's other optimizations (e.g. paintImmediately) would
still apply.

John's suggestion of the javax.swing.Timer is a good one. It is usually
smoother than java.util.Timer and the code in the event listener is
already running on the EDT.

One of the things that can happen especially when you are redrawing
the whole component is that once it gets started coalescing it will
sometimes coalesce many more frames than you want. I think this is
caused partly by the overhead of the coalesce, while faster than
drawing it isn't free. Throw in some system overhead and you can get
a situation where it draws a couple of frames, skips several, draws a
couple more and skips again.

If you draw the images synchronously, with paintImmediately() for
example, on a slow computer the presentation will be slow but the
whole story will appear.

Ah, thank you for clarifying this. Nothing is lost when I lose a frame
or two in my simple motion simulation; in contrast, a dropped frame in
Nigel's ionogram applet may be a significant extreme, lasting some
portion of the sampling interval.

No one-size-fits-all.
 
N

Nigel Wade

Knute Johnson wrote:

Nice applet Nigel. I'm curious how you get your soundings?

We have an ionosonde located on Svalbard, at our radar site near Longyearbyen
between the EISCAT site and mine 7. Normally it takes a sounding every 4 mins.
That data is transferred back to our web server, a jpg image is produced and
put on the web. The applet makes a CGI request to the web server which responds
with a ZIP file containing one days images. The "movie" just displays those
images in sequence.

The applet was developed to replace the QuickTime movie which we used to
produce. QT had the advantage over most other (all other?) movie formats in
that it allowed you to single step frames forward and backward and drag the
frame pointer to any location in the movie. Something which is not really
needed for movies, but is what we wanted for viewing data images. When we
switched from IRIX to Linux we lost the ability to convert a sequence of JPGs
to a QT movie. Hence the applet.

Unfortunately we have lost funding for both the radar and the ionosonde and the
service will cease next year (I think its next year). The UK govt. (or at least
its funded research councils) don't consider Solar Terrestrial physics to be of
any importance any more. After all why do we care about the Sun-Earth energy
budget or understanding the onset of geomagnetic storms which can wipe out
satellites like GPS? They think the tax payer who funds the research is much
more interested in finding the Higgs Boson, because that's going to
revolutionise their lives. But that's another story...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top