2

I'm trying to write an ICO decoder in Java so Im able to display favicons from websites. I've first used the image4j library but noticed it didn't decode all the favicons I threw at it. So after some investigation I realized that image4j only is able to decode ICO-files which contain PNG images or BMP images with header size 40 which turns out to be bitmaps with the header type BITMAPINFOHEADER. Since Windows Vista most bitmaps contain the BITMAPV5HEADER or others like BITMAPV4HEADER so image4j is not an option for me.

I tried to write my own ICO decoder, which now works at the moment but the only problem left is to re-construct a BufferedImage out of the data. According to Wikipedia a PNG image is stored in its entirely and a BMP image is also stored in its entirely but without a file header. Instead of writing the decoding routines for every compression, image format, ... I noticed most of the Windows bitmap headers (except for 8, 16 bit bitmaps and Adobe) are supported by the ImageIO API. However there is one little catch, in order to use ImageIO.read(Inputstream input) the BMP image requires a file header which can be seen here. So what I'm trying to do is re-creating the file header for the BMP image, append the BMP data from the ICO-file and try to read it using ImageIO.read(Inputstream stream). At the moment Im getting a EOF exception and I think it has to be declaring the image data offset in the file header but it could also be I'm writing the data wrong.

Below is some code showing what I'm doing:

final BitmapInfoHeader infoHeader = new BitmapInfoHeader(data);//decodes bitmapheader to figure out amount of color palette entries
final int fileHeaderSize = 14;
final int offset = fileHeaderSize + infoHeader.headerSize + infoHeader.clrImportant * 4;// offset = 54
final int fileSize = offset + data.length + fileHeaderSize;// file size = 1196
final ByteBuffer BMPFile = ByteBuffer.allocate(fileSize).order(ByteOrder.LITTLE_ENDIAN);
//BMPFile.putChar('B');//when doing this file header is 16 bytes long
//BMPFile.putChar('M');
BMPFile.put((byte) 'B');//when doing this file header is 14 bytes long
BMPFile.put((byte) 'M');
BMPFile.putInt(fileSize);
BMPFile.putInt(0); //Reserved
BMPFile.putInt(offset);
BMPFile.put(data); //Data from ICO file

final byte[] imageData = BMPFile.array();
System.out.println("File header + data: " + Arrays.toString(imageData));

final BufferedImage favIcon = ImageIO.read(new ByteArrayInputStream(imageData));//Throws EOF exception

And the output:

/******* READING ICO FILE *******/

Image type: 1 -> .ICO

Number of entries: 2

Entry #1-> 16X16 | Color Palette: 0 | Reserved: 0 | Planes: 0 | Format: 32 | Data size: 1128 | Data offset: 38

Entry #2-> 32X32 | Color Palette: 0 | Reserved: 0 | Planes: 0 | Format: 32 | Data size: 4264 | Data offset: 1166

Data entry #1: (1128) -> [40, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 64, 4,...

/******* READING BITMAP HEADER *******/

HeaderSize: 40
[BITMAPINFOHEADER]
width: 16 | height: 32 | colorPlanes: 1 | bitsPerPixel: 32 | Compression: 0 | Image Size: 1088 | xPelsPerMeter: 0 | yPelsPerMeter: 0 | clrUsed: 0 | clrImportant: 0

/******* CREATING BITMAP FILE HEADER *******/

File header + data: [66, 77, 0, 0, 4, -84, 0, 0, 0, 0, 0, 0, 0, 54, 40, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 64, 4,...

When re-constructing the file header using bytes for the magic number the file header size is 14 bytes:

BMPFile.put((byte) 'B');
BMPFile.put((byte) 'M');
                 "B" "M"                             | 14 bytes

File header + data: [66, 77, 0, 0, 4, -84, 0, 0, 0, 0, 0, 0, 0, 54, 40, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 64, 4,...

However when using putChar for the magic number the file header size is 16 bytes:

BMPFile.putChar('B');
BMPFile.putChar('M');
                 --B-- --M--                              | 16 bytes 

File header + data: [0, 66, 0, 77, 0, 0, 4, -84, 0, 0, 0, 0, 0, 0, 0, 54, 40, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 64, 4,

All help is welcome :)

MircoProgram
  • 295
  • 1
  • 20
  • A char can take a Unicode character and Unicode does not fit into a byte. – Thomas Weller Oct 07 '16 at 14:54
  • Possible duplicate of [Isn't the size of character in Java 2 bytes?](http://stackoverflow.com/questions/5078314/isnt-the-size-of-character-in-java-2-bytes) – Thomas Weller Oct 07 '16 at 14:56
  • FWIW: I've created an [ICO/CUR plugin for ImageIO](https://github.com/haraldk/TwelveMonkeys#ico--cur---ms-windows-icon-and-cursor-formats), it should work with any ICO files you throw at it (if not, send me a sample, and I'll try to fix it! ;-) ). – Harald K Oct 07 '16 at 20:09
  • @Thomas Even when using `BMPFile.put((byte) 'B');` Im getting an `EOF exception` – MircoProgram Oct 08 '16 at 08:48

0 Answers0