Reputation: 61
I am trying to save a texture to a png and the only thing I'm getting is a screenshot of a portion of the screen.
my code example:
src_texture = SDL_CreateTextureFromSurface( renderer, some_surface );
/*.........*/
/*create target texture */
SDL_Texture *tmp_texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_TARGET , w, h);
SDL_SetTextureBlendMode(tmp_texture, SDL_BLENDMODE_NONE);
SDL_SetRenderTarget(renderer, tmp_texture);
SDL_RenderCopy(renderer, src_texture, NULL, NULL);
/*create surface and get pixels from texture*/
PixelFormat mask = GetMask(format);
s = SDL_CreateRGBSurface(0, w, h, 32, mask.Rmask, mask.Gmask, mask.Bmask, mask.Amask);
if (s) {
SDL_SetRenderTarget(renderer, tmp_texture);
SDL_RenderReadPixels(renderer, NULL, s->format->format, s->pixels, s->pitch);
IMG_SavePNG(s, "image.png");
}
SDL_DestroyTexture(tmp_texture);
Any idea how to achieve this?
Upvotes: 5
Views: 11820
Reputation: 3336
Here is my solution:
void save_texture(const char* file_name, SDL_Renderer* renderer, SDL_Texture* texture) {
SDL_Texture* target = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, texture);
int width, height;
SDL_QueryTexture(texture, NULL, NULL, &width, &height);
SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
SDL_RenderReadPixels(renderer, NULL, surface->format->format, surface->pixels, surface->pitch);
IMG_SavePNG(surface, file_name);
SDL_FreeSurface(surface);
SDL_SetRenderTarget(renderer, target);
}
This function has no side effects outside of the file write. This makes it easy to insert as you don't have to worry that the render target will be clobbered. Feel free to add error handling as you see fit.
Upvotes: 11
Reputation:
I was looking for the same thing and was disappointed when I couldn't find a snippet that performed this "simple" task.
So, I went ahead and made one for the next person that wants it. I wouldn't use this in production code, it's just for debugging. Tested in SDL 2.0.7.
Saves any SDL_Texture
to a .bmp
file.
Technically, it converts any SDL_Texture
into an SDL_Surface
, to which it saves as a .bmp
.
Particularly well suited for:
Pretty straightforward, just pass any SDL_Renderer
along with the SDL_Texture
you want to save, and a filename
to save to.
Also, this will clobber whatever file is there, so be careful.
The code forces everything into an RGBA32 format, though it is simple enough to tweak to whatever format you desire. If you want to, you can load format
by passing it's pointer as a parameter to SDL_QueryTexture
to auto-detect the format of the input texture and use that for the output file.
If you want to enhance this code, here are a few things to consider.
png
, jpg
, or other compressed format./* Usage example */
save_texture(sdlttyDisplay->ren, sdlttyDisplay->font_tex, "image.bmp");
void save_texture(SDL_Renderer *ren, SDL_Texture *tex, const char *filename)
{
SDL_Texture *ren_tex;
SDL_Surface *surf;
int st;
int w;
int h;
int format;
void *pixels;
pixels = NULL;
surf = NULL;
ren_tex = NULL;
format = SDL_PIXELFORMAT_RGBA32;
/* Get information about texture we want to save */
st = SDL_QueryTexture(tex, NULL, NULL, &w, &h);
if (st != 0) {
SDL_Log("Failed querying texture: %s\n", SDL_GetError());
goto cleanup;
}
ren_tex = SDL_CreateTexture(ren, format, SDL_TEXTUREACCESS_TARGET, w, h);
if (!ren_tex) {
SDL_Log("Failed creating render texture: %s\n", SDL_GetError());
goto cleanup;
}
/*
* Initialize our canvas, then copy texture to a target whose pixel data we
* can access
*/
st = SDL_SetRenderTarget(ren, ren_tex);
if (st != 0) {
SDL_Log("Failed setting render target: %s\n", SDL_GetError());
goto cleanup;
}
SDL_SetRenderDrawColor(ren, 0x00, 0x00, 0x00, 0x00);
SDL_RenderClear(ren);
st = SDL_RenderCopy(ren, tex, NULL, NULL);
if (st != 0) {
SDL_Log("Failed copying texture data: %s\n", SDL_GetError());
goto cleanup;
}
/* Create buffer to hold texture data and load it */
pixels = malloc(w * h * SDL_BYTESPERPIXEL(format));
if (!pixels) {
SDL_Log("Failed allocating memory\n");
goto cleanup;
}
st = SDL_RenderReadPixels(ren, NULL, format, pixels, w * SDL_BYTESPERPIXEL(format));
if (st != 0) {
SDL_Log("Failed reading pixel data: %s\n", SDL_GetError());
goto cleanup;
}
/* Copy pixel data over to surface */
surf = SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h, SDL_BITSPERPIXEL(format), w * SDL_BYTESPERPIXEL(format), format);
if (!surf) {
SDL_Log("Failed creating new surface: %s\n", SDL_GetError());
goto cleanup;
}
/* Save result to an image */
st = SDL_SaveBMP(surf, filename);
if (st != 0) {
SDL_Log("Failed saving image: %s\n", SDL_GetError());
goto cleanup;
}
SDL_Log("Saved texture as BMP to \"%s\"\n", filename);
cleanup:
SDL_FreeSurface(surf);
free(pixels);
SDL_DestroyTexture(ren_tex);
}
Upvotes: 6
Reputation: 11
Have you managed to solve this? Using your code my program crashes on the SDL_RenderReadPixels() call.
If you change the texture to be SDL_TEXTUREACCESS_STREAMING you can capture it correctly with the above method, but note that only in the resolution of the actual renderer. If the texture is bigger, it will be cut off.
Upvotes: 0
Reputation: 1316
For saving part of screen into file, better create RGBSurface:
SDL_Surface *ss = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
Here is SDL_CreateRGBSurface documentation
Next step is the same as in your code - use SDL_RenderReadPixels
with correct color format.
When you got this, you can simply save with SDL_SaveBMP
or other functions.
Upvotes: 0