Converting a buffered image into an int[] array, then converting it back

Long story short, I want to get the int[] array of RGB values from a BufferedImage
The original BufferedImage is called startImage, for which the RGB values go into startPixels

Next I have another int array called endPixels, and an image called endImage. When the program is done, endPixels will be dithered from startPixels but for the time being they are identical.

Then I want endPixels to be converted to endImage, which is then written to a file.

Here is the code:

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class DitherMain {

	static String imageString = "/IMG_3462.jpg";
	
	BufferedImage startImage, endImage;
	int[] startPixels,endPixels;
	int width, height;
	
	public static void main(String[] args){
		new DitherMain(loadImage(imageString));
	}
	
	//this object transforms the old image and writes the new image
	DitherMain(BufferedImage img){
		//filing the image into startpixels (this works)
		startImage = img;
		width = img.getWidth();
		height = img.getHeight();
		startPixels = new int[width*height];
		img.getRGB(0, 0, width, height, startPixels, 0, width);

		transformPixels();
		
		//putting endPixels in endImage (does not work)
		endImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		WritableRaster raster = (WritableRaster) endImage.getData();
		raster.setPixels(0,0,width,height,endPixels);
		
		//writing the file for endImage into the harddrive (may or may not work)
		File file = new File("/RESULT.png");
		try {
			ImageIO.write(endImage, "jpg", file);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	void transformPixels(){
		//endPixels will one day be different from startPixels, but for now it is idenitcial
		endPixels = startPixels;
	}
	
	//this method just loads a specific buffered image
	public static BufferedImage loadImage(String fileName){	
		BufferedImage img;
		
		try{
			img = ImageIO.read(DitherMain.class.getResource(fileName));
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		return img;
	}
}

And got this error message in the console:

Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 1228800
at java.awt.image.SinglePixelPackedSampleModel.setPixels(Unknown Source)
at java.awt.image.WritableRaster.setPixels(Unknown Source)
at sun.awt.image.SunWritableRaster.setPixels(Unknown Source)
at DitherMain.(DitherMain.java:34)
at DitherMain.main(DitherMain.java:17)

Line 34 is

		raster.setPixels(0,0,width,height,endPixels);

But I don’t see what’s wrong with this… does it have to do with storing an ARGB as an int, because I have no idea.
It says some array is going out of bounds. 1228800 is the height*width of the test image I’m using, but I don’t see what should be out of bounds

I really don’t understand what the problem with this code is, but seeing as I’ve never written something like this before I assume there are probably a lot.
In any case, can any of you guys shed some light on this?

Thanks

Like most methods in java, indexes are zero based, so I would imagine you would want to use
raster.setPixels(0,0,width-1,height-1,endPixels);

This is what I would do:

if (image.getType() != BufferedImage.TYPE_INT_ABGR) {
    BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
    tmp.getGraphics().drawImage(image, 0, 0, null);
    image = tmp;
}

int[] srcPixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();

BufferedImage out = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
int[] outPixels = ((DataBufferInt)out.getRaster().getDataBuffer()).getData();

//do something.. like copy pixels from A to B
System.arraycopy(srcPixels, 0, outPixels, 0, srcPixels.length);

//write to file...

Also note that you specify the output format as “png” but then use the JPG extension – maybe a typo?

setPixels() doesn’t do what you think it does. It expects the data to be unpacked, with ARGB in 4 separate ints, not 1. Therefore it’s expecting your array to be 4 times the size it is, and throwing the exception.

You want to try setDataElements() instead.

He simply wants a reference to the actual pixels.

int[] rgb = ((DataBufferInt)bufImg.getRaster().getDataBuffer()).getData();

No need to push the pixels back into the image. These are the pixels in the image.

You’re assuming that’s what the OP wants, though! @davedes had already mentioned that method. I was simply answering the OP’s question / confusion about why he was getting an ArrayIndexOutOfBoundsException.

I usually use the direct data buffer method you mention, but to do so you have to understand the trade-offs between using get/setDataElements() and directly manipulating the data buffer. Namely that get/setDataElements() will perform an extra copy of the data, while direct data buffer access will stop the image from being accelerated by the graphics card. If all the OP wants to do (in the long term) is save the image to a file, then direct access is fine - if the code will eventually be used for repeated drawing to the screen it may not be.

[quote]while direct data buffer access will stop the image from being accelerated by the graphics card.
[/quote]
Is there any way to “re-flag” it for acceleration after making some changes to the array?

Is there any way to “re-flag” it for acceleration after making some changes to the array?
[/quote]
There used to be. No idea if it still works (maybe someone else knows for sure). Use -Dsun.java2d.allowrastersteal=true or otherwise set that property as early as possible (before Java2d gets initialized by anything). You’ll then need to mark the image as dirty every time you modify the array - used to be recommended to draw a 1x1 transparent rectangle on it.

More in this old message - http://www.java-gaming.org/topics/software-rendering/12453/msg/100335/view.html#msg100335

From what I know, every bufferedimage will try and be accelerated after it is drawn some number of times. So if you de-accelerate it, it should eventually be re accelerated as long as you don’t change anything on it. Problem is most people want to change things every frame or so which kills performance.

Yay! I got it to work!
It takes about 1 or 2 seconds for an image of 960 * 1280, but I don’t care about performance. This project is just a hobby project I’m making to filter images.
I’m sure there’s a much faster way than this, but here’s the final code
It splits every color into RGB components (3 separate arrays) then it codes it into the image in 3 separate “bands”
At the moment, it just exports the same image that’s imported, but now that I have the RGB arrays at my disposal, I can now edit the arrays to get some cool effects

Here’s the code if anyone wants to do something similar in the future
It’ll need a lot of cleaning up, but this is functional
http://pastebin.java-gaming.org/fe8b0023139
Thanks

No, no, no, no and no! ;D If you grab the data array, the BufferedImage is permanently de-accelerated (unless you try the semi-official hack mentioned above).