[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
}