/* * Copyright 2008 Sean Fox * Copyright 2008 James Bursa * * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/ * Licenced under the MIT License, * http://www.opensource.org/licenses/mit-license.php */ #include #include #include #include #include #include #include #include "../include/libnsgif.h" #define BYTES_PER_PIXEL 4 #define MAX_IMAGE_BYTES (48 * 1024 * 1024) static void *bitmap_create(int width, int height) { /* ensure a stupidly large bitmap is not created */ if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) { return NULL; } return calloc(width * height, BYTES_PER_PIXEL); } static void bitmap_set_opaque(void *bitmap, bool opaque) { (void) opaque; /* unused */ (void) bitmap; /* unused */ assert(bitmap); } static bool bitmap_test_opaque(void *bitmap) { (void) bitmap; /* unused */ assert(bitmap); return false; } static unsigned char *bitmap_get_buffer(void *bitmap) { assert(bitmap); return bitmap; } static void bitmap_destroy(void *bitmap) { assert(bitmap); free(bitmap); } static void bitmap_modified(void *bitmap) { (void) bitmap; /* unused */ assert(bitmap); return; } static unsigned char *load_file(const char *path, size_t *data_size) { FILE *fd; struct stat sb; unsigned char *buffer; size_t size; size_t n; fd = fopen(path, "rb"); if (!fd) { perror(path); exit(EXIT_FAILURE); } if (stat(path, &sb)) { perror(path); exit(EXIT_FAILURE); } size = sb.st_size; buffer = malloc(size); if (!buffer) { fprintf(stderr, "Unable to allocate %lld bytes\n", (long long) size); exit(EXIT_FAILURE); } n = fread(buffer, 1, size, fd); if (n != size) { perror(path); exit(EXIT_FAILURE); } fclose(fd); *data_size = size; return buffer; } static void warning(const char *context, gif_result code) { fprintf(stderr, "%s failed: ", context); switch (code) { case GIF_INSUFFICIENT_FRAME_DATA: fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA"); break; case GIF_FRAME_DATA_ERROR: fprintf(stderr, "GIF_FRAME_DATA_ERROR"); break; case GIF_INSUFFICIENT_DATA: fprintf(stderr, "GIF_INSUFFICIENT_DATA"); break; case GIF_DATA_ERROR: fprintf(stderr, "GIF_DATA_ERROR"); break; case GIF_INSUFFICIENT_MEMORY: fprintf(stderr, "GIF_INSUFFICIENT_MEMORY"); break; default: fprintf(stderr, "unknown code %i", code); break; } fprintf(stderr, "\n"); } static void write_ppm(FILE* fh, const char *name, gif_animation *gif, bool no_write) { unsigned int i; gif_result code; if (!no_write) { fprintf(fh, "P3\n"); fprintf(fh, "# %s\n", name); fprintf(fh, "# width %u \n", gif->width); fprintf(fh, "# height %u \n", gif->height); fprintf(fh, "# frame_count %u \n", gif->frame_count); fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial); fprintf(fh, "# loop_count %u \n", gif->loop_count); fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count); } /* decode the frames */ for (i = 0; i != gif->frame_count; i++) { unsigned int row, col; unsigned char *image; code = gif_decode_frame(gif, i); if (code != GIF_OK) warning("gif_decode_frame", code); if (!gif->frames[i].display) { continue; } if (!no_write) { fprintf(fh, "# frame %u:\n", i); image = (unsigned char *) gif->frame_image; for (row = 0; row != gif->height; row++) { for (col = 0; col != gif->width; col++) { size_t z = (row * gif->width + col) * 4; fprintf(fh, "%u %u %u ", (unsigned char) image[z], (unsigned char) image[z + 1], (unsigned char) image[z + 2]); } fprintf(fh, "\n"); } } } } int main(int argc, char *argv[]) { gif_bitmap_callback_vt bitmap_callbacks = { bitmap_create, bitmap_destroy, bitmap_get_buffer, bitmap_set_opaque, bitmap_test_opaque, bitmap_modified }; gif_animation gif; size_t size; gif_result code; unsigned char *data; FILE *outf = stdout; bool no_write = false; if (argc < 2) { fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]); fprintf(stderr, "\n"); fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded " "but not output.\n"); fprintf(stderr, "Otherwise [out] is an output filename.\n"); fprintf(stderr, "When [out] is unset, output is to stdout.\n"); return 1; } if (argc > 2) { if (strcmp(argv[2], "NOWRITE") == 0) { no_write = true; } else { outf = fopen(argv[2], "w+"); if (outf == NULL) { fprintf(stderr, "Unable to open %s for writing\n", argv[2]); return 2; } } } /* create our gif animation */ gif_create(&gif, &bitmap_callbacks); /* load file into memory */ data = load_file(argv[1], &size); /* begin decoding */ do { code = gif_initialise(&gif, size, data); if (code != GIF_OK && code != GIF_WORKING) { warning("gif_initialise", code); gif_finalise(&gif); free(data); return 1; } } while (code != GIF_OK); write_ppm(outf, argv[1], &gif, no_write); if (argc > 2 && !no_write) { fclose(outf); } /* clean up */ gif_finalise(&gif); free(data); return 0; }