Why doesn't ImageIO read a BMP file until it is re-saved in MS Paint?
Asked Answered
G

3

8

I have a bitmap file, test3.bmp, which I can view and edit with every image viewer I have tested with.

That said, I cannot read it into my Java application. 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. 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();
        }
    }

}
Greige answered 22/11, 2011 at 0:41 Comment(2)
there a LOT of different types of BMP files. Probably that your MS Paint can read the original format but is using others BMP headers/encoding when saving back the file. I know for sure that Java definitely cannot read all the various BMP files out there (few BMP readers can). It just happen to be that Java supports that particular kind of BMP that MS Paint outputs. Why BMP? If you want lossless, would PNG be an option?Reginaldreginauld
same problem here as "Roman B."... The links for the BMP file cannot work. You could hexdump the headers (the beginning) of both files and add the result to your question but to me anyway the issue is precisely what I described above :-/Reginaldreginauld
R
10

(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.

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"

Correct: "ImageIO cannot read image encoded with a format that is not one of these format"

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). But there's not "one" PNG nor "one" 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). Luckily, for the PNG pictures the problem ain't too bad.

The BMP format is very complicated. 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".

There are basically two problematic types of BMP files:

  • BMP variants that appeared after the Java decoder was created
  • BMP variants that are obscure enough so that the Java ImageIO implementors didn't consider it worthy of support

The "error" consists in thinking that there's one PNG or one BMP format. 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.

  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).

  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)

  4. that other format happens to be supported by your version of 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.

These libraries are also typically updated to support newer variants and newer formats much faster than Java is. 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)...

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. With BMPs it's all too common.

Reginaldreginauld answered 22/11, 2011 at 1:42 Comment(1)
I usually neglect to mention that image (and most sound) formats are 'container formats' unless something crops up that underlines the differences between the encodings. +1 for your comprehensive explanation.Shirleenshirlene
P
0

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.

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. So if its supported by both, then maybe the JAI support is more comprehensive. That seems unlikely though, as it doesn't make much sense. OTOH, it was Sun.

If you're using JDK 6, you can definitely do PNG (which is more portable), can you convert your images? IIRC MS Paint will save a png.

Penton answered 22/11, 2011 at 1:21 Comment(1)
the image "formats" claimed to be supported by the ImageIO reader(s) are highly misleading, as explained in my lenghty answer : ) If BMP was really supported, the OP could open his image using ImageIO just as he can open it from MS Paint. This is not the case.Reginaldreginauld
C
0

I tested the two images using my own Java BMP decoder. 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. So I assume imageio bmp reader cannot handle 32 bit bmp correctly.

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. The following is a dump from Java ImageIO created 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. The truth is the fourth band or the fourth byte is not for alpha channel for a 32 bit Windows BMP image. 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

Constitution answered 15/3, 2012 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.