从 BufferedImage 获取 RGBA 字节数组 Java

Getting a RGBA byte array from a BufferedImage Java

我有一个 BufferedImage 并且想要获得格式为 R G B A(每个字节一个通道)的字节数组。我该怎么做?

BufferedImage#getRGB 开始,其中 returns ARGB 值与图像的 ColorModel 无关 - 检查其 documentation.

然后使用 ColorModeldefault 实例获取组件(getRed()getGreen()、...)或获取组件的 int 数组(例如 getComponents())。或者直接拆分 getRGB() 返回的值,格式在 ColorModel#getRGBdefault.

中描述

最终图像(或其栅格数据)可以转换为TYPE_4BYTE_ABGR,所以栅格数据可以直接使用(只是猜测,我没有做过)

最简单的方法是使用 BufferedImage.getRGB(尽管它的名字给了你 ARGB 值),并将打包的 int[] 转换为 byte[] 四倍长。输入可以是任何 ImageIO 可以读取的文件,PNG 就可以了。

public static void main(String[] args) throws IOException {
    BufferedImage image = ImageIO.read(new File(args[0]));

    int[] argb = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
    byte[] rgba = intARGBtoByteRGBA(argb);
}

private static byte[] intARGBtoByteRGBA(int[] argb) {
    byte[] rgba = new byte[argb.length * 4];

    for (int i = 0; i < argb.length; i++) {
        rgba[4 * i    ] = (byte) ((argb[i] >> 16) & 0xff); // R
        rgba[4 * i + 1] = (byte) ((argb[i] >>  8) & 0xff); // G
        rgba[4 * i + 2] = (byte) ((argb[i]      ) & 0xff); // B
        rgba[4 * i + 3] = (byte) ((argb[i] >> 24) & 0xff); // A
    }

    return rgba;
}

一种更有趣的方法是创建一个 BufferedImage,它由已经采用 RGBA 格式的 byte[] 支持,如下所示:

public static void main(String[] args) throws IOException {
    BufferedImage image = ImageIO.read(new File(args[0]));

    ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
    WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), image.getWidth() * 4, 4, new int[] {0, 1, 2, 3}, null); // R, G, B, A order
    BufferedImage imageToo = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);

    // This array will be in the same R, G, B, A order
    byte[] rgbaToo = ((DataBufferByte) raster.getDataBuffer()).getData();

    // Draw the image onto the RGBA buffer, which will be updated immediately
    Graphics2D g = imageToo.createGraphics();
    try {
        g.setComposite(AlphaComposite.Src);
        g.drawImage(image, 0, 0, null);
    }
    finally {
        g.dispose();
    }
}

以上示例中的哪一个更好用,取决于用例。

  • 如果您只需要一次性转换,第一个可能更容易推理并且工作得很好。

  • 如果您需要多次更新缓冲区,第二种方法可能会产生更好的性能。

PS:我对所有测试输入使用两种替代方法得到完全相同的结果, 除了 原始灰度(使用 ColorSpace.CS_GRAY).我相信这是一个困扰 Java2D 用户多年的已知问题...

最终我只是使用 java 库 PNGJ 加载它,并以 RGBA 格式相应地加载每一行:

for(int j = 0; j < reader.imgInfo.rows;j++) {
        IImageLine row = reader.readRow();
        for(int b = 0; b < reader.imgInfo.cols; b++) { 
                int argb = ch == 3 ? ImageLineHelper.getPixelRGB8(row, b) : ch == 4 ? ImageLineHelper.getPixelARGB8(row, b) : 0xFF000000;

然后我将其转换为 RGBA:

pixels.write((argb & 0x00FF0000) >> 16);
pixels.write((argb & 0x0000FF00) >> 8 );
pixels.write((argb & 0x000000FF) >> 0 );//neatness lol
pixels.write((argb & 0xFF000000) >> 24);