20

Hi I would like to know if it is possible to simply take a screenshot with SDL2. I tried SDL_GetWindowSurface but I get an error saying:

No hardware accelerated renderers available.

I took the code from here.

Another solution I thought about is converting a texture to a surface but I didn't manage to do so...

Do you have any solution?

skypjack
  • 49,335
  • 19
  • 95
  • 187
user3371807
  • 239
  • 1
  • 2
  • 9

2 Answers2

28

It seems like you are mixing the rendering systems. That method will only work in the context of software rendering. For hardware rendering you should use the method SDL_RenderReadPixels(). To save the screenshot you would need a code like that:

SDL_Surface *sshot = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch);
SDL_SaveBMP(sshot, "screenshot.bmp");
SDL_FreeSurface(sshot);

Where w and h are the screen width and height (you can get these values using SDL_GetRendererOutputSize()).

TalesM
  • 1,077
  • 11
  • 12
  • It worked!!! BTW, if you want to save a texture you modified, just use this with: SDL_SetRenderTarget() Thank you – user3371807 Mar 12 '14 at 01:36
  • Great answer! The [linked question](http://stackoverflow.com/questions/20233469/how-do-i-take-and-save-a-bmp-screenshot-in-sdl-2) referred to by the questioner uses `SDL_RenderReadPixels` to read the data. The call to `SDL_GetWindowSurface` seems to be there only to establish the parameters to feed into `SDL_CreateRGBSurface`. You’ve hard-coded these values but can that be relied on? How would you query for these values with the new API? Thanks! – Leo May 31 '14 at 19:38
  • Having played around with this and looked at the documentation, I think I can answer my own question now: the bit depth and channel masks are hard-coded, but as they are set according to the format requested in `SDL_RenderReadPixels`, it doesn’t matter what format the renderer’s pixels were in originally. – Leo Jun 01 '14 at 17:53
  • 1
    `surface = SDL_GetWindowSurface(window)` and `format = SDL_GetWindowPixelFormat(window)` could actually fill those in for you, provided you don't mind using whatever settings your SDL_Window is already using. – RectangleEquals Jun 24 '14 at 06:46
  • @TalesM: Isn't the method your using independent of sw/hw rendering and should work in both cases? Why and when to use the method in the linked answer? Could you elaborate on that, please? Thanks. – mkiever Sep 18 '16 at 09:54
  • You should maybe add information about endianness (in the call to `SDL_CreateRGBSurface`). Also as an aside for some reason under macOS Catalina (and SDL from MacPorts - I don't trust Homebrew because it takes ownership of `/usr/local` - shamefully I'll add) - earlier releases were fine as I recall - the `SDL_RenderReadFromPixels` segfaults on the NULL rectangle (even though documentation suggests it should be fine). Works fine under Linux though... – Pryftan Jun 06 '20 at 17:51
  • Very nice. I also wrote my screensaver so that it takes the game name and adds on the year, month, day, hour, minute and second to the end of the file name so that each time you save it, you get a unique filename with the time it was taken. If you seen the way the game World of Warcraft saves screenshots, it's like that. Their filename format is excellent. Then you can save screencaps to your hearts content. I also recommend using the FreeImage library which allows you to easily save as a PNG or other type. – Neil Roy Dec 25 '20 at 07:00
3

In C SDL2 version 2.0.3, it works with:

fenetre=SDL_GetWindowFromId(touche.windowID); // "touche" is a   SDL_KeyboardEvent, "fenetre" is a SDL_window pointer

r_copie=SDL_GetRenderer(fenetre);

s_SnapSource=SDL_CreateRGBSurface(0,SCREEN_WIDTH,SCREEN_HEIGHT,32,
rmask,
gmask,
bmask,
amask); // s_SnapSource is a SDL_Surface pointer

SDL_LockSurface(s_SnapSource);
SDL_RenderReadPixels(r_copie,NULL,s_SnapSource->format->format,
s_SnapSource->pixels,S_SnapSource->pitch);

SDL_SaveBMP(s_SnapSource,NomFichier); // NomFichier is a char*
SDL_UnlockSurface(s_SnapSource);
SDL_FreeSurface(s_SnapSource);

/!\ ATTENTION /!\

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    Uint32 rmask = 0xff000000;
    Uint32 gmask = 0x00ff0000;
    Uint32 bmask = 0x0000ff00;
    Uint32 amask = 0x000000ff;  
#else
    Uint32 rmask = 0x000000ff;
    Uint32 gmask = 0x0000ff00;
    Uint32 bmask = 0x00ff0000;
    Uint32 amask = 0xff000000;
#endif

...must previously be set somewhere before any use of those variables of course ^^

If you want to put it in a header file make sure you put some "guards" like

#ifndef ENDIANNESS #define ENDIANNESS

...put the stuff here...

#endif

Otherwise, as said in the comments, you could have multiple definitions error when compiling :{ My bad :{

Don't hesitate to check the functions prototypes for return type and parameter(s), the comments here just giving informations, not more.

  • Your answer could be improved by giving more context. You're right about the endianness but even if ou say this or that is a `char *` you don't show what it is. That's not as helpful when trying to explain how something works. Also you should be able to get away with putting t hose in a header file if you use include guards. Oh and you don't include the types of the variables and that itself is rather critical. – Pryftan Jun 06 '20 at 17:54
  • Looking at what each function returns, you could get what the variables types are. It is required to know how function mecanism is about when attacking SDL2 library. I'm french speaking "NomFichier is char*" is not very required as each "man SDL_SaveBMP" will give you the prototype of the function, and "NomFichier" in french gives you "FileName" so this is a path to the file you want to save to... All those functions are documented in SDL2. Yes, I agree you have to put a "guard" before the RGBA definitions... and closing it after ^^ – Hurukan Imperial Stepper Jun 28 '20 at 04:00
  • I never replied because - I'm not even sure why. Been too many things going on and I don't mean Covid (though that's of course crazy). Anyway as for you feeling bad about it - don't. Sometimes we neglect to do something or we miss something or .. and as you say you're French so it might have been harder for you to explain it naturally. It was constructive criticism and I hope that you know that. If you didn't you hopefully will see this. – Pryftan Nov 18 '22 at 15:45