Почему ImageIO не читает файл BMP, пока он не будет повторно сохранен в MS Paint?

у меня есть растровый файл, test3.bmp, который я могу просматривать и редактировать с помощью каждого средства просмотра изображений, с которым я тестировал.

тем не менее, я не могу прочитать его в своем приложении Java. Если я редактирую BMP в MS Paint, сохраните его, отмените изменение и сохраните его (test3_resaved.bmp), у меня такое же изображение, но с другим размером файла. Разные размеры файлов меня не волнуют... что делает, так это то, что мое приложение может прочитать повторно сохраненный файл.

может ли кто-нибудь просветить меня о том, почему работает одно изображение с моим кодом, но другой-нет?

изображения файлов:

вот минимальное тестовое приложение:

package Test;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Test extends JFrame {
    private ImageIcon imageIcon;

    public Test(String filename) throws IOException {
        super();
        BufferedImage image = javax.imageio.ImageIO.read(new File(filename));
        imageIcon = new ImageIcon(image);
        setVisible(true);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        repaint();
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
        if (imageIcon != null)
            g2d.drawImage(imageIcon.getImage(), 0, 0, this);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            if (args.length > 0)
                new Test(args[0]);
            else
                System.out.println("usage - specify image filename on command line");
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

3 ответов


(расширяя мои комментарии)

проблема сводится к следующему: люди обычно считают, что "формат" дал следующую команду:

ImageIO.getReaderFileSuffixes();

поддерживаются Java.

но это не так, как его следует читать/понимать, потому что это просто не как это работает.

неправильно: "ImageIO может читать любой файл, закодированный в одном из этих форматов"

правильно: " ImageIO не может прочитать изображение кодируется с форматом, который не является одним из этих форматов"

но теперь, что это говорит о форматах, появляющихся в этом списке? Что ж... Это становится сложным.

например, этот список обычно возвращает как "PNG", так и " BMP " (и другие форматы). Но нет ни" одного "PNG, ни" одного " BMP. Я могу прийти завтра с" действительным " форматом PNG (sub), который был бы совершенно прекрасным, но ни один png-декодер не будет декодироваться (он должен быть проверен и принят: но как только он будет принят, он "сломает" все существующие декодеры PNG). К счастью, для PNG-изображений проблема не так уж плоха.

формат BMP очень сложный. Вы можете иметь сжатие или нет (что может объяснить различный размер файла, который вы видели). Вы можете иметь различные заголовки (разной длины, что также может объяснить различный размер файла, который вы видели). Черт, BMP на самом деле настолько сложен, что я думаю, что вы можете вставлять png-кодированные пиксели внутри BMP "оболочка."

существует в основном два проблемных типа файлов BMP:

  • варианты BMP, появившиеся после создания декодера Java
  • варианты BMP, которые достаточно неясны, чтобы разработчики Java ImageIO не считали его достойным поддержки

"ошибка" заключается в том, что существует один PNG или один формат BMP. Оба формата (и другие форматы изображений тоже) на самом деле "расширяемы". Каждый раз новое вариант выходит, он имеет потенциал, чтобы сломать любой декодер там.

Итак, что происходит в вашем случае это:

  1. Вы читаете исходный файл BMP из MS Paint, и MS Paint может прочитать этот файл, потому что это формат BMP, который понимает MS Paint.

  2. этот же формат BMP чужд версии Java, которую вы используете (есть надежда, что он будет поддерживаться в другой Java но я бы на это не рассчитывал).

  3. когда вы повторно сохраняете этот файл из MS Paint, вы сохраняете в формате BMP, который определенно не то же самое, что и исходный формат (различный размер файла довольно сказать)

  4. этот другой формат поддерживается вашей версией Java.

теперь, чтобы фактически решить вашу проблему: в моем опыте библиотеки изображений, такие как ImageMagick могут читать гораздо больше изображений, чем API Java ImageIO по умолчанию, поэтому я бы посмотрел на другие библиотеки изображений или обертки вокруг ImageMagick.

эти библиотеки также обычно обновляются для поддержки новых вариантов и новых форматов намного быстрее, чем Java. Например, удивительный WebP формат от Google (до 28% до 34% лучше, чем PNG на без потерь+полупрозрачные изображения) уже поддерживается довольно некоторыми библиотеками обработки изображений но я не задерживаю дыхание, когда дело доходит доImageIO.читайте (someWebPpicture)...

другой вариант - использовать PNG: хотя теоретически PNG может быть расширен, вы с меньшей вероятностью найдете "не поддерживаемые" PNG в дикой природе. С BMPs это слишком распространено.


здесь есть пример кодаhttp://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm это перечислит поддерживаемые форматы изображений вашим JDK.

BMP поддерживается advanced image toolkit http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html но я знаю, что в нем также есть вещи, которые теперь поддерживаются базовым JDK. Так что если его поддерживают оба, то, возможно поддержка JAI является более всеобъемлющей. Это кажется маловероятным, поскольку это не имеет большого смысла. OTOH, это был Солнце.

Если вы используете JDK 6, Вы можете определенно сделать PNG (который более портативный), можете ли вы конвертировать свои изображения? IIRC MS Paint сохранит png.


я протестировал два изображения с помощью моего собственного декодера Java BMP. Он также сбрасывает некоторую информацию об изображении. Я нашел оригинал 32 бита BMP и вновь спас один-это 24 бита одна. Поэтому я предполагаю, что imageio bmp reader не может правильно обрабатывать 32-битный bmp.

Update: чтобы подтвердить мою давнюю догадку, я снова протестировал изображение и все еще проблематично. Проблема здесь не показаны изображения и причиной является Java ImageIO считает, изображение полностью прозрачное. Ниже дамп из Java ImageIO создал BufferedImage:

 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
 IntegerInterleavedRaster: width = 494 height = 516 #Bands = 4 xOff = 0 yOff = 0
 dataOffset[0] 0
 java.awt.image.SinglePixelPackedSampleModel@80809ee

мы видим, что здесь есть 4 полосы, представляющие RGBA как Java ImageIO интерпретировал его. Правда, четвертая полоса или четвертый байт не для альфа-канала для 32-битного образа Windows BMP. Это просто мусор или сделать его двойным выровненным словом.

Windows 3.X bmp декодер и многое другое отсюда https://github.com/dragon66/icafe