java - BufferedImage bytes have a different byte order, when running from Eclipse and the command line -
i trying convert bufferedimage
's byte[]
32-bit rgba 24-bit rgb. according this answer fastest way byte[]
image is:
byte[] pixels = ((databufferbyte) bufferedimage.getraster().getdatabuffer()).getdata();
so iterate on bytes assuming order r g b , every 4 bytes, write first 3 in output byte[] (i.e. ignoring alpha value).
this works fine when run eclipse , bytes converted correctly. when run same program command line same bytes returned opposite byte order!
the test image use test 5x5 black image top-left corner different having rgba color [aa cc ee ff]
:
and zoomed-in version conveniency:
my folder structure is:
- src/ - test.png - test/ - testbufferedimage.java
the sscce following:
package test; import java.awt.image.bufferedimage; import java.awt.image.databufferbyte; import java.io.ioexception; import java.io.inputstream; import javax.imageio.imageio; public class testbufferedimage { private static void log(string s) { system.out.println(s); } private static string tobytestring(byte b) { // perform bitwise , convenience while printing. // otherwise integer.tohexstring() interprets values integers , negative byte 0xff printed "ffffffff" return integer.tohexstring(b & 0xff); } /** * @param args * @throws ioexception */ public static void main(string[] args) throws ioexception { inputstream stream = testbufferedimage.class.getclassloader().getresourceasstream("test.png"); bufferedimage image = imageio.read(stream); stream.close(); log("image loaded succesfully, width=" + image.getwidth() + " height=" + image.getheight()); log("converting 32-bit 24-bit..."); databufferbyte buffer = (databufferbyte)image.getraster().getdatabuffer(); byte[] input = buffer.getdata(); byte[] output = convertto24bit(input); log("converted total of " + input.length + " bytes " + output.length + " bytes"); } private static byte[] convertto24bit(byte[] input) { int datalength = input.length; byte[] converteddata = new byte[ datalength * 3 / 4 ]; (int = 0, j = 0; < datalength; i+=4, j+=3) { convertintbytetobyte(input, i, converteddata, j); } return converteddata; } private static void convertintbytetobyte(byte[] src, int srcindex, byte[] out, int outindex) { byte r = src[srcindex]; byte g = src[srcindex+1]; byte b = src[srcindex+2]; byte = src[srcindex+3]; out[outindex] = r; out[outindex+1] = g; out[outindex+2] = b; log("i=" + srcindex + " converting [" + tobytestring(r) + ", " + tobytestring(g) + ", " + tobytestring(b) + ", " + tobytestring(a) + "] --> [" + tobytestring(out[outindex]) + ", " + tobytestring(out[outindex+1]) + ", " + tobytestring(out[outindex+2]) + "]" ); } }
output when run eclipse (version: juno service release 2 build id: 20130225-0426):
image loaded succesfully, width=5 height=5 converting 32-bit 24-bit... i=0 converting [aa, cc, ee, ff] --> [aa, cc, ee] // <-- bytes have correct order i=4 converting [0, 0, 0, ff] --> [0, 0, 0] i=8 converting [0, 0, 0, ff] --> [0, 0, 0] ..... i=96 converting [0, 0, 0, ff] --> [0, 0, 0] converted total of 100 bytes 75 bytes
output when run command line (windows vista) java test.testbufferedimage
:
image loaded succesfully, width=5 height=5 converting 32-bit 24-bit... i=0 converting [ff, ee, cc, aa] --> [ff, ee, cc] // <-- bytes returned different byte order! i=4 converting [ff, 0, 0, 0] --> [ff, 0, 0] i=8 converting [ff, 0, 0, 0] --> [ff, 0, 0] ..... i=96 converting [ff, 0, 0, 0] --> [ff, 0, 0] converted total of 100 bytes 75 bytes
so has encountered similar issue and/or can explain going on? why byte order different when running inside eclipse?
before answering own question really, really, thank @joni , @haraldk pointed me right direction. knowledge internals of bufferedimage
, colormodel
, samplemodel
, not great have helped me out.
so here happened:
first of different behaviour caused different jres. log statement printing java version revealed eclipse prints 1.6.0_16-b01
while command line prints 1.6.0_31-b05
. apparently implementation of image loading (this pngimagereader
class) has changed between versions , suspect did during this change.
still though both versions use same colormodel
, samplemodel
couldn't understand change (it seemed real code-breaker me) have investigated further.
the important change between 2 versions of pngimagereader
in iterator<imagetypespecifier> getimagetypes()
method decides available compatible formats can used create new image:
version 1.6.0_16-b01:
... case png_color_rgb_alpha: // component r, g, b, (non-premultiplied) rgb = colorspace.getinstance(colorspace.cs_srgb); bandoffsets = new int[4]; bandoffsets[0] = 0; bandoffsets[1] = 1; bandoffsets[2] = 2; bandoffsets[3] = 3; l.add(imagetypespecifier.createinterleaved(rgb, bandoffsets, datatype, true, false)); break;
version 1.6.0_31-b05:
... case png_color_rgb_alpha: if (bitdepth == 8) { // standard types of buffered images // wich can used destination l.add(imagetypespecifier.createfrombufferedimagetype( bufferedimage.type_4byte_abgr)); l.add(imagetypespecifier.createfrombufferedimagetype( bufferedimage.type_int_argb)); } // component r, g, b, (non-premultiplied) rgb = colorspace.getinstance(colorspace.cs_srgb); bandoffsets = new int[4]; bandoffsets[0] = 0; bandoffsets[1] = 1; bandoffsets[2] = 2; bandoffsets[3] = 3; l.add(imagetypespecifier.createinterleaved(rgb, bandoffsets, datatype, true, false)); break;
so in newer version, new imagetypespecifier
s png loader compatible image internal representation bufferedimage.type_4byte_abgr
, since first type in list, loader goes ahead , uses that. why bands color channels (and bytes) reversed.
realizing above, hit me not bug nor code breaker thought. reason because using byte[] pixels = ((databufferbyte) bufferedimage.getraster().getdatabuffer()).getdata();
bytes hacking way internal data structures (i.e. ignoring samplemodel
) of image. nothing in contract of imagereader
guarantees order of bytes. free change internal data stuctures if wants (this point of encapsulation right?). way correctly configure imagereader
if desire else default behaviour default imagereadparam
, configure it. pass reader using reader.read(imageindex, param);
since want reader return specific format image bytes should this:
log("java version: " + system.getproperty("java.runtime.version")); // reader imagereader ir = imageio.getimagereadersbyformatname("png").next(); // default param imagereadparam p = ir.getdefaultreadparam(); p.setdestinationtype( // define image type return if supported imagetypespecifier.createinterleaved( colorspace.getinstance(colorspace.cs_srgb), new int[] {0, 1, 2, 3}, // <-- order of color bands return bytes in desired order databuffer.type_byte, true, false) ); inputstream stream = testbufferedimage.class.getclassloader().getresourceasstream("test.png"); imageinputstream imagestream = imageio.createimageinputstream(stream); ir.setinput(imagestream); bufferedimage image = ir.read(0, p);
now both versions return order of bytes in same rgba form i.e. output different color print in both cases:
... i=0 converting [aa, cc, ee, ff] --> [aa, cc, ee] ...
Comments
Post a Comment