透明图像上的ByteArrayOutput / InputStream

时间:2013-06-09 18:47:58

标签: java bufferedimage bytearrayoutputstream

我正在尝试通过套接字发送BufferedImage,我是通过将图像转换为byte[]然后在Base64中对其进行编码后将其发送来实现的。我发送超过2个BufferedImages,其中一个是“满”,另一个是大约50%透明。我遇到的问题是,当它们到达时,第二个图像仍然在视觉上透明,但是当我通过Raster获取数据数组时,它已被更改。

我制作了一个小测试代码来证明问题;

        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));

我在这里所做的只是将BufferedImage转换为byte[],然后返回,并比较我从DataBufferByte获得的数据。输出是

  

LEVEL:false

     

背景:真的

背景是“完整”图像,而Level是具有一些透明像素的图像。

如果一般的想法是错误的,我想听到另一个想法,我想要的是能够完全重建2个缓冲图像。

1 个答案:

答案 0 :(得分:1)

编辑:到目前为止我们已经建立了什么:

  • 使用IndexColorModel(彩色地图)的图像(之前和之后)为TYPE_BYTE_INDEXED(13)
  • 前映像在颜色映射中具有透明颜色,索引为255(字节数组中的值为-1,因为Java使用带符号的byte s)。后映像在此索引处具有不同的值,这是不透明的。
  • 使用ImageIO
  • 以PNG格式序列化/反序列化图像
  • 图像在视觉上相等,但原始像素数据(字节数组)不同

这导致结论是ImageIO PNGImageWriter在写入时重新排列颜色映射中的条目,从而产生不同的像素数据/颜色映射。

这基本上给我们留下了两个选择:

  1. 以不同方式序列化图像数据,以确保不修改色彩映射/像素数据。可以发送像素数据阵列以及颜色映射阵列和图像的高度/宽度,然后在客户端准确地重新创建图像。这是相当多的代码,可能还有关于SO的其他问题。

  2. 不要依赖像素数据/颜色图相同。使用((IndexColorModel) levelBufferedNew.getColorModel()).getTransparentPixel()的值来测试/设置透明度而不是硬编码值-1。这几乎不需要对代码进行其他更改。

  3. 注意:这些解决方案仅适用于TYPE_BYTE_INDEXED(13)图像。

    对于更通用(但可能更慢)的方法,使用原始答案中的代码设置透明部分,并使用(levelBufferedNew.getRGB(x, y) >> 24) == 0测试透明度。这应该适用于TYPE_INT_ARGB或TYPE_4BYTE_ABGR。

    原始回答:

    为什么不尝试使用普通的Java2D,而不是在字节数组级别摆弄图像? ; - )

    类似的东西:

    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();
    }
    

    ......应该有用。

    PS:由于图片使用IndexColorModel,您可以使用getTransparentPixel()获取透明像素索引,而不是依赖于某个索引(-1/255)。然后你仍然可以在字节数组级别进行操作。 ; - )