June 2, 2007

Taking a Screenshot From an OpenGL Application

Posted in OpenGL tagged , , at 5:18 am by Eon Strife

A while ago, I was asked by my assistant about how to take a screenshot from an OpenGL Application. Obviously, the easiest way is by pressing the “PrintScreen” button on our keyboard. But, we should do it programmatically which is more elegant. Consider that we need to take multiple screenshots without switching between our program with image editor program. I believe some games already implement this feature, by assigning a specific key for screen capturing.

 

To make life easier, get an image loading/saving library first, because writing code for loading and saving image is pretty cumbersome. There are many image loading/saving libraries floating in the net, such as DEVIL, FREEIMAGE, and CORONA. In this tutorial, I’m using CORONA which can be downloaded from http://corona.sourceforge.net/.

 

Some OpenGL functions we use are : glDrawBuffer, glReadBuffer, and glReadPixels.

 

We use glDrawBuffer to specify to which color buffer the image is rendered. By default, it’s front(GL_FRONT) for single-buffered and back(GL_BACK) for double-buffered application. We should call the function before calling the rendering function. As for glReadBuffer, we use it to determine from which color buffer the image is read (The default scheme is the same as glDrawBuffer). We may call this function after the rendering function and before calling glReadPixels which is used to read the color buffer and to store the image in somewhere in the memory. Here’s the example code :

 

Init(); //doing some initializations
glDrawBuffer(GL_BACK);
DrawGLScene(); //doing drawing here
glReadBuffer(GL_BACK);
GLvoid *imageData = malloc(WIDTH*HEIGHT*(BIT_R+BIT_G+BIT_B+BIT_A)); //Allocate memory for storing the image
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, imageData); //Copy the image to the array imageData

 

WIDTH, HEIGHT, BIT_R, BIT_G, BIT_B, BIT_A are the symbolic constants defined with (#define). They are width of the window, height of the window, number of bits for red component, number of bits for green component, number of bits for blue component, and number of bits for alpha component, respectively. The first and second parameters of glReadPixels are the (x, y) coordinate of the starting point with (0, 0) located in the lower left corner. The third and fourth parameters are the width and height of the region to be read. The fifth is the pixel format, the sixth is the data type for the pixel, and the last parameter is the storage for the image.

After we have obtained the image, we need to save it to a file. CORONA comes to the rescue :

 

corona::Image *myImage = corona::CreateImage(WIDTH, HEIGHT, corona::PF_R8G8B8A8, imageData);
corona::FlipImage(myImage, corona::CA_X); //Flip the image
corona::SaveImage(fileName, corona::FF_PNG, myImage); //Save it

 

We create image by calling CreateImage, with the first and second parameters determine the dimension, the third parameter is for pixel format, and the last parameter is the image we captured beforehand. After that, we flip the image because the image captured by CORONA is upside-down. And finally, we save the image by calling SaveImage, with the first parameter is null-terminated string for the filename, the second parameter for the image format, and the last is image object we created from CreateImage.

Don’t forget to do a cleanup after it’s over :

 

delete myImage;
free(imageData);

For more information, read OpenGL Programming Guide a.k.a The Red Book.

5 Comments »

  1. randemonium said,

    Thanks so much for posting this. It’s super helpful. But something’s wrong here. I tried to compile your example in VC6 and it has a problem with the corona::ImageCreate function. It’s looking for an overloaded function that’ll takes 4 arguments. It can’t find one. I checked and, what d’ya know, there really isn’t one. Just 3 or 5. The function seems to work a little differently than you describe. Can you help me out?

  2. randemonium said,

    or rather corona::CreateImage()

  3. randemonium said,

    It seems as if CreateImage() is for creating a blank image and not for writing to disk at all. To do that with Corona seems to be a great deal more complex than a single function call and I can’t even get any of the necessary libaries to link. DevIL looks infinitely better suited to the task. Thank you for at least mentioning it in passing! I’m going to go with that.

  4. Eon Strife said,

    Randemonium,
    I compiled using VC6, and it worked.

    CreateImage indeed has two overloaded functions, one which has four parameters and another has five parameters.

    As the one with four parameters, the fourth parameter is an optional one. If you just call the function with three parameters, it will create a blank image. However, if you provide the array of pixels as the fourth parameter, it will create and initialize an image based on the content of the array.

  5. […] in einer Suchmaschine nach “OpenGL Screenshot” sucht, wird auf einige Treffer stoßen (1, 2, 3, 4, 5), die den erforderlichen Code beschreiben. Dies nutzt einem Android-Anwender aber nichts, da er […]


Leave a comment