Drawing a BufferedImage to a BufferStrategy's Graphics object is slow

I am experimenting with full screen exclusive mode and I found that drawing a BufferedImage to a BufferStrategy’s Graphics object is slow. I’m using a BufferedImage as opposed to a VolatileImage because I want to constantly manipulate it on the pixel level.

BufferedImage buffer = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
int[] pixels = ((DataBufferInt)buffer.getRaster().getDataBuffer()).getData();

Here’s how I’m drawing it:

g = bufferStrategy.getDrawGraphics();
if (g != null) {
  if (!bufferStrategy.contentsLost()) {        
    g.drawImage(buffer, imageX, imageY, imageWidth, imageHeight, null);
    bufferStrategy.show();        
  }
  g.dispose();
}

The code certainly works, but I timed g.drawImage() and I found that it can take up to 10 milliseconds. If I render directly to a Canvas within a JFrame, it takes a fraction of a millisecond.

In full screen exclusive mode, it might be forced to convert the BufferedImage for compatibility reasons before rendering, which might be eating up all that time. Or, it might be waiting for VSync or something.

Has anyone else noticed these performance problems?

I think that most people who are concerned with performance use OpenGL/LWJGL in some form or another.

I’d be more than happy to if there were a way to manipulate the pixels of images like you can do with a BufferedImage.

I suppose you can, it simply takes a little work.

You store your image data in a ByteBuffer or byte[] and manipulate a set of four bytes (most likely with a helper method like [icode]setPixel(int x, int y, int pixel)[/icode]) and then use glTexSubImage2D to send the updated ByteBuffer to OpenGL for your texture.

This may clarify what I mean:

private ByteBuffer buffer;

private byte[] bytes;

private int width;

private int height;

public BufferedImage(int width, int height)
{
	this.width = width;
	this.height = height;
	bytes = new byte[(width * height) << 2];
	buffer = ByteBuffer.allocateDirect((width * height) << 2);
}

public void flush()
{
	buffer.clear();
	buffer.rewind();
	buffer.put(bytes);
	buffer.flip();
}

public ByteBuffer getBuffer()
{
	return buffer;
}

public void clear()
{
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			setPixel(x, y, 0xFFFFFF00);
		}
	}
}

public int getPixel(int x, int y)
{
	int index = y * width + x;
	byte r = bytes[index], g = bytes[index + 1], b = bytes[index + 2], a = bytes[index + 3];
	return ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF);
}

public void setPixel(int x, int y, int pixel) 
{
	int index = y * width + x;
	byte r = (byte) ((pixel >> 24) & 0xFF);
	byte g = (byte) ((pixel >> 16) & 0xFF);
	byte b = (byte) ((pixel >> 8) & 0xFF);
	byte a = (byte) (pixel & 0xFF);
	bytes[index] = r; bytes[index + 1] = g; bytes[index + 2] = b; bytes[index + 3] = a;
}

Then just send do


BufferedImage img = new BufferedImage(100, 100);
img.clear();
// set pixels
img.flush();
ByteBuffer forOpenGL = img.getBuffer();

and update the data for OpenGL.

You can also walk the ByteBuffer as an IntBuffer, which does that for you:
http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#asIntBuffer()

I would also avoid having separate byte[] and ByteBuffer.

[quote]I suppose you can, it simply takes a little work.
[/quote]
Thanks for the code guys.

How’s the performance on that technique?

I haven’t really tested it.

If you wanted to, you could also look into framebuffers, but the posted solution would probably be the easiest, except with a few performance enhancements and tweaks like the ones BurntPizza suggested.