1

我的 midlet 可以正常显示一些图像,但不能正常显示其他图像。

它们都是 8 位 PNG,但没有显示的是我自己在 PhotoShop 中创建的。

所以我想也许我的 PhotoShop (CS6) 设置是错误的......

PNG-8,选择性,扩散,颜色:256,抖动:100%,无光泽:无,Web Snap:0%,转换为 sRGB:勾选,宽度:48,高度:48,百分比:100%,质量:双三次。

我已经尝试了其中一些设置,但无济于事。

有任何想法吗?

这里有一个类似的问题,但这与我的相反,因为 PhotoShop 在这种情况下会修补东西,而不是破坏东西......

我的代码是...

image = Image.createImage("/img/loading1.png");

...这是我的堆栈跟踪:

java.io.EOFException
    at javax.imageio.stream.ImageInputStreamImpl.readFully(
ImageInputStreamImpl.java:353)
    at java.io.DataInputStream.readUTF(DataInputStream.java:609)
    at javax.imageio.stream.ImageInputStreamImpl.readUTF(ImageInputStreamImpl.java:332)
    at com.sun.kvem.png.PNGImageReader.parse_iTXt_chunk(PNGImageReader.java:447)
    at com.sun.kvem.png.PNGImageReader.readMetadata(PNGImageReader.java:650)
    at com.sun.kvem.png.PNGImageReader.readImage(PNGImageReader.java:1312)
    at com.sun.kvem.png.PNGImageReader.read(PNGImageReader.java:1582)
    at com.sun.kvem.midp.GraphicsBridge.loadImage(GraphicsBridge.java:2602)
    at com.sun.kvem.midp.GraphicsBridge.createImageFromData(GraphicsBridge.java:2511)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.sun.kvem.sublime.MethodExecution.process(MethodExecution.java:42)
    at com.sun.kvem.sublime.SublimeExecutor.processRequest(SublimeExecutor.java:63)
    at javax.microedition.lcdui.Image.createImage(Image.java:315)

有问题的图像确实存在 - 无论是在项目中还是在构建的 jar 中。

这是有问题的图像: 在此处输入图像描述

4

1 回答 1

3

根据崩溃日志,J2ME 中的 PNG 解码器在非关键块内失败iTXt1

> com.sun.kvem.png.PNGImageReader.readMetadata
  > com.sun.kvem.png.PNGImageReader.parse_iTXt_chunk
    > javax.imageio.stream.ImageInputStreamImpl.readUTF
      > java.io.DataInputStream.readUTF

根据libpng 文档iTXt,块的文本部分必须是有效的 UTF8:

...根据压缩标志,剩余的块数据是主要的 UTF-8 文本,无论是否经过 zlib 压缩。由于它的长度可以从块长度确定,所以它不是空终止的。与其他两个文本块一样,换行符应由单个换行字符(十进制 10)表示,并且不鼓励使用所有其他控制字符(1-9、11-31 和 127-159)。

因此通常这表明读取的流不是有效的 UTF8 文本 - 它包含高于0..127不符合 UTF8 规则的纯 ASCII 范围的“原始”字节。

我发现在示例图像中并非如此。只有一组连续的字节构成一个UTF8码序列,并且是有效的:

<?xpacket begin=" EFBBBF " id=" ..

(粗体部分以十六进制表示 3 个数据字节)。我首先怀疑这是错误:

如果 BOM 字符出现在数据流的中间,Unicode 表示它应该被解释为“零宽度不间断空格”(禁止字形之间的换行)。在 Unicode 3.2 中,这种用法已被弃用,取而代之的是“Word Joiner”字符 U+2060。[1] 这允许 U+FEFF 仅用作 BOM。
http://en.wikipedia.org/wiki/Byte_order_mark

.. 所以一个完全符合 UTF8 的阅读器应该检查它的字节,并UTFDataFormatException在遇到 BOM 时抛出一个,而不是作为第一个值。令人惊讶的是,这似乎不是问题!首先,没有任何迹象表明任何readUTF来源除了仅验证 UTF8 代码本身是否有效而不管其值如何。有很多“无效”的 Unicode代码点(不代表有效 Unicode 字符或指令的值),但在我看来,它们都被默默地忽略了。但我注意到通用readUTF函数只实现了 UTF8/Unicode 的一小部分(例如,参见Oracle 文档中的Modified UTF-8)。

所以问题出在其他地方。另一个线索是抛出的错误不是 UTFDataFormatException,而是EOFException,表明读取缓冲区用完了它承诺包含的字节数。

(警告:纯猜想如下)

查看 的来源DataInputStream,我发现了这段代码:

588       public final static String readUTF(DataInput in) throws IOException {
589           int utflen = in.readUnsignedShort();

后跟一个循环来读取utflen 字节(不是“Unicode 字符”)。这对于一个块来说是错误的iTXt,因为它没有一个“第一个单词”来指示它的长度。纯文本中的字节数可以从块长度(即,根据 PNG 约定,不包括长度长字、iTXt签名本身和最终 CRC32 代码的总数据长度)减去零的长度得出 -终止的关键字名称、语言和“翻译的关键字”字符串,以及指示完整纯文本压缩的两个字节。


作为一种解决方法,请iTXt从您的 PNG 图像中删除这些块。数据本身 - XMP 元数据 - 对于您的目的来说很可能根本不感兴趣(但请随意阅读Adob​​e 认为它有什么好处)。如果您的工作流程不使用它,它只是一堆无用的未压缩文本,占用了示例图像中 981 个字节中的 814 个字节——高达 83%!

您可以使用外部实用程序来删除无关的数据块;pngcrush例如,流行的命令行是

pngcrush -rem alla -rem text InputFile.png OutputFile.png

(来自en.wikipedia.org/wiki/Pngcrush)。

或者直接从 Photoshop 中:如果您使用“另存为”菜单选项以“通常方式”保存 PNG,元数据就会进入,并且没有复选框可以摆脱它。如果您改用“保存为网络和设备”,您会看到一个带有许多方便选项的大对话框,例如标有“元数据”的下拉列表。

选择“全部”我得到一个更大的文件;我的 Photoshop 版本创建了大量3K 的 XMP 元数据块,包括一个 2K 完全空的“填充”块......
选择“版权”或“无”终于摆脱了所有的杂物(大概是因为我没有填写任何版权信息),然后你会得到一个漂亮的 169 字节长的 PNG,其中唯一的元数据是使用的软件称为“Adobe ImageReady”。


1这有点讽刺。根据 PNG 规范,

..遇到辅助位为1的未知块的解码器可以安全地忽略该块并继续显示图像。
来源

这个“辅助位”是块ID第一个字节的第5位:0(大写)=关键,1(小写)=辅助,即如果块ID的第一个字符是大写,PNG阅读器必须正确读取和解释其数据,如果不是,则可以静默跳过。

所以从技术上讲,J2ME 的编写者可以放心地忽略这整个块。但是他们把它搞砸了,试图读取它,现在代码在所有试图读取恰好包含iTXt块的 PNG 中的图像数据的程序上崩溃。

于 2015-02-19T22:09:18.970 回答