Screengrabbing to JPEG format

Hi Folks

My application needs to grab a screen image and save it as a JPEG.
Any ideas how I can do this? I am using regular SDL 1.3 (albeit quite an old build now).

Thanks
Ed

SDL_RenderReadPixels (http://wiki.libsdl.org/moin.cgi/SDL_RenderReadPixels) will allow you to grab the image data in a format that should allow you to create an SDL_Surface easily. However, there is no IMG_SAVE AFAIK, so you’re stuck with SDL_SaveBMP (http://wiki.libsdl.org/moin.cgi/SDL_SaveBMP) unless you want to implement your own jpeg saving code. Alternatively, here is public domain code for saving in PNG format (http://nothings.org/stb/stb_image_write.h), albeit rather limited, if that will suffice.------------------------
EM3 Nathaniel Fries, U.S. Navy

http://natefries.net/

Hi

Thanks for the advice, but this is a commercial product (I hope) that needs to save in JPEG for reasons of disk space limitations.
Maybe I can find a converter to save from BMP to JPEG.

Cheers

Read the surface contents and then use libjpeg to compress it.On Thu, Apr 14, 2011 at 3:18 AM, ebyard <e_byard at yahoo.co.uk> wrote:

Hi

Thanks for the advice, but this is a commercial product (I hope) that needs
to save in JPEG for reasons of disk space limitations.
Maybe I can find a converter to save from BMP to JPEG.

Cheers


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

The biggest problem I have is that I am using textures, not surfaces. There is no SDL_ConvertTextureToSurface and I have no idea how to write one.

I can’t even grab a frame to a BMP :frowning:

Try SDL_QueryTexturePixels() to get the pixel data.On Thu, Apr 14, 2011 at 4:14 AM, ebyard <e_byard at yahoo.co.uk> wrote:

The biggest problem I have is that I am using textures, not surfaces.
There is no SDL_ConvertTextureToSurface and I have no idea how to write one.

I can’t even grab a frame to a BMP [image: Sad]


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Patrick,

I’ve never used this function before. Is the pixel data in the same format as for a surface? So could I create an SDL_Surface, copy the pixel data from a texture to the surface, and then save that? Does that sound right?!

Thanks for your help
Ed

First, you know the width/height of the texture, in pixels, as well as the
format. If you didn’t save them, use SDL_QueryTexture() to get them. So
let’s assume you didn’t:

SDL_Texture* tex; //Assumed this already exists and is initialized

Uint32 format; //texture format
int access; //not going to be used by us
int width, height; //dimensions of the texture

//Step 1: Get texture information using SDL_QueryTexture()
if(SDL_QueryTexture(tex, &format, &access, &width, &height) != 0)
{
//error: unable to query texture
return;
}

//Step 2: Get texture pixels using SDL_QueryTexturePixels()
void* rawdata = NULL;
int pitch;
if(SDL_QueryTexturePixels(tex,&rawdata, &pitch) != 0)
{
//error: unable to query texture pixels
return;
}

//Use pitch, width, height, and rawdata.

On the subject of pitch…
The ‘pitch’ is the number of bytes in one row of pixels. This doesn’t have
to be the size of a pixel multiplied by the width of the texture, but it
must be at least that. For example, if you had 10 pixels per row (i.e. width
== 10) and 24 bits per pixel (3 bytes per pixel), then a row of pixels is 30
bytes. HOWEVER, the graphics card may round that up to 32 bytes because it
is a power of two and nicer to work with.Thus it has added 2 bytes to the
end of every row. This makes a big different because when you actually use
some library to save your image as a JPEG, you need to let it know what is
pixel data and what it just padding. Each row begins at offset from
’rawdata’ that is a multiple of the pitch. In the first example, row 0 would
begin at offset 0, row 1 would begin at offset 32, row 2 would begin at
offset 64, etc.

for(i=0; i<height; i++)
{
char* rowstart = ((char*)rawdata) + i*pitch;
//Use this data somehow
}

You don’t need to convert to an SDL_Surface* or anything. Once you’ve got
the pixels, use an external library (e.g. libjpeg) to save the pixels. A
quick use of Google dug up this short tutorial on libjepg that seems to be
appropriate to your needs.
http://www.cim.mcgill.ca/~junaed/libjpeg.php

Open the file jpeg_sample.c and pay attention to the function
write_jpeg_file(). That probably has most of the information you need to
finish this. I’m not going to write it all out for you, but hopefully this
should get you well on your way to completing this.On Thu, Apr 14, 2011 at 4:46 AM, ebyard <e_byard at yahoo.co.uk> wrote:

Patrick,

I’ve never used this function before. Is the pixel data in the same format
as for a surface? So could I create an SDL_Surface, copy the pixel data from
a texture to the surface, and then save that? Does that sound right?!

Thanks for your help
Ed


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

First, you know the width/height of the texture, in pixels, as well as
the format. If you didn’t save them, use SDL_QueryTexture() to get
them. So let’s assume you didn’t:

SDL_Texture* tex; //Assumed this already exists and is initialized

Uint32 format; //texture format
int access; //not going to be used by us
int width, height; //dimensions of the texture

//Step 1: Get texture information using SDL_QueryTexture()
if(SDL_QueryTexture(tex, &format, &access, &width, &height) != 0)
{
//error: unable to query texture
return;
}

//Step 2: Get texture pixels using SDL_QueryTexturePixels()
void* rawdata = NULL;
int pitch;
if(SDL_QueryTexturePixels(tex,&rawdata, &pitch) != 0)
{
//error: unable to query texture pixels
return;
}

//Use pitch, width, height, and rawdata.

On the subject of pitch…
The ‘pitch’ is the number of bytes in one row of pixels. This doesn’t
have to be the size of a pixel multiplied by the width of the texture,
but it must be at least that. For example, if you had 10 pixels per
row (i.e. width == 10) and 24 bits per pixel (3 bytes per pixel), then
a row of pixels is 30 bytes. HOWEVER, the graphics card may round that
up to 32 bytes because it is a power of two and nicer to work
with.Thus it has added 2 bytes to the end of every row. This makes a
big different because when you actually use some library to save your
image as a JPEG, you need to let it know what is pixel data and what
it just padding. Each row begins at offset from ‘rawdata’ that is a
multiple of the pitch. In the first example, row 0 would begin at
offset 0, row 1 would begin at offset 32, row 2 would begin at offset
64, etc.

for(i=0; i<height; i++)
{
char* rowstart = ((char*)rawdata) + i*pitch;
//Use this data somehow
}

You don’t need to convert to an SDL_Surface* or anything. Once you’ve
got the pixels, use an external library (e.g. libjpeg) to save the
pixels. A quick use of Google dug up this short tutorial on libjepg
that seems to be appropriate to your needs.
http://www.cim.mcgill.ca/~junaed/libjpeg.php
http://www.cim.mcgill.ca/~junaed/libjpeg.php
Some people prefer http://code.google.com/p/jpeg-compressor/ which they
say is simpler to use. I have no experience with it though. Oh, it’s
public domain!

Cheers,

AndreOn 14/04/2011 07:34, Patrick Baggett wrote:

Open the file jpeg_sample.c and pay attention to the function
write_jpeg_file(). That probably has most of the information you need
to finish this. I’m not going to write it all out for you, but
hopefully this should get you well on your way to completing this.

On Thu, Apr 14, 2011 at 4:46 AM, ebyard <e_byard at yahoo.co.uk <mailto:e_byard at yahoo.co.uk>> wrote:

Patrick,

I've never used this function before. Is the pixel data in the
same format as for a surface? So could I create an SDL_Surface,
copy the pixel data from a texture to the surface, and then save
that? Does that sound right?!

Thanks for your help
Ed

_______________________________________________
SDL mailing list
SDL at lists.libsdl.org <mailto:SDL at lists.libsdl.org>
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

wait, are you trying to get pixel data from the window itself (IE, like a screenshot tool) or from a texture?------------------------
EM3 Nathaniel Fries, U.S. Navy

http://natefries.net/

I’m trying to just grab the contents of both SDL windows in my app - I think the QueryTexture stuff should do it. Thanks very much for your help!

FYI libjpeg interface is really easy, this is code I am using my SDL project to compress some texture based frames into JEPG:

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);
	cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
	cinfo.image_width       = img_width;
	cinfo.image_height      = img_height;
	cinfo.input_components  = 3;
	cinfo.in_color_space    = JCS_RGB;
	jpeg_set_defaults(&cinfo);
	/* Specify data destination for compression */
	jpeg_stdio_dest(&cinfo, fp);
	/* Start compressor */
	jpeg_start_compress(&cinfo, TRUE);
	/* Process data */
	ptr = (uint8_t *)img_buffer;
	while(cinfo.next_scanline < cinfo.image_height) {
		jpeg_write_scanlines(&cinfo, &ptr, 1);
		ptr += img_pitch;
	}
	/* finish compression and release memory */
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

You get img_width/height/pitch/buffer with SDL_QueryTexture and SDL_QueryTexturePixels or SDL_LockTexture.
The fp file pointer from fopen.

There’s also good jpeg in memory write handler under MIT at:
http://www.chandrabrown.org/surendar/qat/source/transcode.c

Cheers,–
Adam Strzelecki

ebyard wrote:

I’m trying to just grab the contents of both SDL windows in my app - so I don’t think the QueryTexture stuff will work?

SDL_RenderReadPixels (http://wiki.libsdl.org/moin.cgi/SDL_RenderReadPixels) will do the trick. It’s essentially the same as the query texture function, but for your window rather than a texture.------------------------
EM3 Nathaniel Fries, U.S. Navy

http://natefries.net/