a fast screenshot function

Hello together,

here is an example to make screenshots from OpenGL context very quickly. The file is written in uncompressed TARGA (*.tga) format.


public static final int TARGA_HEADER_SIZE = 18;

void screenshot(GL gl, int width, int, height, File file) {
  try {
    RandomAccessFile out = new RandomAccessFile(file, "rw");
    FileChannel ch = out.getChannel();
    int fileLength = TARGA_HEADER_SIZE + width * height * 3;
    out.setLength(fileLength);
    MappedByteBuffer image = ch.map(FileChannel.MapMode.READ_WRITE, 0, fileLength);

    // write the TARGA header
   image.put(0, (byte) 0).put(1, (byte) 0);
   image.put(2, (byte) 2); // uncompressed type
   image.put(12, (byte) (width & 0xFF)); // width
   image.put(13, (byte) (width >> 8)); // width
   image.put(14, (byte) (height & 0xFF)); height
   image.put(15, (byte) (height >> 8)); height
   image.put(16, (byte) 24); // pixel size
              
   // go to image data position
   image.position(TARGA_HEADER_SIZE);
   // jogl needs a sliced buffer
   ByteBuffer bgr = image.slice();
   // read the BGR values into the image buffer
   gl.glReadPixels(0, 0, width, height, GL.GL_BGR,
                      GL.GL_UNSIGNED_BYTE, bgr);

   // close the file channel
   ch.close();
  } catch (Exception e) {
     e.printStackTrace();
  }
}

I think it is useful.

bye
Carsten

Thanks for the code. I’ve added it to JOGL Issue 131. We need to add a bunch of utility classes to JOGL to make it more useful.

Why not use ImageIO instead? That way you can save to all supported formats from the bytebuffer. Or perhaps even print it too :slight_smile:

[quote]Why not use ImageIO instead? That way you can save to all supported formats from the bytebuffer. Or perhaps even print it too :slight_smile:
[/quote]
The ImageIO class is ok, but much slower. I’ve used this function to make screenshots on the fly in Jake2. I’ve tested ImageIO too and the Quake2 game loop hangs a little bit on saving an image.

The trick in this example is to bypass the CPU for image writing. This function copies the graphics memory (AGP) to a memory mapped file.
I think only the DMA contoller has a lot to do :slight_smile:

An other fact is, that you don’t need extra memory allocation like int[].
There is no extra runtime (dynamic) garbage. And the MappedByteBuffer will be collected by GC after closing the file channel.

try it and you will see what I mean.

bye
Carsten

First of all, thanks for the amazing code snippet. Using ImageIO, i was getting pauses ~1 second for each snapshot, with this method there is no appreciable delay :smiley:

Unfortunately, there seems to be a problem with the resultant images. What should look something like

www.homepages.inf.ed.ac.uk/s9902505/actual.png

comes out like

www.homepages.inf.ed.ac.uk/s9902505/borked.png

I’m calling it with


screenshot( canvas.getGL(), canvas.getWidth(), canvas.getHeight(), imageFile );

where canvas is my GLCanvas.

Where am i going wrong?

Hi,

To fix this, either make sure your image width will always be a multiple of 4, or add the line

gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);

before your call to gl.glReadPixels()

Thanks for the tip on using file channels! I have just finished implementing code to capture directly to an AVI file (with sound). Anyone interested?

Kevin

Awesome, that’s got it. ;D

Thanks you for the test and for the hint with pixel alignment.
I had forgotten this issue.

Keving, I’m interested in your AVI tool.

bye
Carsten

I too am interested in encoding directly to a video file. Currently i’m using this screenshot code to capture a whole crapload of frames, which i then encode with other tools.
Skipping this step and encoding directly would be ideal.

Incidentally -as I understand it- avi is a container for various video and audio formats. What video format would be used?

Me too please :slight_smile:

I noticed you do not specify which color buffer the glreadpixels should capture the pix info from. On prompting of a screengrab I usually render the final pass of my app into an Auxilary Buffer and then dump the color information from the Aux buffer to file. That way your app doesn’t not have to be showing on the screen for the screendump to be successful.

I suppose, the code resulted in the Screenshot Class of JSR-231.
How about adding something like the AVI tool, too?

Could you send me the code for the AVI capturing, please?

Jens

how do you get the GLCanvas outside one of the jogl functions (init,display,reshape) ?

You shouldn’t really need to. The work you need to do to capture the screen contents should typically be called from within your GLEventListener.display() method.

[quote=“keving,post:6,topic:23419”]
I am :slight_smile: