Really interesting graphics/image problem
I'm working on a project where I have to have some scrolling text
displayed. I wrote a component that does just that and it works fine.
I have that component on a JPanel that has a BufferedImage drawn on it.
I'm getting some performance problems depending on the type of image
file that I load the image from. I created three gray 1600x1200 pixel
images by creating a BufferedImage with type INT_RGB, getting a graphics
and doing a fillRect on the whole image with Color.GRAY. Then I wrote
this BufferedImage to three different files, a JPG, a GIF and a PNG.
When playing with my application I discovered that using either the GIF
or PNG image caused the program to use considerably more processor. The
test computer is a 3Ghz Celeron running Win XP. I have the same issues
on my desktop computer which is a 3Ghz Pentium D but not to the same
extent. It doesn't matter if the graphics card is configured for 16bit
or 32bit color.
I am sorry that the test program below is so large but it is the minimum
to demonstrate the problem. The images download from my web site and
are less than 30k bytes so they will load pretty fast. You can get the
source files from there too, all via HTTP. When the program first
starts the ImagePanel does not yet have an image loaded and the
scrolling characters will overwrite each other. Just press one of the
buttons to draw the gray background and it will look just fine. On my
fast desktop I don't see a performance hit when I use the GIF image,
just the PNG image. On the test computer the GIF shows a definite
increase in processor. My Pentium D desktop shows about 50% on each
processor when it is using the PNG image.
I don't know a lot about BufferedImages and I don't know if there is a
difference in the BufferedImage depending on what format of a file it
was loaded from. I can only guess that this is caused by some
ColorModel incompatibility. I do know that the problem is related to
the component being transparent. Try out the Toggle Opaque button to
see the difference.
I might not have ever seen this but the app I'm working on had to draw
over a mottled background and I needed to set the component to
transparent to see what was behind.
Thanks very much for looking.
knute...
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import javax.imageio.*;
import javax.swing.*;
public class test extends JFrame implements ActionListener {
BufferedImage bi1,bi2,bi3;
ImagePanel ip;
HorizontalScrollingJPanel hsjp;
public test() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
URL url = new URL(
"http://www.knutejohnson.com/images/gray.jpg");
bi1 = ImageIO.read(url);
url = new URL(
"http://www.knutejohnson.com/images/gray.gif");
bi2 = ImageIO.read(url);
url = new URL(
"http://www.knutejohnson.com/images/gray.png");
bi3 = ImageIO.read(url);
} catch (Exception e) {
e.printStackTrace();
}
ip = new ImagePanel();
ip.setLayout(null);
hsjp = new HorizontalScrollingJPanel();
hsjp.setOpaque(false);
hsjp.setLocation(20,80);
hsjp.setSize(660,60);
hsjp.setFont(new Font("Arial",Font.PLAIN,42));
hsjp.setVerticalAdjust(-4);
hsjp.setForeground(Color.BLUE);
hsjp.setBackground(Color.BLACK);
hsjp.setText("Now is the time for all good men to come" +
" to the aid of their country. ");
hsjp.setScroll(true);
ip.add(hsjp);
add(ip,BorderLayout.CENTER);
JPanel p = new JPanel();
JButton b = new JButton("JPG");
b.addActionListener(this);
p.add(b);
b = new JButton("GIF");
b.addActionListener(this);
p.add(b);
b = new JButton("PNG");
b.addActionListener(this);
p.add(b);
b = new JButton("Toggle Opaque");
b.addActionListener(this);
p.add(b);
add(p,BorderLayout.SOUTH);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent ae) {
String ac = ae.getActionCommand();
if (ac.equals("JPG")) {
ip.setImage(bi1);
} else if (ac.equals("GIF")) {
ip.setImage(bi2);
} else if (ac.equals("PNG")) {
ip.setImage(bi3);
} else if (ac.equals("Toggle Opaque")) {
hsjp.setOpaque(!hsjp.isOpaque());
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
new test();
}
};
EventQueue.invokeLater(r);
}
class ImagePanel extends JPanel {
BufferedImage image;
public ImagePanel() {
setPreferredSize(new Dimension(700,300));
}
public void setImage(BufferedImage bi) {
image = bi;
repaint();
}
public void paintComponent(Graphics g) {
if (image == null)
g.drawString("Image Not Set",20,180);
else
g.drawImage(image,0,0,null);
}
}
}
**********************************************************
//package com.knutejohnson.components;
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class HorizontalScrollingJPanel extends JPanel {
private static final int RATE = 33;
private boolean scroll;
private int verticalAdjust;
private String text = "Text Not Set";
private int pixel;
private java.util.Timer timer = new java.util.Timer(true);
private TimerTask task;
public HorizontalScrollingJPanel() {
super(true); // double buffer
}
public void start() {
// cancel any running task
stop();
// create new task
task = new TimerTask() {
public void run() {
repaint();
}
};
// schedule it to run
timer.schedule(task,10,RATE);
}
public void stop() {
if (task != null)
task.cancel();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
pixel = 0;
repaint();
}
public void setScroll(boolean scroll) {
pixel = 0;
this.scroll = scroll;
if (scroll) {
start();
} else {
stop();
repaint();
}
}
public boolean getScroll() {
return scroll;
}
public void setVerticalAdjust(int pixels) {
verticalAdjust = pixels;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
FontMetrics fm = g2D.getFontMetrics();
int length = fm.stringWidth(text);
int space =
Math.max(getWidth() - length, fm.stringWidth(" "));
int height = fm.getHeight();
if (scroll) {
g2D.drawString(text,pixel,height + verticalAdjust);
g2D.drawString(text,pixel + length + space,
height + verticalAdjust);
if (--pixel <= -length - space)
pixel = 0;
} else {
g2D.drawString(text,(getWidth() - length) / 2,
height + verticalAdjust);
}
}
}
--
Knute Johnson
email s/nospam/knute/