[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: [pygame] .png saving
Patrick Mullen wrote:
> I just checked, I do actually have libpng in my pygame folder. So if
> you could bypass SDL_Image and go straight to libpng for saving png
> files, it would be extremely useful. Png has become the standard
> format for me, because of the good lossless compression and the alpha
> channel support. By the way, in case you didn't know, PNG is also a
> lossless format.
>
>
Here is some lightly modified C code which I wrote for the 'mupen64' emulator,
so it's licensed under the GPL. It's written to save a screenshot from an
OpenGL context, but it could be easily modified to access a pygame surface
instead. Anyone who wishes is welcome to use this to add PNG saving support,
because that would be a nice feature.
Richard
#include <png.h>
static void OGL_png_error(png_structp png_write, const char *message)
{
printf("PNG Error: %s\n", message);
}
static void OGL_png_warn(png_structp png_write, const char *message)
{
printf("PNG Warning: %s\n", message);
}
void OGL_SaveScreenshot()
{
// start by getting the base file path
char filename[2048];
strcpy(filename, "SavedImage.png");
// allocate PNG structures
png_structp png_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, OGL_png_error, OGL_png_warn);
if (!png_write)
{
printf("Error creating PNG write struct.\n");
return;
}
png_infop png_info = png_create_info_struct(png_write);
if (!png_info)
{
png_destroy_write_struct(&png_write, (png_infopp)NULL);
printf("Error creating PNG info struct.\n");
return;
}
// Set the jumpback
if (setjmp(png_jmpbuf(png_write)))
{
png_destroy_write_struct(&png_write, &png_info);
printf("Error calling setjmp()\n");
return;
}
// open the file to write
FILE *savefile = fopen(filename, "wb");
if (savefile == NULL)
{
printf("Error opening '%s' to save screenshot.\n", filename);
return;
}
// give the file handle to the PNG compressor
png_init_io(png_write, savefile);
// read pixel data from OpenGL
char *pixels = (char*)malloc( OGL.width * OGL.height * 3 );
glReadBuffer( GL_FRONT );
glReadPixels( 0, OGL.heightOffset, OGL.width, OGL.height, GL_RGB, GL_UNSIGNED_BYTE, pixels );
glReadBuffer( GL_BACK );
// set the info
int width = OGL.width;
int height = OGL.height;
png_set_IHDR(png_write, png_info, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// lock the screen, get a pointer and row pitch
long pitch = width * 3;
// allocate row pointers and scale each row to 24-bit color
png_byte **row_pointers;
row_pointers = (png_byte **) malloc(height * sizeof(png_bytep));
for (int i = 0; i < height; i++)
{
row_pointers[i] = (png_byte *) (pixels + i * pitch);
}
// set the row pointers
png_set_rows(png_write, png_info, row_pointers);
// write the picture to disk
png_write_png(png_write, png_info, 0, NULL);
// free memory
free(row_pointers);
png_destroy_write_struct(&png_write, &png_info);
free(pixels);
// all done
}