Difference between revisions of "OpenGL:Tutorials:Taking a Screenshot"

From GPWiki
Jump to: navigation, search
(Created page with "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...")
 
Line 10: Line 10:
  
 
If using SDL:
 
If using SDL:
<source lang="c">
+
<source lang="cpp">
 
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
 
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
 
// call your screenshot function
 
// call your screenshot function
Line 20: Line 20:
  
 
Create a destination SDL surface.  
 
Create a destination SDL surface.  
<source lang="c">
+
<source lang="cpp">
 
SDL_Surface * surf = SDL_CreateRGBSurface(SDL_SWSURFACE, window_width, window_height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
 
SDL_Surface * surf = SDL_CreateRGBSurface(SDL_SWSURFACE, window_width, window_height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
 
if (surf == NULL) return;
 
if (surf == NULL) return;
Line 26: Line 26:
  
 
Read the whole buffer into the surface data array.
 
Read the whole buffer into the surface data array.
<source lang="c">
+
<source lang="cpp">
 
glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels);
 
glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels);
 
</source>
 
</source>
  
 
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.
 
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.
<source lang="c">
+
<source lang="cpp">
 
SDL_Surface * flip = flipVert(surf);
 
SDL_Surface * flip = flipVert(surf);
 
if (flip == NULL) return;
 
if (flip == NULL) return;
Line 38: Line 38:
  
 
Then we just save the destination (my function has a string parameter, filename).
 
Then we just save the destination (my function has a string parameter, filename).
<source lang="c">
+
<source lang="cpp">
 
SDL_SaveBMP(flip, filename.c_str());
 
SDL_SaveBMP(flip, filename.c_str());
 
</source>
 
</source>
  
 
And clean up when finished.
 
And clean up when finished.
<source lang="c">
+
<source lang="cpp">
 
SDL_FreeSurface(flip);
 
SDL_FreeSurface(flip);
 +
</source>
 +
 +
== My flipVert function ==
 +
<source lang="cpp">
 +
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;
 +
}
 
</source>
 
</source>

Revision as of 09:16, 26 December 2012

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 find...an 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;
SDL_FreeSurface(surf);

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

SDL_SaveBMP(flip, filename.c_str());

And clean up when finished.

SDL_FreeSurface(flip);

My flipVert function

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;
}