Source code

And the complete source code for the program:

/* disassmble2.c */

#include <stdio.h>
#include <stdlib.h>

/* note that this following line will probably not work for you,
* change it to:
* #include <png.h> */
#include PNGHEADER

#define READ_BLOCK_LEN 1024

void info_callback(png_structp png_ptr, png_infop info_ptr);
void row_callback(png_structp png_ptr, png_bytep new_row,
                                  png_uint_32 row_num, int pass);
void end_callback(png_structp png_ptr, png_infop info_ptr);
void frame_info_callback(png_structp png_ptr, png_uint_32 frameNum);
void frame_end_callback(png_structp png_ptr, png_uint_32 frameNum);
void writeFrame(png_uint_32 frameNum);
void writeSetup2(png_structp png_ptr_read, png_infop info_ptr_read,
                 png_structp png_ptr_write, png_infop info_ptr_write);

png_structp png_ptr_read;
png_infop info_ptr_read;
png_bytepp rowPointers;
png_uint_32 bytesPerRow;
png_uint_32 numFrames;

void fatalError(char* str)
{
    fprintf(stderr, "Fatal Error: %s\n", str);
    exit(1);
}

int main(int argc, char** argv)
{
    FILE* image;
    int rc;
    int numBytes;
    unsigned char data[READ_BLOCK_LEN];
    
    if(argc != 2)
        fatalError("usage: disassemble2 image.png\n");
    
    image = fopen(argv[1], "rb");
    if(image == NULL)
        fatalError("couldn't open original png");
    
    rc = fread(data, 1, 8, image);
    if(rc != 8)
        fatalError("unable to read signature");
    
    rc = png_check_sig(data, 8);
    if(rc == 0)
        fatalError("invalid png signature");
    
    png_ptr_read = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if(png_ptr_read == NULL)
        fatalError("unable to create read struct");
    
    info_ptr_read = png_create_info_struct(png_ptr_read);
    if(info_ptr_read == NULL)
        fatalError("unable to create info struct");
    
    png_set_progressive_read_fn(png_ptr_read, NULL, info_callback, row_callback,
                                end_callback);
    
    numBytes = 8; /* the signature already read */
    while(numBytes != 0)
    {
        png_process_data(png_ptr_read, info_ptr_read, data, numBytes);
        numBytes = fread(data, 1, READ_BLOCK_LEN, image);
    }
    
    png_destroy_read_struct(&png_ptr_read, &info_ptr_read, NULL);
    
    return 0;
}

void info_callback(png_structp png_ptr, png_infop info_ptr)
{
    int count;
    png_uint_32 unused;
    
    if(!png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
        fatalError("source image must be animated");
    
    png_get_acTL(png_ptr, info_ptr, &numFrames, &unused);
    
    png_set_progressive_frame_fn(png_ptr, frame_info_callback, frame_end_callback);
    
    if (png_get_interlace_type(png_ptr, info_ptr) == PNG_INTERLACE_ADAM7)
        png_set_interlace_handling(png_ptr);
    
    png_read_update_info(png_ptr_read, info_ptr_read);
    
    rowPointers = png_malloc(png_ptr, sizeof(png_bytep) * png_ptr->height);
    bytesPerRow = png_get_rowbytes(png_ptr, info_ptr);
    for(count = 0; count < info_ptr_read->height; count++)
        rowPointers[count] = png_malloc(png_ptr_read, bytesPerRow);
}

void row_callback(png_structp png_ptr, png_bytep new_row,
                  png_uint_32 row_num, int pass)
{
    if(new_row == NULL)
        return;
    
    png_progressive_combine_row(png_ptr, rowPointers[row_num], new_row);
}

void end_callback(png_structp png_ptr_read, png_infop info_ptr_read)
{
    ;
}

void frame_info_callback(png_structp png_ptr, png_uint_32 frameNum)
{
    ;
}

void frame_end_callback(png_structp png_ptr, png_uint_32 frameNum)
{
    writeFrame(frameNum);
}

void writeFrame(png_uint_32 frameNum)
{
    png_structp png_ptr_write;
    png_infop info_ptr_write;
    char filename[512];
    FILE* newImage;
    
    sprintf(filename, "extracted-%02u.png", (unsigned)frameNum);
    newImage = fopen(filename, "wb");
    if(newImage == NULL)
        fatalError("couldn't create png for writing");
    
    png_ptr_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if(png_ptr_write == NULL)
        fatalError("unable to create write struct");
    
    info_ptr_write = png_create_info_struct(png_ptr_write);
    if(info_ptr_write == NULL)
        fatalError("unable to create write struct");
    
    png_init_io(png_ptr_write, newImage);
    
    writeSetup2(png_ptr_read, info_ptr_read, png_ptr_write, info_ptr_write);
    
    png_write_info(png_ptr_write, info_ptr_write);
    
    png_write_image(png_ptr_write, rowPointers);
    
    png_write_end(png_ptr_write, NULL);
    
    png_destroy_write_struct(&png_ptr_write, &info_ptr_write);
    
    fclose(newImage);
    
    printf("extracted frame %u into %s\n", (unsigned)frameNum, filename);
}

void writeSetup2(png_structp png_ptr_read, png_infop info_ptr_read,
                 png_structp png_ptr_write, png_infop info_ptr_write)
{
    /* fcTl */
    png_uint_32 next_frame_width;
    png_uint_32 next_frame_height;
    png_uint_32 next_frame_x_offset;
    png_uint_32 next_frame_y_offset;
    png_uint_16 next_frame_delay_num;
    png_uint_16 next_frame_delay_den;
    png_byte next_frame_dispose_op;
    png_byte next_frame_blend_op;
    
    /* IHDR */
    png_uint_32 width;
    png_uint_32 height;
    int bit_depth;
    int colour_type;
    int interlace_method;
    int compression_method;
    int filter_method;
    
    /* PLTE */
    png_colorp palette = NULL;
    int palette_size = 0;
    
    /* gAMA */
    double gamma;
    
    /* tRNS */
    png_bytep trans;
    int num_trans;
    png_color_16p trans_values;
    
    /* bKGD */
    png_color_16p background;
    
    if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_fcTL))
    {
        png_get_next_frame_fcTL(png_ptr_read, info_ptr_read,
                                &next_frame_width, &next_frame_height,
                                &next_frame_x_offset, &next_frame_y_offset,
                                &next_frame_delay_num, &next_frame_delay_den,
                                &next_frame_dispose_op, &next_frame_blend_op);
    }
    else
    {
        /* the first frame doesn't have an fcTL so it's expected to be hidden, 
        * but we'll extract it anyway */
        next_frame_width = png_get_image_width(png_ptr_read, info_ptr_read);
        next_frame_height = png_get_image_height(png_ptr_read, info_ptr_read);
    }
    
    png_get_IHDR(png_ptr_read, info_ptr_read, &width, &height,
                 &bit_depth, &colour_type, &interlace_method,
                 &compression_method, &filter_method);
    png_set_IHDR(png_ptr_write, info_ptr_write, 
                 next_frame_width, next_frame_height,
                 bit_depth, colour_type, interlace_method,
                 compression_method, filter_method);
    
    if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_PLTE))
    {
        png_get_PLTE(png_ptr_read, info_ptr_read, &palette, &palette_size);
        png_set_PLTE(png_ptr_write, info_ptr_write, palette, palette_size);
    }
    
    if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_gAMA))
    {
        png_get_gAMA(png_ptr_read, info_ptr_read, &gamma);
        png_set_gAMA(png_ptr_write, info_ptr_write, gamma);
    }
    
    if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_tRNS))
    {
        png_get_tRNS(png_ptr_read, info_ptr_read, &trans, &num_trans, &trans_values);
        png_set_tRNS(png_ptr_write, info_ptr_write, trans, num_trans, trans_values);
    }
    
    if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_bKGD))
    {
        png_get_bKGD(png_ptr_read, info_ptr_read, &background);
        png_set_bKGD(png_ptr_write, info_ptr_write, background);
    }
}