SDL2 C++ Taking a screenshot
Asked Answered
C

3

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?

Crissie answered 11/3, 2014 at 3:38 Comment(0)
R
29

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()).

Rascality answered 11/3, 2014 at 23:36 Comment(7)
It worked!!! BTW, if you want to save a texture you modified, just use this with: SDL_SetRenderTarget() Thank youCrissie
Great answer! The linked question 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!Viquelia
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.Viquelia
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.Rove
@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.Scarlett
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...Fidelis
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.Jota
N
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.

Nadiya answered 26/3, 2018 at 12:27 Comment(3)
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.Fidelis
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 ^^Nadiya
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.Fidelis
C
1

instead of ARGB8888 format (32 bits per pixel) you can use more compact format SDL_PIXELFORMAT_RGB24:

SDL_Surface* sshot = SDL_CreateRGBSurfaceWithFormat(0, cdest.w, cdest.h, 24, SDL_PIXELFORMAT_RGB24);

SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGB24, sshot->pixels, sshot->pitch);

SDL_SaveBMP(sshot, filename);

SDL_FreeSurface(sshot);
Castillo answered 16/12, 2023 at 9:34 Comment(1)
This probably should be a comment on the other answer.Imperial

© 2022 - 2024 — McMap. All rights reserved.