2

我正在尝试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));

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

水平:假

背景:真实

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

如果总体思路是错误的,我想听听另一个,我想要的只是能够准确地重新创建 2 个缓冲图像。

4

1 回答 1

2

编辑:到目前为止我们已经建立的:

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

由此得出结论,ImageIO PNGImageWriter在写入时重新排列颜色图中的条目,导致不同的像素数据/颜色图。

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

  1. 以不同的方式序列化图像数据,以确保不修改颜色映射/像素数据。可以发送像素数据数组,连同颜色映射数组和图像的高度/宽度,然后在客户端重新创建图像。这是相当多的代码,并且可能被关于 SO 的其他问题所涵盖。

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

注意:这些解决方案仅适用于 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)。然后您仍然可以在字节数组级别进行操作。;-)

于 2013-06-09T20:24:08.473 回答