OpenGL:Tutorials:Taking a Screenshot

From GPWiki
Jump to: navigation, search

Okay so you're all excited about your new OpenGL game, and you're about to post on a forum or perhaps email some friends about it. So you get playing and at just the exciting bit, you get a friend to reach over and hit the Print Screen key for you. Excitedly, you quit out and load up an image editor to empty window?

The problem

What has happened is that your system is using hardware acceleration, and is writing graphics data straight to the video card, and the operating system can't get to the pixels, so it screenshots nothing.

I should point out it's different on different systems, so on some systems native (provided by the os) screenshots will work, on other systems or operating systems it wont.

The solution

The solution is to capture the keypress event of the Print Screen key, and then use OpenGL to save a screenshot into a directory somewhere.

If using SDL:

if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
	// call your screenshot function

Capturing the screenshot

It all comes down the the glReadPixels function. This is an example using SDL.

Create a destination SDL surface.

SDL_Surface * surf = SDL_CreateRGBSurface(SDL_SWSURFACE, window_width, window_height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
if (surf == NULL) return;

Read the whole buffer into the surface data array.

glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels);

The OpenGL data is from bottom-of-window to top-of-window. There may be a way to change this. You will need to flip your surface to save it.

SDL_Surface * flip = flipVert(surf);
if (flip == NULL) return;

Then we just save the destination (my function has a string parameter, filename).

SDL_SaveBMP(flip, filename.c_str());

And clean up when finished.


My flipVert function

This isn't probably the best way to flip a sdl surface, but it does work, and it's only used for screenshots (because the rest of the game is OpenGL)

SDL_Surface* flipVert(SDL_Surface* sfc)
	SDL_Surface* result = SDL_CreateRGBSurface(sfc->flags, sfc->w, sfc->h,
		sfc->format->BytesPerPixel * 8, sfc->format->Rmask, sfc->format->Gmask,
		sfc->format->Bmask, sfc->format->Amask);
	if (result == NULL) return NULL;
	Uint8* pixels = (Uint8*) sfc->pixels;
	Uint8* rpixels = (Uint8*) result->pixels;
	Uint32 pitch = sfc->pitch;
	Uint32 pxlength = pitch*sfc->h;
	for(int line = 0; line < sfc->h; ++line) {
		Uint32 pos = line * pitch;
		memcpy(&rpixels[pos], &pixels[(pxlength-pos)-pitch], pitch);
	return result;