简体   繁体   English

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

[英]Why doesn't ImageIO read a BMP file until it is re-saved in MS Paint?

I have a bitmap file, test3.bmp , which I can view and edit with every image viewer I have tested with. 我有一个位图文件test3.bmp ,我可以使用我测试过的每个图像查看器查看和编辑它。

That said, I cannot read it into my Java application. 也就是说,我无法将其读入我的Java应用程序。 If I edit the BMP in MS Paint, save it, undo the change, and save it ( test3_resaved.bmp ), I have the same image, but with a different file size. 如果我在MS Paint中编辑BMP,保存它,撤消更改并保存它( test3_resaved.bmp ),我有相同的图像,但文件大小不同。 The different file sizes do not concern me... what does, is that my application can read the re-saved file. 不同的文件大小与我无关......我的应用程序可以读取重新保存的文件是什么。

Could anyone enlighten me on why one image works with my code but the other does not? 任何人都可以告诉我为什么一个图像与我的代码一起工作但另一个图像没有?

Images files: 图像文件:

Here is a minimal test application: 这是一个最小的测试应用程序:

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

}

(expanding on my comments) (扩大我的意见)

The problem boils down to this: people typically believe that the "format" given by the following command: 问题归结为:人们通常认为以下命令给出的“格式”:

ImageIO.getReaderFileSuffixes();

are supported by Java. 受Java支持。

But that's not how it should be read/understood because that is simply not how it works. 但这不是应该如何阅读/理解,因为这根本不是它的工作原理。

Wrong: "ImageIO can read any file encoded with one of these format" 错误: “ImageIO可以读取以这些格式之一编码的任何文件”

Correct: "ImageIO cannot read image encoded with a format that is not one of these format" 正确: “ImageIO无法读取以不是这些格式之一的格式编码的图像”

But now what does that say about formats appearing in that list? 但是现在对于该列表中出现的格式有什么看法呢? Well... It gets tricky. 嗯......这很棘手。

For example that list typically returns both "PNG" and "BMP" (and other formats). 例如,该列表通常返回“PNG”和“BMP”(以及其他格式)。 But there's not "one" PNG nor "one" BMP. 但是,没有“一个”PNG,也没有“一个”BMP。 I can come tomorrow with a "valid" PNG (sub)format that would be perfectly fine but that no single PNG decoder out there would decode (it would have to be validated and accepted: but once it would be accepted, it would "break" all the existing PNG decoders out there). 我明天可以用一个“有效”的PNG(子)格式来完美,但是那里没有单个PNG解码器可以解码(它必须经过验证和接受:但一旦它被接受,就会“破坏” “所有现有的PNG解码器都在那里。 Luckily, for the PNG pictures the problem ain't too bad. 幸运的是,对于PNG图片来说问题并不算太糟糕。

The BMP format is very complicated. BMP格式非常复杂。 You can have compression or not (which may explain the varying file size you've seen). 你可以压缩或不压缩(这可以解释你看到的不同文件大小)。 You can have various headers (of differing length, which may also explain the varying file sized you've seen). 您可以拥有各种标题(不同长度,也可以解释您看到的不同文件大小)。 Heck, BMP is actually so complex that I think that you can embed PNG encoded pixels inside a BMP "shell". 哎呀,BMP实际上非常复杂,我认为你可以将PNG编码的像素嵌入到BMP“shell”中。

There are basically two problematic types of BMP files: 基本上有两种有问题的BMP文件类型:

  • BMP variants that appeared after the Java decoder was created 创建Java解码器后出现的BMP变体
  • BMP variants that are obscure enough so that the Java ImageIO implementors didn't consider it worthy of support BMP变体模糊不清,因此Java ImageIO实现者不认为它值得支持

The "error" consists in thinking that there's one PNG or one BMP format. “错误”在于认为有一种PNG或一种BMP格式。 Both formats (and other image formats too) are actually "extensible". 两种格式(以及其他图像格式)实际上都是“可扩展的”。 An everytime a new variant comes out it has the potential to break any decoder out there. 每当一个新的变种出现时,它就有可能打破那里的任何解码器。

So what's happening in your case is this: 那么在你的情况下发生的是这样的:

  1. you're reading your original BMP file from MS Paint and MS Paint is able to read that file because it happens to be a BMP format that MS Paint understands. 你正在从MS Paint读取原始的BMP文件,MS Paint能够读取该文件,因为它恰好是MS Paint理解的BMP格式。

  2. that same BMP format is alien to the Java version you're using (there's hope that it will be supported in another Java version but I wouldn't count on it). 相同的BMP格式与您正在使用的Java版本不同(希望它将在另一个Java版本中得到支持,但我不会指望它)。

  3. when you re-save that file from MS Paint, you're saving in a BMP format that is definitely not the same as the original format (the varying file size being quite a tell) 当您重新保存从MS油漆文件,你在一个BMP格式,绝对是一样的原始格式保存(在不同的文件大小是相当告诉)

  4. that other format happens to be supported by your version of Java. 您的Java版本恰好支持其他格式。

Now to actually solve your problem: in my experience image libraries like ImageMagick are able to read much much more pictures than the default Java ImageIO API so I'd give a look at either other image libraries or wrappers around ImageMagick . 现在要真正解决你的问题:根据我的经验,像ImageMagick这样的图像库能够读取比默认Java ImageIO API更多的图片,所以我将看看ImageMagick周围的其他图像库或包装器。

These libraries are also typically updated to support newer variants and newer formats much faster than Java is. 这些库通常也会更新,以便比Java更快地支持更新的变体和更新的格式。 For example the amazing WebP format from Google (up to 28% to 34% better than PNG on lossless+translucent images) is already supported by quite some image manipulation libraries but I'm not holding my breath when it comes to do a ImageIO.read( someWebPpicture) ... 例如,来自谷歌的惊人的WebP格式(无损+半透明图像上比PNG高出28%到34%)已经得到了相当多的图像处理库的支持,但是在做一个ImageIO时我并没有屏住呼吸。读(someWebPpicture) ......

Another option would be to use PNG: even though theoretically PNG can be extended you're less likely to find "non supported" PNGs in the wild. 另一种选择是使用PNG:即使理论上PNG可以扩展,你也不太可能在野外找到“不支持的”PNG。 With BMPs it's all too common. 对于BMP来说,它太常见了。

There's some example code here http://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm that will enumerate supported image formats by your JDK. 这里有一些示例代码http://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm将枚举JDK支持的图像格式。

BMP is supported by the advanced image toolkit http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html but I know that it also has things in it that are now supported by the base JDK too. 高级图像工具包http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html支持BMP,但我知道它现在还有其他内容也是由JDK基础支持的。 So if its supported by both, then maybe the JAI support is more comprehensive. 因此,如果它得到两者的支持,那么JAI的支持可能更全面。 That seems unlikely though, as it doesn't make much sense. 这似乎不太可能,因为它没有多大意义。 OTOH, it was Sun. OTOH, 太阳。

If you're using JDK 6, you can definitely do PNG (which is more portable), can you convert your images? 如果你正在使用JDK 6,你绝对可以做PNG(更便携),你可以转换你的图像吗? IIRC MS Paint will save a png. IIRC MS Paint将节省一个png。

I tested the two images using my own Java BMP decoder. 我使用自己的Java BMP解码器测试了这两个图像。 It also dumps some information of the image. 它还会转储图像的一些信息。 I found the original one is a 32 bits bmp and the re-saved one is a 24 bits one. 我发现原来的是32位bmp,重新保存的是24位。 So I assume imageio bmp reader cannot handle 32 bit bmp correctly. 所以我假设imageio bmp阅读器无法正确处理32位bmp。

Update: To confirm my long time ago guess, I tested the image again and still problematic. 更新:为了确认我很久以前的猜测,我再次测试了图像,但仍然存在问题。 The problem is there is no image showing and the reason is Java ImageIO thinks the image is completely transparent. 问题是没有图像显示,原因是Java ImageIO认为图像是完全透明的。 The following is a dump from Java ImageIO created BufferedImage: 以下是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

We can see here there are 4 bands representing RGBA as Java ImageIO interpreted it. 我们可以看到这里有4个代表RGBA的波段,因为Java ImageIO对其进行了解释。 The truth is the fourth band or the fourth byte is not for alpha channel for a 32 bit Windows BMP image. 事实是第四个波段或第四个字节不适用于32位Windows BMP图像的alpha通道。 It is simply garbage or to make it double word aligned. 它只是垃圾或使其双字对齐。

A Windows 3.x BMP decoder and lots more from here https://github.com/dragon66/icafe 一个Windows 3.x BMP解码器和更多来自这里https://github.com/dragon66/icafe

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM