J2ME - PNG created in PhotoShop not displaying in emulator

时间秒杀一切 提交于 2019-12-11 12:08:17

问题


My midlet is showing some images fine, but not others.

They are all 8-bit PNGs, but the ones that aren't displaying are the ones I have created myself in PhotoShop.

So I am thinking maybe my PhotoShop (CS6) settings are wrong...

PNG-8, Selective, Diffusion, Colors: 256, Dither: 100%, Matte: None, Web Snap: 0%, Convert to sRGB: ticked, Width: 48, Height: 48, Percent: 100%, Quality: Bicubic.

I've experimented with a few of these settings, but to no avail.

Any ideas?

There is a similar problem here but this is opposite to mine in that PhotoShop mends things in that case, rather than breaks things...

My code is...

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

...and here is my stack trace:

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)

The image in question does exist - both in the project and in the jar that is built.

Here is the image in question:


回答1:


According to the crash log, the PNG decoder in J2ME fails inside the non-critical chunk iTXt:1

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

According to libpng documentation, the text part of an iTXt chunk must be valid UTF8:

... The remaining chunk data is the main UTF-8 text, either zlib-compressed or not, according to the compression flag. Since its length can be determined from the chunk length, it is not null-terminated. As with the other two text chunks, newlines should be represented by single line-feed characters (decimal 10), and all other control characters (1-9, 11-31, and 127-159) are discouraged.

and so normally this would indicate that the stream read is not valid UTF8 text - it contains 'raw' bytes higher than the plain ASCII range 0..127 that do not conform to UTF8 rules.

I found that not to be the case in the sample image. There is only one set of consecutive bytes that form a UTF8 code sequence, and it is a valid one:

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

(the bolded section represents 3 data bytes in hexadecimal notation). I first suspected this was the error:

If the BOM character appears in the middle of a data stream, Unicode says it should be interpreted as a "zero-width non-breaking space" (inhibits line-breaking between word-glyphs). In Unicode 3.2, this usage is deprecated in favour of the "Word Joiner" character, U+2060.[1] This allows U+FEFF to be only used as a BOM.
(http://en.wikipedia.org/wiki/Byte_order_mark)

.. and so a fully conforming UTF8 reader should inspect its bytes and throw an UTFDataFormatException when it encounters a BOM anywhere else than as the very first value. Surprisingly, this does not seem to be the problem! First of all, there is no indication any of the readUTF sources do anything else than only verify if the UTF8 code is valid on its own, irrespective of its value. There are lots of 'invalid' Unicode code points (values that do not represent a valid Unicode character or instruction), but it appears to me they are all silently ignored. But I noticed the common readUTF functions only implement a small subset of UTF8/Unicode (see, e.g., Modified UTF-8 in Oracle's documentation).

So the problem lies elsewhere. Another clue to this is that the error thrown is not UTFDataFormatException but rather EOFException, indicating the read buffer ran out of the number of bytes it was promised to contain.

(warning: pure conjecture follows)

Looking at a source of DataInputStream, I find this snippet of code:

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

followed by a loop to read utflen bytes (not "Unicode characters"). This is wrong for an iTXt chunk, as it does not have a 'first word' to indicate its length. The number of bytes in the plain text can be derived from the chunk length (which is, per PNG convention, the total data length excluding the length long word, the iTXt signature itself, and the final CRC32 code) minus the length of the zero-terminated keyword name, language, and "translated keyword" strings, and the two bytes which indicate compression of the full plain text.


As a work-around, remove the iTXt chunks from your PNG images. The data itself -- XMP Metadata -- is most likely not interesting at all for your purposes (but feel free to read what benefits Adobe thinks it has). And if your workflow does not use it, it's just a useless hunk of uncompressed text, taking up 814 bytes of the total of 981 bytes in your sample image -- a whopping 83%!

You can use an external utility to remove extraneous data chunks; the command line for the popular pngcrush, for example, is

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

(from en.wikipedia.org/wiki/Pngcrush).

Or directly from Photoshop: if you save a PNG 'the usual way' with the "Save As" menu option, the metadata goes in and there is no checkbox to get rid of it. If you use "Save for Web & Devices" instead, you get a large dialog with a lot of handy options, such as a drop down list labelled "Metadata".

Choosing "All" I got an even larger file; my version of Photoshop creates a massive 3K chunk of XMP Metadata, including a 2K totally empty 'filler' block...
Selecting "Copyright" or "None" finally got rid of all the crud (presumably because I did not fill in any copyright information), and then you get a nice 169 bytes long PNG, in which the only metadata is that software used is called "Adobe ImageReady".


1 Which is kind of ironic. Per PNG specifications,

.. A decoder encountering an unknown chunk in which the ancillary bit is 1 can safely ignore the chunk and proceed to display the image.
(source)

This "ancillary bit" is the 5th bit of the first byte of the chunk ID: 0 (uppercase) = critical, 1 (lowercase) = ancillary, i.e., if the first character of the chunk ID is a capital, a PNG reader must read and interpret its data correctly, and if it's not, it can be skipped silently.

So technically, the writers of J2ME could safely have ignored this entire chunk. But they messed it up, attempt to read it, and now the code crashes on all programs that merely try to read the image data in PNGs which happen to contain iTXt chunks.



来源:https://stackoverflow.com/questions/28484088/j2me-png-created-in-photoshop-not-displaying-in-emulator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!