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.