为什么ImageIO在将其重新保存到MS Paint中之前不会读取BMP文件?

时间:2011-11-22 00:41:39

标签: java image bmp

我有一个位图文件test3.bmp,我可以使用我测试过的每个图像查看器查看和编辑它。

那就是说,我无法将其读入我的Java应用程序。如果我在MS Paint中编辑BMP,保存它,撤消更改并保存它(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 个答案:

答案 0 :(得分:10)

(扩展我的评论)

问题归结为:人们通常认为以下命令给出的“格式”:

ImageIO.getReaderFileSuffixes();

受Java支持。

但这不是应该如何阅读/理解,因为这根本不是它的工作方式。

错误:“ImageIO可以读取以这些格式之一编码的任何文件”

正确:“ImageIO无法读取使用不属于这些格式的格式编码的图像”

但是现在对该列表中出现的格式有什么看法呢?嗯......这很棘手。

例如,该列表通常返回“PNG”和“BMP”(以及其他格式)。但是,没有“一个”PNG,也没有“一个”BMP。我明天可以用一个“有效”的PNG(子)格式来完美,但是那里没有单个PNG解码器可以解码(它必须经过验证和接受:但一旦它被接受,就会“破坏” “所有现有的PNG解码器都在那里。幸运的是,对于PNG图片来说问题并不算太糟糕。

BMP格式非常复杂。你可以压缩或不压缩(这可以解释你看到的不同文件大小)。您可以拥有各种标题(不同长度,也可以解释您看到的不同文件大小)。哎呀,BMP实际上非常复杂,我认为你可以将PNG编码的像素嵌入到BMP“shell”中。

基本上有两种有问题的BMP文件类型:

  • 创建Java解码器后出现的BMP变体
  • BMP变体模糊不清,以致Java ImageIO实现者认为不值得支持

“错误”包括认为有一种PNG或一种BMP格式。两种格式(以及其他图像格式)实际上都是“可扩展的”。每当一个新版本出现时,它就有可能打破任何解码器。

所以你的情况是这样的:

  1. 您正在从MS Paint读取原始BMP文件,MS Paint能够读取该文件,因为它恰好是MS Paint理解的BMP格式。

  2. 相同的BMP格式与您正在使用的Java版本不同(希望它将在另一个Java版本中得到支持,但我不会指望它)。

  3. 当您从MS Paint重新保存该文件时,您将保存的BMP格式绝对与原始格式相同(不同的文件大小相当于告诉)

  4. 您的Java版本恰好支持其他格式。

  5. 现在要真正解决您的问题:根据我的经验,像 ImageMagick 这样的图像库能够读取比默认Java ImageIO API更多的图片,所以我将看看其他图像库或 ImageMagick 周围的包装。

    这些库通常也会更新,以便比Java更快地支持更新的变体和更新的格式。例如,来自谷歌的惊人的 WebP 格式(在无损+半透明图像上比PNG高出28%到34%)已经得到了相当多的图像处理库的支持,但我不会屏住呼吸。它来做一个 ImageIO.read(someWebPpicture) ...

    另一种选择是使用PNG:即使理论上PNG可以扩展,你也不太可能在野外找到“不支持的”PNG。对于BMP来说,它太常见了。

答案 1 :(得分:0)

这里有一些示例代码http://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm将枚举JDK支持的图像格式。

高级图像工具包http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html支持BMP,但我知道它也包含基本JDK现在支持的内容。因此,如果它得到两者的支持,那么JAI的支持可能更全面。这似乎不太可能,因为它没有多大意义。 OTOH, 太阳。

如果你正在使用JDK 6,你绝对可以做PNG(更便携),你可以转换你的图像吗? IIRC MS Paint将保存一个png。

答案 2 :(得分:0)

我使用自己的Java BMP解码器测试了这两个图像。它还会转储图像的一些信息。我发现原来的是32位bmp,重新保存的是24位。所以我假设imageio bmp阅读器无法正确处理32位bmp。

更新:为了确认我很久以前的猜测,我再次测试了图像,但仍然存在问题。问题是没有图像显示,原因是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图像的alpha通道。它只是垃圾或使其双字对齐。

Windows 3.x BMP解码器以及此处的更多内容https://github.com/dragon66/icafe