-
Bug
-
Resolution: Fixed
-
P4
-
1.4.1, 1.4.2
-
tiger
-
x86
-
linux, windows_2000
Name: jk109818 Date: 03/04/2003
FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_02-b06, mixed mode)
FULL OS VERSION :
Linux Mandrake 2.4.8-26mdk
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
From the API Doc for javax.swing.ImageIcon:
"Images that are created from a URL or filename are preloaded using MediaTracker to monitor the loaded state of the image."
Even though it is not explicitly said, this is true also for the images created from an array of bytes, as one can see from the source code. Generally speaking, ImageIcon users relies on the fact that the image production process is complete when they acquire an ImageIcon object without error.
But the readObject method of ImageIcon creates an image from the deserialized pixels, without calling the loadImage() protected method with would complete the image production. As a result, a component that relies on the completeness of the image, such as a JLabel that is not showing (a DefaultCellRenderer using ImageIcon, for instance) will not be able to draw the image. On the other hand, this problem shows up in very peculiar conditions, because the default ImageObserver implementation of Component will suffice to trigger the image production on the very first attempt to paint the icon. This is not the case of JLabel, because its overridden imageUpdate() method will stop the image production process returning false if the component is not showing.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a JFrame with a JList and a JLabel attached.
Create an ImageIcon and add it to the model of a JList keeping the default cell renderer.
Serialize the ImageIcon object, retrieve from the storage and add it to the JList model.
Retrieve ANOTHER copy from the storage and set it as icon for the JLabel.
EXPECTED VERSUS ACTUAL BEHAVIOR :
I expect to see a JFrame with three identical images on it:
two displaying in a JList, and one displaying on a JLabel.
I can see only two images, one in the JList and one in the JLabel.
The missing image is the one I serialized and added to the list model.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
public class IISerTest extends JFrame {
public static void main(String[] args) {
new IISerTest().show();
}
JLabel lbl;
Container lblsBox;
JList lst;
DefaultListModel lstMod;
AbstractAction aaTest, aaWA1, aaWA2;
public IISerTest() {
super("IconImage Serialization test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
ImageIcon icon=buildIcon();
Container c=getContentPane();
lstMod=new DefaultListModel();
lstMod.addElement(icon);
c.add(new JScrollPane(lst=new JList(lstMod)),
BorderLayout.CENTER);
lst.setCellRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(
JList l, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(
l,value,index,isSelected,cellHasFocus);
setText(value.toString());
return this;
}
});
c.add(lbl=new JLabel(icon.toString(),icon,JLabel.LEADING),
BorderLayout.SOUTH);
aaTest=new AbstractAction("Test") {
public void actionPerformed(ActionEvent ae) {
test();
}
};
aaWA1=new AbstractAction("Workaround 1") {
public void actionPerformed(ActionEvent ae) {
workAround(false);
}
};
aaWA2=new AbstractAction("Workaround 2") {
public void actionPerformed(ActionEvent ae) {
workAround(true);
}
};
setJMenuBar(new JMenuBar() { { add(new JMenu("Test") {
{ add(aaTest); add(aaWA1); add(aaWA2); }
} ); } } );
c.add(new JToolBar() {
{ add(aaTest); add(aaWA1); add(aaWA2); }
}, BorderLayout.NORTH);
setSize(500,300);
validate();
}
synchronized void test() {
ImageIcon ii=buildIcon();
lbl.setText(ii.toString());
lbl.setIcon(saveAndLoad(ii));
// it works: this label is showing...
lstMod.add(0,saveAndLoad(ii));
// it DOESN'T work: layout is OK but image is not displayed
validate();
}
synchronized void workAround(boolean forceLoad) {
test();
ImageIcon ii=(ImageIcon)lstMod.get(0);
if(forceLoad) {
ii.setImage(ii.getImage());
// this forces protected loadImage();
} else {
ii.setImageObserver(lst);
// this bypasses default observer=hidden ListCellRenderer
}
validate();
// it eventually works
}
ImageIcon saveAndLoad(ImageIcon ii) {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
try {
ObjectOutputStream out=new ObjectOutputStream(baos);
out.writeObject(ii);
out.flush();
ObjectInputStream in=new ObjectInputStream(
new ByteArrayInputStream(baos.toByteArray())
);
ii=(ImageIcon)in.readObject();
} catch(Exception ex) {
ex.printStackTrace();
}
return ii;
}
ImageIcon buildIcon() {
int w=32,h=32;
float halfW=w/2 , halfH=h/2;
int col=(int)(Math.random()*0xffffff);
int[] pixels=new int[w*h];
for(int y=0; y<h; y++) {
for(int x=0; x<w; x++) {
float cx=1f-(float)x/halfW;
float cy=1f-(float)(y/halfH);
double ray=Math.sqrt(cx*cx+cy*cy);
pixels[y*w+x]=ray<1?col| (255-(int)(ray*255)) << 24:0;
}
}
return new ImageIcon(
createImage(new MemoryImageSource(w, h, pixels, 0, w)),
"Color #"+Integer.toHexString(col));
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
There are several ways to complete the production of the image.
The easiest two:
1.
Force the internal call to loadImage(), for instance with something like
myIconImage.setImage(myIcon.getImage());
2.
If you are using a ListCellRenderer, use the JList as ImageObserver for the IconImage:
myIconImage.setImageObserver(myJList);
(Review ID: 182007)
======================================================================
- duplicates
-
JDK-4906099 1.4.1 REGRESSION: Deserialized ImageIcons don't display when used in JTrees
-
- Closed
-