In jME we don't use setRGB either, we create our BufferedImage and then directly access the DataBuffer of the Raster... which can be manipulated as an array of ints...
int[] ibuf = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
Our data is grabbed from an IntBuffer that is populated from the GL thread (throttled though to only grab every X ms) using:
public void grabScreenContents(IntBuffer buff, int x, int y, int w, int h) {
GL11.glReadPixels(x, y, w, h, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE,
buff);
}
Then it's a simple matter to copy from the IntBuffer to the int array backing the BufferedImage:
buf.clear(); // Note: clear() resets marks and positions,
// but not data in buffer.
//Grab pixel information and set it to the BufferedImage info.
for (int x = height; --x >= 0; ) {
buf.get(ibuf, x * width, width);
}
You go on to draw the image as normal.
The copying portions of this code are extremely fast, the slow part is just glReadPixels, which we throttle to only happen a certain number of times per second... There's no real reason to do it any faster than screen refresh rate for example.
That all said, I'm anxious to see .96 lwjgl with it's direct integration into AWT (or so I've heard.)