1

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?

  • Thats just getting the bytes in a png file – lucas.ss.05 Jan 04 '21 at 20:19
  • I wonder, is this what you need? https://stackoverflow.com/questions/15414259/java-bufferedimage-to-byte-array-and-back – HoRn Jan 04 '21 at 20:25
  • No, I want to get the RGBA data, nothing else. I need to get the pixels from the raster in this format but the pixel formats are all different for different images I just want a method which can do this in one abstraction – lucas.ss.05 Jan 04 '21 at 20:27
  • Is this for LWJGL? Here's my answer from a few years ago. Should plug right into your code. https://stackoverflow.com/questions/48875161/efficiently-extracting-rgba-buffer-from-bufferedimage/48875283#48875283 – markspace Jan 04 '21 at 20:33
  • MadProgrammer, from 2014 ->https://stackoverflow.com/a/27538044/2148953 – aran Jan 04 '21 at 20:34
  • @markspace that did it mostly, still getting errors on lots of the images – lucas.ss.05 Jan 04 '21 at 20:45

3 Answers3

4

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...

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • Ive loaded a PNG file, but the color model is different depending on the file and the pixel formats arent always 32 bits per pixel ( r g b a ) so it gives errors. willl this fix it? – lucas.ss.05 Jan 04 '21 at 23:40
  • I updated the code examples to use `ImageIO.read(...)` instead of `new BufferedImage`, to clarify usage. You should be able to use a PNG or any other file format, as long as `ImageIO.read(...)` can read it. The color model of the image does not matter for `BufferedImage.getRGB(...)` (it *aways* returns packed 32 bit ARGB values in sRGB color space). – Harald K Jan 05 '21 at 08:55
  • Your method worked, but I was still getting some errors on some images, so I decided to try another png library (pngj) which worked, but thank you! – lucas.ss.05 Jan 05 '21 at 12:36
  • I don't know what error you are talking about (you have never posted it, nor a sample file causing the problem). It sounds like a problem that is completely unrelated to the `int ARGB` -> `4 byte RGBA` conversion... But I'm happy it works for you. – Harald K Jan 05 '21 at 12:49
0

Eventually I just used the java library PNGJ to load it, and loaded each row accordingly in RGBA format:

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;

Which I then shifted into RGBA:

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

Start with BufferedImage#getRGB which returns ARGB values no matter the ColorModel of the imgage - check its documentation.

Then use the default instance of ColorModel to either get the components (getRed(), getGreen(), ...) or to get an int array of the components (e.g. getComponents()). Or just split the value returned by getRGB(), format is described in ColorModel#getRGBdefault.

Eventually the image (or its raster data) can be converted to TYPE_4BYTE_ABGR, so the raster data can be used directly (just a guess, I never done it)

nobody
  • 1
  • 1