I'm trying to send a BufferedImage
over socket, I do this by converting the image to byte[]
and then send it over after encoding it in Base64. I'm sending over 2 BufferedImages, one of them is "full", the other one is about 50% transparent. The problem I'm having, is that when they arrive, the second image is still visually transparent, but when I get the data array via Raster, it has been changed.
I made a small test code to demonstrate the problem;
BufferedImage levelBufferedOriginal = ...
BufferedImage backgroundBufferedOriginal = ...
byte[] levelDataOriginal = ((DataBufferByte) levelBufferedOriginal.getRaster().getDataBuffer()).getData();
byte[] backgroundDataOriginal = ((DataBufferByte) backgroundBufferedOriginal.getRaster().getDataBuffer()).getData();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] temp = null, temp2=null;
try {
ImageIO.write(levelBufferedOriginal, "png", baos);
baos.flush();
temp = baos.toByteArray();
baos.close();
baos=new ByteArrayOutputStream();
ImageIO.write(backgroundBufferedOriginal, "png", baos);
baos.flush();
temp2 = baos.toByteArray();
baos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
BufferedImage levelBufferedNew = null;
BufferedImage backgroundBufferedNew = null;
try {
levelBufferedNew = ImageIO.read(new ByteArrayInputStream(temp));
backgroundBufferedNew = ImageIO.read(new ByteArrayInputStream(temp2));
} catch (IOException e) {
e.printStackTrace();
}
byte[] levelDataNew = ((DataBufferByte) levelBufferedNew.getRaster().getDataBuffer()).getData();
byte[] backgroundDataNew = ((DataBufferByte) backgroundBufferedNew.getRaster().getDataBuffer()).getData();
System.out.println("LEVEL: " + Arrays.equals(levelDataOriginal, levelDataNew));
System.out.println("BACKGROUND: " + Arrays.equals(backgroundDataOriginal, backgroundDataNew));
All I do here, is simply transform the BufferedImage
to byte[]
, then back, and compare the data I get from DataBufferByte
. The output is
LEVEL: false
BACKGROUND: true
Background is the "full" image, and Level is the one with some transparent pixels.
If the general idea is wrong, I would like to hear another, all I want is to be able to exactly recreate 2 bufferedImages.
edit: What we have established so far:
byte
s). The after image has a different value at this index, that is not transparent.Which leads to the conclusion that the ImageIO PNGImageWriter re-arranges the entries in the color map when writing, resulting in different pixel data/color map.
This basically leaves us with two options:
Serialize the image data in a different way, to assure the color map/pixel data is not modified. It is possible to send the pixel data array, along with the color map array and the height/width of the image, and then re-create the image exactly at the client. This is quite a bit of code, and is probably covered by other questions on SO.
Don't rely on the pixel data/color maps being the same. Use the value of ((IndexColorModel) levelBufferedNew.getColorModel()).getTransparentPixel()
to test for/set transparency instead of the hardcoded value -1
. This requires pretty much no other change in your code.
Note: These solutions will only work for TYPE_BYTE_INDEXED (13) images.
For a more generic (but possibly slower) approach, use the code in the original answer to set transparent parts, and use (levelBufferedNew.getRGB(x, y) >> 24) == 0
to test for transparency. This should work even for TYPE_INT_ARGB or TYPE_4BYTE_ABGR.
original answer:
Instead of fiddling with the image at byte array level, why not try using normal Java2D? ;-)
Something like:
Graphics2D g = levelBufferedNew.createGraphics();
try {
g.setComposite(AlphaComposite.Clear);
g.fillOval(x, y, w, h); // The area you want to make transparent
}
finally {
g.dispose();
}
...should work.
PS: As the images use IndexColorModel
, you can use the getTransparentPixel()
to get the transparent pixel index, instead of relying on it being at a certain index (-1/255). Then you can still manipulate at byte array level. ;-)