X-Git-Url: https://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/fba9c8ad290c71471cdb8b52d1c5f0e4eb1fb83e..e1720d76904b883a5a4e820c631afe816ffd2701:/src/lbximg.c?ds=sidebyside diff --git a/src/lbximg.c b/src/lbximg.c index 0770049..d528290 100644 --- a/src/lbximg.c +++ b/src/lbximg.c @@ -2,11 +2,21 @@ #include #include #include +#include +#include #include +#include + +#include #include "image.h" #include "lbx.h" +/* Global flags */ +static int verbose = 0; +static char *outname = "out"; +static int usepalette = 1; + static const char *progname; #define errmsg(fmt, ...) (\ fprintf(stderr, "%s: " fmt, progname, __VA_ARGS__)\ @@ -18,21 +28,247 @@ enum { MODE_IDENT, }; +int parserange(struct lbx_imginfo *info, char *str, unsigned char *bits) +{ + unsigned long start, end; + unsigned int i; + char *endptr; + + start = strtoul(str, &endptr, 0); + if (start >= info->nframes) { + errmsg("frame %lu out of range.\n", start); + return -1; + } + + if (endptr == str) { + errmsg("invalid frame range: %s.\n", str); + return -1; + } + + switch (*endptr) { + case '\0': + end = start; + break; + case '-': + end = strtoul(endptr+1, &endptr, 0); + if (end >= info->nframes) { + errmsg("frame %lu out of range.\n", end); + return -1; + } + + if (endptr == str) + end = info->nframes - 1; + break; + default: + errmsg("invalid frame range: %s.\n", str); + return -1; + } + + if (end < start) { + errmsg("invalid frame range: %s.\n", str); + return -1; + } + + for (i = start; i <= end; i++) { + bits[i / CHAR_BIT] |= 1 << (i % CHAR_BIT); + } + + return 0; +} + +int outpng(unsigned int frameno, unsigned char **framedata, + unsigned int width, unsigned int height, + struct lbx_colour palette[static 256]) +{ + char name[strlen(outname) + sizeof ".65535.png"]; + FILE *of; + + png_structp png; + png_infop info; + + assert(frameno < 65536); + snprintf(name, sizeof name, "%s.%03d.png", outname, frameno); + + of = fopen(name, "wb"); + if (!of) { + errmsg("failed to open %s: %s.\n", name, strerror(errno)); + return -1; + } + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + errmsg("failed to init libpng.\n", 0); + goto err; + } + + info = png_create_info_struct(png); + if (!info) { + errmsg("failed to init libpng.\n", 0); + png_destroy_write_struct(&png, NULL); + goto err; + } + + if (setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, &info); + goto err; + } + + + png_set_IHDR(png, info, width, height, 8, + usepalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + if (usepalette) + png_set_PLTE(png, info, (png_colorp)palette, 256); + png_set_rows(png, info, framedata); + + png_init_io(png, of); + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + + png_destroy_write_struct(&png, &info); + fclose(of); + + if (verbose) + printf("wrote %s\n", name); + + return 0; +err: + fclose(of); + remove(name); + return -1; +} + +static int loadpalette(LBX_IMG *img, struct lbx_imginfo *info, + FILE *palf, struct lbx_colour palette[static 256]) +{ + int i; + + for (i = 0; i < 256; i++) + palette[i] = (struct lbx_colour){0xff, 0x00, 0xff}; + + if (palf) { + if (lbximg_loadpalette(palf, palette) == -1) { + errmsg("error reading palette: %s\n", lbx_strerror()); + return -1; + } + } else if (!info->haspalette) { + errmsg("no palette available.\n", 0); + return -1; + } + + if (lbximg_getpalette(img, palette) == -1) { + errmsg("error reading embedded palette: %s\n", lbx_strerror()); + return -1; + } + + return 0; +} + +int decode(LBX_IMG *img, FILE *palf, FILE *override, char **argv) +{ + unsigned char *framebits; + struct lbx_colour palette[256]; + struct lbx_imginfo info; + int extracted = 0; + unsigned int i; + + lbximg_getinfo(img, &info); + + framebits = malloc(info.nframes / CHAR_BIT + 1); + if (!framebits) { + return EXIT_FAILURE; + } + + /* Figure out what images we're extracting. */ + if (!argv[0]) { + /* extract all images by default. */ + memset(framebits, -1, info.nframes / CHAR_BIT + 1); + } else { + for (i = 0; argv[i]; i++) { + parserange(&info, argv[i], framebits); + } + } + + if (usepalette && loadpalette(img, &info, palf, palette) == -1) { + goto err; + } + + if (override) { + LBX_IMG *overimg = lbximg_fopen(override); + struct lbx_imginfo info; + + if (!overimg) { + errmsg("failed to open override image: %s\n", + lbx_strerror()); + goto err; + } + lbximg_getinfo(overimg, &info); + + if (!info.haspalette) { + errmsg("override image has no palette.\n", 0); + lbximg_close(overimg); + goto err; + } + + if (lbximg_getpalette(overimg, palette) == -1) { + errmsg("error reading override palette: %s\n", + lbx_strerror()); + lbximg_close(overimg); + goto err; + } + + lbximg_close(overimg); + } + + /* Extract the images, in order. */ + for (i = 0; i < info.nframes; i++) { + unsigned char **framedata; + + if (!(framebits[i / CHAR_BIT] & (1 << (i % CHAR_BIT)))) + continue; + + framedata = lbximg_getframe(img, i); + if (!framedata) { + errmsg("error in frame %u: %s\n", i, lbx_strerror()); + continue; + } + + if (!outpng(i, framedata, info.width, info.height, palette)) { + extracted = 1; + } + } + + if (!extracted) { + errmsg("no frames extracted.\n", 0); + goto err; + } + + free(framebits); + return EXIT_SUCCESS; +err: + free(framebits); + return EXIT_FAILURE; +} + int main(int argc, char **argv) { - int mode = MODE_NONE, verbose = 0; - FILE *inf = stdin, *palf = NULL; + int mode = MODE_NONE; + FILE *inf = stdin, *palf = NULL, *overf = NULL; const char *name = "stdin"; LBX_IMG *img; int opt; - static const char *sopts = "idvf:p:"; + static const char *sopts = "idvf:p:O:"; static const struct option lopts[] = { - { "info", 0, NULL, 'i' }, - { "decode", 0, NULL, 'd' }, - { "verbose", 0, NULL, 'v' }, - { "file", 1, NULL, 'f' }, - { "palette", 1, NULL, 'p' }, + { "info", 0, NULL, 'i' }, + { "decode", 0, NULL, 'd' }, + { "verbose", 0, NULL, 'v' }, + { "file", 1, NULL, 'f' }, + { "palette", 1, NULL, 'p' }, + { "override", 1, NULL, 'p' }, + + { "nopalette", 0, &usepalette, 0 }, { 0 } }; @@ -70,7 +306,15 @@ int main(int argc, char **argv) } break; - default: + case 'O': + overf = fopen(optarg, "rb"); + if (!overf) { + errmsg("failed to open %s: %m\n", optarg); + return EXIT_FAILURE; + } + break; + case '?': + case ':': return EXIT_FAILURE; } } @@ -90,13 +334,18 @@ int main(int argc, char **argv) struct lbx_imginfo info; lbximg_getinfo(img, &info); - printf("%s is %ux%u LBX image, %u frames\n", - name, info.width, info.height, info.nframes); + printf("%s is %ux%u LBX image, %u frame(s)%s%s\n", + name, info.width, info.height, info.nframes, + info.haspalette ? ", embedded palette" : "", + info.looping ? ", loops" : ""); } switch (mode) { case MODE_DECODE: - errmsg("decode function not yet implemented.\n", 0); + if (decode(img, palf, overf, &argv[optind])) { + lbximg_close(img); + return EXIT_FAILURE; + } break; }