Using just a given SDL_Window* and SDL_Renderer*, how can I create and save a screenshot in SDL 2.0?
Asked
Active
Viewed 8,494 times
3 Answers
11
Below is a function for saving a screenshot in SDL 2 taken from a library I'm currently writing.
bool saveScreenshotBMP(std::string filepath, SDL_Window* SDLWindow, SDL_Renderer* SDLRenderer) {
SDL_Surface* saveSurface = NULL;
SDL_Surface* infoSurface = NULL;
infoSurface = SDL_GetWindowSurface(SDLWindow);
if (infoSurface == NULL) {
std::cerr << "Failed to create info surface from window in saveScreenshotBMP(string), SDL_GetError() - " << SDL_GetError() << "\n";
} else {
unsigned char * pixels = new (std::nothrow) unsigned char[infoSurface->w * infoSurface->h * infoSurface->format->BytesPerPixel];
if (pixels == 0) {
std::cerr << "Unable to allocate memory for screenshot pixel data buffer!\n";
return false;
} else {
if (SDL_RenderReadPixels(SDLRenderer, &infoSurface->clip_rect, infoSurface->format->format, pixels, infoSurface->w * infoSurface->format->BytesPerPixel) != 0) {
std::cerr << "Failed to read pixel data from SDL_Renderer object. SDL_GetError() - " << SDL_GetError() << "\n";
delete[] pixels;
return false;
} else {
saveSurface = SDL_CreateRGBSurfaceFrom(pixels, infoSurface->w, infoSurface->h, infoSurface->format->BitsPerPixel, infoSurface->w * infoSurface->format->BytesPerPixel, infoSurface->format->Rmask, infoSurface->format->Gmask, infoSurface->format->Bmask, infoSurface->format->Amask);
if (saveSurface == NULL) {
std::cerr << "Couldn't create SDL_Surface from renderer pixel data. SDL_GetError() - " << SDL_GetError() << "\n";
delete[] pixels;
return false;
}
SDL_SaveBMP(saveSurface, filepath.c_str());
SDL_FreeSurface(saveSurface);
saveSurface = NULL;
}
delete[] pixels;
}
SDL_FreeSurface(infoSurface);
infoSurface = NULL;
}
return true;
}
Cheers! -Neil

O'Neil
- 3,790
- 4
- 16
- 30

Neil Flodin
- 578
- 7
- 22
-
3`pixels = NULL;` should be `delete[] pixels;`, otherwise the array is leaked. – Emil Laine Mar 19 '16 at 17:28
9
If you are using OpenGL with SDL2 you can call glReadPixels
directly instead of using the info surface and renderer. Here is an example (without error checking).
void Screenshot(int x, int y, int w, int h, const char * filename)
{
unsigned char * pixels = new unsigned char[w*h*4]; // 4 bytes for RGBA
glReadPixels(x,y,w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(pixels, w, h, 8*4, w*4, 0,0,0,0);
SDL_SaveBMP(surf, filename);
SDL_FreeSurface(surf);
delete [] pixels;
}
Here's the SDL wiki page with an example of setting up the window and an OpenGL context.
Edit: If you are tempted to copy/paste this snippet, remember to add some error checking

szmoore
- 924
- 10
- 18
-
1Thanks for the code sample! It works pretty good after you [invert the surface vertically](https://stackoverflow.com/a/41494487/536172). – AntonK Jun 20 '19 at 00:36
-
1And also take into account the [endianness](https://stackoverflow.com/a/38038504/536172) :) – AntonK Jun 20 '19 at 01:18
1
void Screenshot(int x, int y, int w, int h, const char * filename)
{
Uint32 rmask, gmask, bmask, amask;
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
unsigned char * pixels = new unsigned char[w*h*4]; // 4 bytes for RGBA
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(pixels, w, h, 8*4, w*4, rmask, gmask, bmask, amask);
SDL_SaveBMP(surf, filename);
SDL_FreeSurface(surf);
delete [] pixels;
}

Severin Turin
- 51
- 2
-
Please read the guidelines about how to ask a question: https://stackoverflow.com/help/how-to-ask. Specifically, describe your problem, your attempt, and the difficulties with it. – Seriously Mar 03 '21 at 15:08