I am looking for a way to screenshot my GLCanvas
programmatically without awt Robot
.
Here is my current setup:
Constructor:
glcaps = new GLCapabilities(GLProfile.get(GLProfile.GL2));
glcaps.setDoubleBuffered(true);
glcaps.setHardwareAccelerated(true);
glcanvas = new GLCanvas(glcaps);
glcanvas.setSize(720, 720);
glcanvas.addGLEventListener(this);
glcanvas
is declared as an instance variable: GLCanvas glcanvas
OpenGL init:
@Override
public void init(GLAutoDrawable glad) {
GL2 gl = glad.getGL().getGL2();
glu = new GLU();
gl.glEnable(GL2.GL_DEPTH_TEST);
gl.glDepthFunc(GL2.GL_LEQUAL);
gl.glShadeModel(GL2.GL_SMOOTH);
gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
gl.glClearColor(0f, 0f, 0f, 1f);
// Some camera related code not shown
}
OpenGL display:
public void display(GLAutoDrawable glad) {
GL2 gl = glad.getGL().getGL2();
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
...
// Orient camera and draw a simple cube
...
gl.glFlush();
}
Screenshot method:
BufferedImage b = new BufferedImage(glcanvas.getWidth(), glcanvas.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = b.createGraphics();
glcanvas.setupPrint(glcanvas.getWidth(), glcanvas.getWidth(), 50, 50, 50);
glcanvas.print(g);
try {
ImageIO.write(b, "png", new File("test.png"));
} catch (IOException ex) {
// Error handling
}
glcanvas.releasePrint();
g.dispose();
This method works, as in executes without crashing, but the png file I get is just black with no cube. I also tried using glReadPixels but that does not work either as it just gives me a buffer full of 0's (black).
I think that the problem is that I am not reading glcanvas
from the draw thread. Is this the error, and if so, how can I solve it?
All answers appreciated!
First, you have to be sure that you read the framebuffer after what you want to catch has been rendered.
Second, you can do something like this:
protected void saveImage(GL3 gl3, int width, int height) {
try {
BufferedImage screenshot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = screenshot.getGraphics();
ByteBuffer buffer = GLBuffers.newDirectByteBuffer(width * height * 4);
// be sure you are reading from the right fbo (here is supposed to be the default one)
// bind the right buffer to read from
gl3.glReadBuffer(GL_BACK);
// if the width is not multiple of 4, set unpackPixel = 1
gl3.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
// The color are the three consecutive bytes, it's like referencing
// to the next consecutive array elements, so we got red, green, blue..
// red, green, blue, and so on..+ ", "
graphics.setColor(new Color((buffer.get() & 0xff), (buffer.get() & 0xff),
(buffer.get() & 0xff)));
buffer.get(); // consume alpha
graphics.drawRect(w, height - h, 1, 1); // height - h is for flipping the image
}
}
// This is one util of mine, it make sure you clean the direct buffer
BufferUtils.destroyDirectBuffer(buffer);
File outputfile = new File("D:\\Downloads\\texture.png");
ImageIO.write(screenshot, "png", outputfile);
} catch (IOException ex) {
}
}
I filled some comment inside, if something is still unclear, don't hesitate to ask further