Search code examples
javaimagepngbufferedimage

Getting a RGBA byte array from a BufferedImage Java


I have a BufferedImage and would like to get a byte array in the format R G B A (one channel per byte). How can I do this?


Solution

  • The easy way is to use BufferedImage.getRGB (which despite its name gives you ARGB values), and convert the packed int[] to byte[] four times as long. Input can be any file ImageIO can read, a PNG will work fine.

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

    A slightly more fun way, is to create a BufferedImage that is backed by a byte[] already in RGBA format, like this:

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

    Which one of the above examples is better to use, depends on the use case.

    • If you just need a one time conversion, the first one is probably easier to reason about and works just fine.

    • If you need to update the buffer many times over, the second approach might yield better performance.

    PS: I get the exact same results using both alternatives for all my test inputs, except the ones where the original is in grayscale (using ColorSpace.CS_GRAY). I believe this is a known issue that has troubled Java2D users for ages...