]> git.draconx.ca Git - liblbx.git/commitdiff
Implement decoding for lbximg.
authorNick Bowler <draconx@gmail.com>
Mon, 31 Dec 2007 07:25:15 +0000 (02:25 -0500)
committerNick Bowler <draconx@gmail.com>
Mon, 31 Dec 2007 07:25:15 +0000 (02:25 -0500)
src/lbximg.c

index 0770049c48ff071c8dac3b21aedf442968b6594a..409936a503f5a167c5f267b29aa350050b5ff5c0 100644 (file)
@@ -2,11 +2,20 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
+#include <assert.h>
 #include <getopt.h>
+#include <errno.h>
+
+#include <png.h>
 
 #include "image.h"
 #include "lbx.h"
 
+/* Global flags */
+static int verbose = 0;
+static char *outname = "out";
+
 static const char *progname;
 #define errmsg(fmt, ...) (\
        fprintf(stderr, "%s: " fmt, progname, __VA_ARGS__)\
@@ -18,9 +27,190 @@ 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, PNG_COLOR_TYPE_PALETTE,
+                    PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+                    PNG_FILTER_TYPE_DEFAULT);
+       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;
+}
+
+int decode(LBX_IMG *img, FILE *palf, 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 -1;
+       }
+
+       /* 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);
+               }
+       }
+
+       /* Read image palette */
+       if (palf) {
+               if (lbximg_loadpalette(palf, palette) == -1) {
+                       errmsg("error reading palette: %s\n", lbx_strerror());
+                       goto err;
+               }
+       } else {
+               fprintf(stderr, "warning: no palette specified; "
+                               "decoded images will likely be very pink.\n");
+
+               for (i = 0; i < 256; i++)
+                       palette[i] = (struct lbx_colour){0xff, 0x00, 0xff};
+       }
+
+       if (lbximg_getpalette(img, palette) == -1) {
+               errmsg("error reading palette override: %s\n", lbx_strerror());
+               goto err;
+       }
+
+       /* 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;
+       int mode = MODE_NONE;
        FILE *inf = stdin, *palf = NULL;
        const char *name = "stdin";
        LBX_IMG *img;
@@ -96,7 +286,10 @@ int main(int argc, char **argv)
 
        switch (mode) {
        case MODE_DECODE:
-               errmsg("decode function not yet implemented.\n", 0);
+               if (decode(img, palf, &argv[optind])) {
+                       lbximg_close(img);
+                       return EXIT_FAILURE;
+               }
                break;
        }