我目前正在尝试编写自己的ICO文件阅读器实现。
是的,我知道可以使用现有的库,但这不是一个选项。
基本上,我让读者进行操作直到图像数据反序列化。图标目录条目似乎已正确加载,但我尝试阅读的.ico
文件中的图像数据“无效”。
首先,这是与此问题相关的文件:
ico
档案:http://www.mediafire.com/file/sj579gdv0g60t3w/test.ico bmp
的原始字节:http://www.mediafire.com/file/jr4xkbqvzpelozo/debug.bmp(损坏的文件)有问题的方法不能正常工作:
private static BufferedImage readImage(InputStream stream, IconDirEntry entry) throws IOException {
System.out.println("deserializing "+entry);
byte[] bytes = new byte[entry.data];
stream.read(bytes);
if (!isPNG(bytes)) {
byte[] bmp = makeBitmapFileHeader(bytes);
bytes = PrimArrays.concat(bmp, bytes);
}
return ImageIO.read(new ByteArrayInputStream(bytes));
}
基本上,我的方法是检查图像数据字节是否为PNG,如果不是,则将它们与新标题连接。
这样我仍然可以使用ImageIO
来读取位图,而不必创建自己的位图反序列化器。
运行代码时,图标目录条目似乎正确加载并打印到控制台中:
IconDirEntry{dims=16x16, palette=0, planes=0, bpPixel=32, data=1128, offset=86}
IconDirEntry{dims=32x32, palette=0, planes=0, bpPixel=32, data=4264, offset=1214}
IconDirEntry{dims=48x48, palette=0, planes=0, bpPixel=32, data=9640, offset=5478}
IconDirEntry{dims=64x64, palette=0, planes=0, bpPixel=32, data=16936, offset=15118}
IconDirEntry{dims=128x128, palette=0, planes=0, bpPixel=32, data=67624, offset=32054}
通过各种调试,我已经验证了标头的长度确实应该是14字节,图像数据数组的第一个字节看起来像BMP信息块等。
我还验证了当加载了图标目录条目中指定的所有字节数时,到达了EOF。
此外,第一个IconDirEntry
中的偏移量似乎是正确的,因为ico
标头的大小为6个字节,而5个ICONDIRENTRY
个块的每个大小为16个字节。 (6 + 5*16 = 86)
来自net.grian
软件包的所有内容都已经过测试,并且已经证明有效,但错误不一定存在。
尽管一切看似有效,但在加载第一张图片时出现以下异常:
java.io.EOFException
at javax.imageio.stream.ImageInputStreamImpl.readFully(ImageInputStreamImpl.java:353)
at javax.imageio.stream.ImageInputStreamImpl.readFully(ImageInputStreamImpl.java:405)
at com.sun.imageio.plugins.bmp.BMPImageReader.read32Bit(BMPImageReader.java:1353)
at com.sun.imageio.plugins.bmp.BMPImageReader.read(BMPImageReader.java:890)
at javax.imageio.ImageIO.read(ImageIO.java:1448)
at javax.imageio.ImageIO.read(ImageIO.java:1352)
at me.headaxe.imtu.io.DeserializerICO.readImage(DeserializerICO.java:103)
at me.headaxe.imtu.io.DeserializerICO.fromStream(DeserializerICO.java:54)
at me.headaxe.imtu.io.DeserializerICO.fromStream(DeserializerICO.java:27)
at me.headaxe.imtu.io.DeserializerICO.fromStream(DeserializerICO.java:18)
所有我真正需要的是一个暗示,为什么无法正确加载图像数据,尽管我的其余代码完美运行。
(由“人工”bmp标题组成的原始字节+来自ico文件的图像数据可供上载下载)
当我在ico文件中加载第一个bmp时,我确切地读了960多个字节,我不再得到EOFException
,而是第一个位图被加载,看起来像这样:
由于该偏移,第二个位图显然无法加载IIOException
,因为它格式不正确。
在尝试将它们反序列化之前打印所有字节数组,因为Bitmaps会产生以下结果:
这表明缓冲的字节数是正确的,因为它们都以标志40
开头,BITMAPINFOHEADER
结构的长度。
这次使用了不同的->ico
转换器,效果保持不变,因为缓冲超过data
字段指定生成实际的bmp
文件:
字节数为:[40, 0, 0, 0, 32, 0, 0, 0, 64, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 0, 16, 0, 0, 35, 46, 0, 0, 35, 46, 0 ...
由于32
应该是图像宽度而64
应该是图像高度,因此我很困惑,因为ico是32x32
。
在第二个测试中,.ico
文件大4286字节。
第一个且仅ICONDIRENTRY
的偏移量为22
,数据为4264
。
尽管完全正确且分配了正确的字节数,但读取bmp失败。
答案 0 :(得分:0)
private static BufferedImage readImage(InputStream stream, IconDirEntry entry) throws IOException {
byte[] bytes = new byte[entry.data];
stream.read(bytes);
if (!isPNG(bytes)) {
setLittleInt(bytes, 8, entry.height);
byte[] bmp = makeBitmapFileHeader(bytes);
bytes = PrimArrays.concat(bmp, bytes);
}
if (DEBUG_FILE.exists() && entry.width == 32)
new SerializerByteArray().toFile(bytes, DEBUG_FILE);
return ImageIO.read(new ByteArrayInputStream(bytes));
}
使其运作所需的只是第5行。
显然,您不能依赖存储在.ico
文件中的位图,不会损坏垃圾,因此手动更正字节数组可以解决问题"。
我对此没有任何言论,我完全是傻瓜。 好吧,如果有人解释为什么这样的事情可能会解决它,请发表评论。
如果有人想知道,这是setLittleInt
:
private static void setLittleInt(byte[] bytes, int index, int value) {
byte[] ins = IOMath.toBytes(value);
bytes[index+3] = ins[0];
bytes[index+2] = ins[1];
bytes[index+1] = ins[2];
bytes[index] = ins[3];
}