Ok, here’s a self contained test case. It defines a GLEventListener that draws a yellow rectangle centered on a magenta background, then saves it to an Image. The main() method creates a GLPbuffer, adds the listener, then displays the saved image in a window. The rectangle comes out in the wrong place. But if you replace the pbuffer with a GLCanvas, it looks correct.
Everything after the GLEventListener definition is the code for grabbing the image, which I copied from http://www.felixgers.de/teaching/jogl/imagingProg.html. I’m pretty sure the problem has nothing to do with this code: if I use it on a GLCanvas, it produces an accurate copy of what’s shown on the screen.
Peter
import javax.media.opengl.*;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.nio.*;
public class BugDemo
{
public static void main(String args[])
{
JFrame fr = new JFrame();
GLPbuffer pbuffer = GLDrawableFactory.getFactory().createGLPbuffer(new GLCapabilities(), new DefaultGLCapabilitiesChooser(), 400, 300, null);
GLListener listener = new GLListener();
pbuffer.addGLEventListener(listener);
pbuffer.display();
pbuffer.destroy();
fr.add(new JLabel(new ImageIcon(listener.img)), BorderLayout.CENTER);
// To see a correct rendering, remove the six lines immediately above and replace them
// with the following four lines:
// GLCanvas canvas = new GLCanvas();
// canvas.addGLEventListener(new GLListener());
// canvas.setPreferredSize(new Dimension(400, 300));
// fr.add(canvas, BorderLayout.CENTER);
fr.pack();
fr.setVisible(true);
}
public static class GLListener implements GLEventListener
{
public Image img;
public void init(GLAutoDrawable drawable)
{
GL gl = drawable.getGL();
gl.glDisable(GL.GL_LIGHTING);
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
{
}
public void display(GLAutoDrawable drawable)
{
GL gl = drawable.getGL();
gl.glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT+GL.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustum(-10.0, 10.0, -10.0, 10.0, 1.0, 100.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glLoadMatrixd(new double [] {
-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
}, 0);
gl.glColor3f(255.0f, 255.0f, 0.0f);
gl.glBegin(GL.GL_QUADS);
gl.glVertex3f(-20.0f, 20.0f, 10.0f);
gl.glVertex3f(-20.0f, -20.0f, 10.0f);
gl.glVertex3f(20.0f, -20.0f, 10.0f);
gl.glVertex3f(20.0f, 20.0f, 10.0f);
gl.glEnd();
img = copyFrame(gl, 400, 300);
}
public void displayChanged(GLAutoDrawable drawable, boolean arg1, boolean arg2)
{
}
}
private static ByteBuffer getFrameData( GL gl, ByteBuffer pixelsRGB, int width, int height )
{
// Read Frame back into our ByteBuffer.
gl.glReadBuffer( GL.GL_BACK );
gl.glPixelStorei( GL.GL_PACK_ALIGNMENT, 1 );
gl.glReadPixels( 0, 0, width, height,
GL.GL_RGB, GL.GL_UNSIGNED_BYTE,
pixelsRGB );
return pixelsRGB;
}
private static BufferedImage copyFrame( GL gl, int width, int height )
{
// Create a ByteBuffer to hold the frame data.
java.nio.ByteBuffer pixelsRGB = ByteBuffer.allocateDirect( width * height * 3 );
// Get date from frame as ByteBuffer.
getFrameData( gl, pixelsRGB, width, height );
return transformPixelsRGBBuffer2ARGB_ByHand( pixelsRGB, width, height );
}
// Copies the Frame to an integer array.
// Do the necessary conversion by hand.
//
private static BufferedImage transformPixelsRGBBuffer2ARGB_ByHand( ByteBuffer pixelsRGB, int width, int height )
{
// Transform the ByteBuffer and get it as pixeldata.
int[] pixelInts = new int[ width * height ];
// Convert RGB bytes to ARGB ints with no transparency.
// Flip image vertically by reading the
// rows of pixels in the byte buffer in reverse
// - (0,0) is at bottom left in OpenGL.
//
// Points to first byte (red) in each row.
int p = width * height * 3;
int q; // Index into ByteBuffer
int i = 0; // Index into target int[]
int w3 = width * 3; // Number of bytes in each row
for (int row = 0; row < height; row++) {
p -= w3;
q = p;
for (int col = 0; col < width; col++) {
int iR = pixelsRGB.get(q++);
int iG = pixelsRGB.get(q++);
int iB = pixelsRGB.get(q++);
pixelInts[i++] =
0xFF000000 | ((iR & 0x000000FF) << 16) |
((iG & 0x000000FF) << 8) | (iB & 0x000000FF);
}
}
// Create a new BufferedImage from the pixeldata.
BufferedImage bufferedImage =
new BufferedImage( width, height,
BufferedImage.TYPE_INT_ARGB);
bufferedImage.setRGB( 0, 0, width, height,
pixelInts, 0, width );
return bufferedImage;
}
}