Java Unpacking Native Image

I use the compatible native image returned from GraphicsConfiguration for the buffers in my rendering code.

The problem I now have with this, is how can I unpack/pack these pixels? I need to modify the RGB values… but it doesn’t seem to be working even though the DataBuffer is still of type DataBufferInt.

The integer pixels appear to be packed in a different format than TYPE_INT_RGB.

Is there a more format independent method to unpacking/packing int pixels than:


	public static int[] unpackInt(int argb, int type) {
		int[] vals = null;
		int p1 = 0;
		int p2 = 1;
		int p3 = 2;
		int p4 = 3;
		switch (type) {
		case TYPE_RGB:
			vals = new int[3];
			vals[p1] = argb >> 16 & 0xFF;
			vals[p2] = argb >> 8 & 0xFF;
			vals[p3] = argb & 0xFF;
			break;
		case TYPE_RGBA:
		case TYPE_ARGB:
			vals = new int[4];
			vals[p4] = argb & 0xFF;
			vals[p3] = argb >> 8 & 0xFF;
			vals[p2] = argb >> 16 & 0xFF;
			vals[p1] = argb >> 24 & 0xFF;
			break;
		default:
			throw (new IllegalArgumentException(
					"type must be a valid field defined by ColorUtils class"));
		}
		return vals;
	}

	public static int packInt(int... rgbs) {
		if (rgbs.length != 3 && rgbs.length != 4) {
			throw (new IllegalArgumentException(
					"args must be valid RGB, ARGB or RGBA value."));
		}
		int color = rgbs[0];
		for (int i = 1; i < rgbs.length; i++) {
			color = (color << 8) + rgbs[i];
		}
		return color;
	}

BufferedImage.setRGB()?

setRGB causes the BufferedImage to become unmanaged, which is REALLY REALLY slow. The only way to manually modify pixels and maintain the advantages of keeping the data managed in video memory is through modifying the data array directly.

Er, I think you got that all backwards! ::slight_smile:

Really? That’s mainly based on information I’ve gathered form other people and Sun’s documentation.

Can you be a little more specific?

I just tried commenting out the data fetching code and replaced it with setRGB and getRGB. It dropped my solid 60fps rendering to 4fps. :expressionless:

His point is that you can’t modify the data array either as that too will make it unmanaged. The image data is stored on the GPU, so once you’ve retrieved the data array Java can’t assume that the CPU array is in sync with the GPU data, so it must unmanage it.

Sorry, I was replying on my phone so kept it brief. I thought your comment was confusing to the OP - hadn’t twigged you were the OP! :wink:

Your statement is wrong though, as is this in many ways -

A BufferedImage is always stored in Java memory. It’s always rendered to in Java memory. When the image is drawn to the screen (or another accelerated surface) the data is sent to the graphics card. If you draw an image more than once without changing it the graphics pipeline will cache a copy on the graphics card. Every time the image is updated any graphics card copy is disposed of. Therefore, the data is not really stored on the GPU.

If you alter a BufferedImage before each time you draw it (ie. every frame) then a copy will never be cached on the GPU. Therefore, it makes no difference whether it’s a managed or unmanaged image.

The fastest way to work with BufferedImage pixel data in Java is to grab the data array directly. This makes the image unmanaged, but often this is not an issue for the reason above. Managed images are only useful for image resources that remain unchanged for a period of time.

That you got a lower frame rate using set/getRGB is not a surprise. These involve extra data coercion and copying. They almost certainly don’t un-manage an image any more (most articles about this were written Java 1.4 time, and a lot has changed). They will be slower for what you’re doing though.

To answer your original post, don’t use compatible images for this at all - it can cause a wealth of problems as you don’t know what format you’re getting, and on some systems (Linux) there was and may still be a bug where you could never get the data array from a compatible image.

Just create a BufferedImage directly using TYPE_INT_RGB, TYPE_INT_ARGB or TYPE_INT_ARGB_PRE. You’ll have a guaranteed format to work with so won’t have to do any data coercion in your code - the pipes for drawing these formats to the screen are some of the most optimized, and will almost certainly outperform the pack / unpack code you have to use to work around it.

Use the pre-multiplied image format if you’re doing any direct manipulation involving alpha (eg. custom blending) - it will save you a bunch of headaches.

Hope that helps you out. The problem with all this Java2D, managed image stuff, etc. is that a little knowledge is dangerous! :wink:

Ok that certainly clears things up. Thank you!

I’m a bit confused though… I do get a performance improvement by using CompatibleImage for the on-screen image buffer. You think I should switch to TYPE_INT_ARGB_PRE?