Java: чтение изображений и отображение в виде ImageIcon - PullRequest
7 голосов
/ 30 марта 2010

Я пишу приложение, которое читает и отображает изображения в виде ImageIcons (внутри JLabel), приложение должно иметь возможность поддерживать JPEG и растровые изображения.

Для jpegs я нахожу, что передача имени файла непосредственно в конструктор ImageIcon работает нормально (даже для отображения двух больших jpegs), однако если я использую ImageIO.read, чтобы получить изображение, а затем передать изображение в конструктор ImageIcon, я получаю OutOfMemoryError (пространство кучи Java) при чтении второго изображения (с использованием тех же изображений, что и раньше).

Для растровых изображений, если я пытаюсь прочитать, передавая имя файла в ImageIcon, ничего не отображается, однако при чтении изображения с помощью ImageIO.read и последующем использовании этого изображения в конструкторе ImageIcon работает нормально.

Из прочтения других сообщений на форуме я понимаю, что причина того, что два метода не работают одинаково для разных форматов, заключается в проблемах совместимости java с растровыми изображениями, однако есть способ обойти мою проблему, чтобы я мог использовать один и тот же метод для растровых изображений и jpegs без OutOfMemoryError?

(Я бы хотел избежать увеличения размера кучи, если это возможно!)

OutOfMemoryError вызывается этой строкой:

img = getFileContentsAsImage(file); 

и определение метода:

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; 
}

Трассировка стека:

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)

Ответы [ 3 ]

3 голосов
/ 30 марта 2010

Недостаточно памяти, потому что ImageIO.read() возвращает несжатый BufferedImage, который очень большой и сохраняется в куче, потому что на него ссылается ImageIcon. Однако изображения, возвращаемые Toolkit.createImage, остаются в сжатом формате (используя закрытый класс ByteArrayImageSource.)

Вы не можете прочитать BMP, используя Toolkit.createImage (и даже если бы вы могли, он все равно остался бы несжатым в памяти, и вам, вероятно, снова бы не хватило места в куче), но вы можете прочитать несжатый образ и сохранить его байтовый массив в сжатом виде, например

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());
}

Таким образом, единственный раз, когда несжатый битовый массив должен храниться в памяти, это когда он загружается или отображается.

0 голосов
/ 30 марта 2010

Неужели у вас действительно просто не хватает памяти? Я имею в виду, ошибка все еще происходит, если вы запускаете Java с, скажем, -Xmx1g?

0 голосов
/ 30 марта 2010

Вы пробовали это?

ImageIcon im = new ImageIcon(Toolkit.getDefaultToolkit().createImage("filename"));
...