views:

387

answers:

3

I'm writing an application which reads and displays images as ImageIcons (within a JLabel), the application needs to be able to support jpegs and bitmaps.

For jpegs I find that passing the filename directly to the ImageIcon constructor works fine (even for displaying two large jpegs), however if I use ImageIO.read to get the image and then pass the image to the ImageIcon constructor, I get an OutOfMemoryError( Java Heap Space ) when the second image is read (using the same images as before).

For bitmaps, if I try to read by passing the filename to ImageIcon, nothing is displayed, however by reading the image with ImageIO.read and then using this image in the ImageIcon constructor works fine.

I understand from reading other forum posts that the reason that the two methods don't work the same for the different formats is down to java's compatability issues with bitmaps, however is there a way around my problem so that I can use the same method for both bitmaps and jpegs without an OutOfMemoryError?

(I would like to avoid having to increase the heap size if possible!)

The OutOfMemoryError is triggered by this line:

img = getFileContentsAsImage(file); 

and the method definition is:

public static BufferedImage getFileContentsAsImage(File file) throws FileNotFoundException { 
  BufferedImage img = null; 
  try { 
    ImageIO.setUseCache(false); 
    img = ImageIO.read(file); 
    img.flush(); 
  } catch (IOException ex) { 
    //log error 
  } 
return img; 
}

The stack trace is:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:58)
        at java.awt.image.ComponentSampleModel.createDataBuffer(ComponentSampleModel.java:397)
        at java.awt.image.Raster.createWritableRaster(Raster.java:938)
        at javax.imageio.ImageTypeSpecifier.createBufferedImage(ImageTypeSpecifier.java:1056)
        at javax.imageio.ImageReader.getDestination(ImageReader.java:2879)
        at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:925)
        at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:897)
        at javax.imageio.ImageIO.read(ImageIO.java:1422)
        at javax.imageio.ImageIO.read(ImageIO.java:1282)
        at framework.FileUtils.getFileContentsAsImage(FileUtils.java:33)
A: 

Have you tried this?

ImageIcon im = new ImageIcon(Toolkit.getDefaultToolkit().createImage("filename"));
Denis
11helen
A: 

It couldn't be that you indeed just run out of memory? I mean, does the error still occur if you run java with, say, -Xmx1g ?

Thomas
+2  A: 

You are running out of memory because ImageIO.read() returns an uncompressed BufferedImage which is very large and is retained in the heap because it is referenced by the ImageIcon. However, the images returned by Toolkit.createImage remain in their compressed format (using the private ByteArrayImageSource class.)

You cannot read a BMP using Toolkit.createImage (and even if you could it would still remain uncompressed in memory and you would probably run out of heap space again) but what you can do is read the uncompressed image and save it in a byte array in compressed form, e.g.

public static ImageIcon getPNGIconFromFile(File file) throws IOException {
    BufferedImage bitmap = ImageIO.read(file);
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    ImageIO.write(bitmap, "PNG", bytes);
    return new ImageIcon(bytes.toByteArray());
}

That way the only time the uncompressed bitmap must be held in memory is when it is being loaded or rendered.

finnw
Brilliant! This works perfectly. Just out of interest why does this method only work by using "PNG" as the formatName when using a bitmap file?
11helen
It needs to be a format that is readable by the Toolkit and there needs to be an ImageIO plugin for it. GIF will probably work (but is limited to 8bpp so the quality may be reduced.) JPEG will also work (but works best for photographic images, which were probably in JPEG format already so it would be redundant.)
finnw