From: Nick Bowler Date: Thu, 23 Jan 2014 02:30:01 +0000 (-0500) Subject: lbximg: Convert to new decoding API. X-Git-Url: http://git.draconx.ca/gitweb/liblbx.git/commitdiff_plain/594902a517f5a77535a2d7bb903f8e1de358edf6 lbximg: Convert to new decoding API. Convert lbximg to the new API. The implementation looks a lot like getframe, but we can make the memory usage much more compact. Not really a coincidence because getframe was originally designed to support this application. --- diff --git a/src/imgoutput.h b/src/imgoutput.h index bb0b2a5..62994fd 100644 --- a/src/imgoutput.h +++ b/src/imgoutput.h @@ -5,12 +5,12 @@ typedef int img_output_func(FILE *f, const char *filename, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask, + unsigned char *pixels, unsigned char *pixel_mask, struct lbx_colour *palette); img_output_func img_output_pbm, img_output_ppm, img_output_pam; img_output_func img_output_png; -_Bool img_is_masked(unsigned char **mask, unsigned width, unsigned height); +_Bool img_is_masked(unsigned char *mask, unsigned width, unsigned height); #endif diff --git a/src/lbximg.c b/src/lbximg.c index 0fd3693..2608689 100644 --- a/src/lbximg.c +++ b/src/lbximg.c @@ -1,7 +1,7 @@ /* * 2ooM: The Master of Orion II Reverse Engineering Project * Simple command-line tool to convert an LBX image to other formats. - * Copyright © 2006-2011, 2013 Nick Bowler + * Copyright © 2006-2011, 2013-2014 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include "imgoutput.h" +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + /* Global flags */ static int verbose = 0; static char *outname = "out"; @@ -97,13 +100,21 @@ static int lookup_format(const char *fmt) return -1; } -bool img_is_masked(unsigned char **mask, unsigned width, unsigned height) +bool img_is_masked(unsigned char *mask, unsigned width, unsigned height) { - unsigned x, y; - - for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { - if (mask[y][x] == 0) - return true; + unsigned long npixels = (unsigned long) width * height; + unsigned long mask_sz = npixels / CHAR_BIT + (npixels % CHAR_BIT != 0); + + for (unsigned long i = 0; i < mask_sz; i++) { + if (i+1 < mask_sz) { + if (mask[i] != (unsigned char)-1) + return true; + } else { + unsigned char test = (1u << npixels % CHAR_BIT) - 1; + + if ((mask[i] & test) != test) + return true; + } } return false; @@ -158,7 +169,7 @@ int parserange(unsigned frames, char *str, unsigned char *bits) } int output(unsigned int frameno, const struct img_format *fmt, - unsigned char **framedata, unsigned char **mask, + unsigned char *pixels, unsigned char *pixel_mask, unsigned int width, unsigned int height, struct lbx_colour palette[static 256]) { @@ -176,7 +187,7 @@ int output(unsigned int frameno, const struct img_format *fmt, return -1; } - rc = fmt->output(of, name, width, height, framedata, mask, palette); + rc = fmt->output(of, name, width, height, pixels, pixel_mask, palette); if (rc < 0) { fclose(of); return -1; @@ -186,7 +197,7 @@ int output(unsigned int frameno, const struct img_format *fmt, tool_err(0, "error writing %s", name); return -1; } - + if (verbose) printf("wrote %s\n", name); @@ -257,23 +268,117 @@ static int loadpalette(struct lbx_image *img, struct lbx_imginfo *info, return 0; } +/* Return true iff a divides b. */ +static bool divides(unsigned a, unsigned b) +{ + if (b == 0) + return true; + if (a == 0) + return false; + + return b % a == 0; +} + +/* Set n bits starting from offset in the bitmap. */ +static void +set_bits(unsigned char *bitmap, unsigned long offset, unsigned long n) +{ + if (offset % CHAR_BIT) { + bitmap[offset/CHAR_BIT] |= (n >= CHAR_BIT ? -1u : (1u << n) - 1) + << offset%CHAR_BIT; + + n -= MIN(n, CHAR_BIT - offset%CHAR_BIT); + offset += CHAR_BIT - offset%CHAR_BIT; + } + + if (n > CHAR_BIT) { + memset(&bitmap[offset/CHAR_BIT], -1, n/CHAR_BIT); + + offset += n - n%CHAR_BIT; + n %= CHAR_BIT; + } + + if (n) { + bitmap[offset/CHAR_BIT] |= (1u << n) - 1; + } +} + +static int decode_frame(struct lbx_image *img, unsigned n, + unsigned char *pixels, unsigned char *pixel_mask) +{ + unsigned x, y; + long rc; + + rc = lbx_img_seek(img, n); + if (rc < 0) { + tool_err(-1, "frame %u: invalid frame: %s\n", n, lbx_errmsg()); + return -1; + } + + while ((rc = lbx_img_read_row_header(img, &x, &y)) != 0) { + unsigned long offset; + + if (rc < 0) { + tool_err(-1, "frame %u: invalid row: %s", n, lbx_errmsg()); + return -1; + } + + offset = (unsigned long) y * img->width + x; + rc = lbx_img_read_row_data(img, pixels+offset); + if (rc < 0) { + tool_err(-1, "frame %u: error reading row: %s\n", n, lbx_errmsg()); + return -1; + } + + set_bits(pixel_mask, offset, rc); + } + + return 0; +} + static int decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) { - unsigned char *framebits; + unsigned char *pixels = NULL, *pixel_mask = NULL, *framebits = NULL; struct lbx_colour palette[256]; struct lbx_imginfo info; - int ret = EXIT_SUCCESS; + int rc, ret = EXIT_FAILURE; int extracted = 0; unsigned int i; + size_t npixels, mask_sz; assert(fmt >= 0 && fmt < sizeof formats / sizeof formats[0]); lbx_img_getinfo(img, &info); + npixels = img->width; + if (img->height && npixels >= SIZE_MAX / img->height) { + tool_err(-1, "image too large"); + goto err; + } + npixels *= img->height; + + /* Ensure there is at least 1 byte to allocate */ + if (npixels == 0) + npixels = 1; + framebits = calloc(1, img->frames / CHAR_BIT + 1); if (!framebits) { - return EXIT_FAILURE; + tool_err(0, "failed to allocate memory"); + goto err; + } + + pixels = calloc(img->width, img->height); + if (!pixels) { + tool_err(0, "failed to allocate memory"); + goto err; + } + + mask_sz = npixels / CHAR_BIT + (npixels % CHAR_BIT != 0); + pixel_mask = malloc(mask_sz); + if (!pixel_mask) { + tool_err(0, "failed to allocate memory"); + goto err; } /* Figure out what images we're extracting. */ @@ -294,26 +399,25 @@ decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) } /* Extract the images, in order. */ + ret = EXIT_SUCCESS; for (i = 0; i < img->frames; i++) { - unsigned char **data; - unsigned char **mask; - - if (!(framebits[i / CHAR_BIT] & (1 << (i % CHAR_BIT)))) - continue; + if (divides(img->chunk, i)) + memset(pixel_mask, 0, mask_sz); - data = lbx_img_getframe(img, i); - if (!data) { - tool_err(-1, "error in frame %u: %s", i, lbx_errmsg()); + rc = decode_frame(img, i, pixels, pixel_mask); + if (rc < 0) { ret = EXIT_FAILURE; - continue; + goto err; } - mask = lbx_img_getmask(img); + if (framebits[i / CHAR_BIT] & (1u << (i % CHAR_BIT))) { + rc = output(i, &formats[fmt], pixels, pixel_mask, + img->width, img->height, + usepalette ? palette : NULL); - if (!output(i, &formats[fmt], data, mask, - img->width, img->height, - usepalette ? palette : NULL)) { - extracted = 1; + if (rc == 0) { + extracted = 1; + } } } @@ -322,6 +426,8 @@ decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) ret = EXIT_FAILURE; } err: + free(pixels); + free(pixel_mask); free(framebits); return ret; } diff --git a/src/png.c b/src/png.c index c52db03..cc885f7 100644 --- a/src/png.c +++ b/src/png.c @@ -1,7 +1,7 @@ /* * 2ooM: The Master of Orion II Reverse Engineering Project * PNG output routines for lbximg extration. - * Copyright © 2013 Nick Bowler + * Copyright © 2013-2014 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,7 +103,7 @@ static void fill_png_palette(png_color *out, const struct lbx_colour *in) static void write_rgba_frame(png_structp png, png_infop info, - unsigned char **framedata, unsigned char **mask, + unsigned char *pixels, unsigned char *mask, struct lbx_colour *lbx_palette) { png_uint_32 width = png_get_image_width(png, info); @@ -133,10 +133,15 @@ write_rgba_frame(png_structp png, png_infop info, fill_png_palette(png_palette, lbx_palette); for (png_uint_32 y = 0; y < height; y++) { for (png_uint_32 x = 0; x < width; x++) { - row[x][0] = png_palette[framedata[y][x]].red; - row[x][1] = png_palette[framedata[y][x]].green; - row[x][2] = png_palette[framedata[y][x]].blue; - row[x][3] = mask[y][x] ? -1 : 0; + unsigned long offset = (unsigned long) y * width + x; + png_color *c = &png_palette[pixels[offset]]; + bool vis; + + vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + row[x][0] = c->red; + row[x][1] = c->green; + row[x][2] = c->blue; + row[x][3] = -vis; } png_write_row(png, (void *)row); } @@ -147,7 +152,7 @@ write_rgba_frame(png_structp png, png_infop info, static void write_masked_index_frame(png_structp png, png_infop info, - unsigned char **framedata, unsigned char **mask) + unsigned char *pixels, unsigned char *mask) { png_uint_32 width = png_get_image_width(png, info); png_uint_32 height = png_get_image_height(png, info); @@ -174,8 +179,12 @@ write_masked_index_frame(png_structp png, png_infop info, png_write_info(png, info); for (png_uint_32 y = 0; y < height; y++) { for (png_uint_32 x = 0; x < width; x++) { - row[x][0] = framedata[y][x]; - row[x][1] = mask[y][x] ? -1 : 0; + unsigned long offset = (unsigned long) y * width + x; + bool vis; + + vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + row[x][0] = pixels[offset]; + row[x][1] = -vis; } png_write_row(png, (void *)row); } @@ -185,28 +194,35 @@ write_masked_index_frame(png_structp png, png_infop info, } static void -write_palette_frame(png_structp png, png_infop info, - unsigned char **framedata, struct lbx_colour *lbx_palette) +write_index_frame(png_structp png, png_infop info, unsigned char *pixels) { - png_color png_palette[256]; + png_uint_32 width = png_get_image_width(png, info); + png_uint_32 height = png_get_image_height(png, info); - fill_png_palette(png_palette, lbx_palette); - png_set_PLTE(png, info, png_palette, 256); - png_set_rows(png, info, framedata); - png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + png_write_info(png, info); + for (png_uint_32 y = 0; y < height; y++) { + unsigned long offset = (unsigned long) y * width; + + png_write_row(png, (void *)(pixels+offset)); + } + png_write_end(png, NULL); } static void -write_index_frame(png_structp png, png_infop info, unsigned char **framedata) +write_palette_frame(png_structp png, png_infop info, + unsigned char *pixels, struct lbx_colour *lbx_palette) { - png_set_rows(png, info, framedata); - png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + png_color png_palette[256]; + fill_png_palette(png_palette, lbx_palette); + png_set_PLTE(png, info, png_palette, 256); + + write_index_frame(png, info, pixels); } int img_output_png(FILE *f, const char *filename, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask, - struct lbx_colour *palette) + unsigned char *pixels, unsigned char *mask, + struct lbx_colour *palette) { bool masked = img_is_masked(mask, width, height); struct file_info file = { filename, f }; @@ -246,28 +262,28 @@ int img_output_png(FILE *f, const char *filename, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - write_rgba_frame(png, info, framedata, mask, palette); + write_rgba_frame(png, info, pixels, mask, palette); } else if (palette) { png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - write_palette_frame(png, info, framedata, palette); + write_palette_frame(png, info, pixels, palette); } else if (masked) { png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - write_masked_index_frame(png, info, framedata, mask); + write_masked_index_frame(png, info, pixels, mask); } else { png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - write_index_frame(png, info, framedata); + write_index_frame(png, info, pixels); } png_destroy_write_struct(&png, &info); diff --git a/src/pnm.c b/src/pnm.c index 865d2a1..ace5e18 100644 --- a/src/pnm.c +++ b/src/pnm.c @@ -1,7 +1,7 @@ /* * 2ooM: The Master of Orion II Reverse Engineering Project * Netpbm output routines for lbximg extration. - * Copyright © 2013 Nick Bowler + * Copyright © 2013-2014 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include #include #include +#include #include "image.h" #include "tools.h" @@ -183,8 +184,8 @@ static int fprintf_ascii(FILE *f, char *fmt, ...) */ int img_output_pbm(FILE *f, const char *filename, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask, - struct lbx_colour *palette) + unsigned char *pixels, unsigned char *mask, + struct lbx_colour *palette) { unsigned x, y; @@ -192,16 +193,14 @@ int img_output_pbm(FILE *f, const char *filename, goto err; for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { + unsigned long offset = (unsigned long) y * width + x; + bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF) goto err; - if (mask[y][x]) { - if (fputc(to_ascii('0'), f) == EOF) - goto err; - } else { - if (fputc(to_ascii('1'), f) == EOF) - goto err; - } + if (fputc(to_ascii(vis ? '0' : '1'), f) == EOF) + goto err; } if (fputc(to_ascii('\n'), f) == EOF) @@ -218,7 +217,7 @@ err: * normal output format. */ static int write_pgm(FILE *f, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask) + unsigned char *pixels, unsigned char *mask) { unsigned x, y; @@ -226,16 +225,14 @@ static int write_pgm(FILE *f, unsigned width, unsigned height, return -1; for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { + unsigned long offset = (unsigned long) y * width + x; + bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF) return -1; - if (!mask[y][x]) { - if (fprintf_ascii(f, " 0") < 0) - return -1; - } else { - if (fprintf_ascii(f, "%3hhu", framedata[y][x]) < 0) - return -1; - } + if (fprintf_ascii(f, "%3hhu", vis ? pixels[offset] : 0) < 0) + return -1; } if (fputc(to_ascii('\n'), f) == EOF) @@ -252,8 +249,8 @@ static int write_pgm(FILE *f, unsigned width, unsigned height, */ int img_output_ppm(FILE *f, const char *filename, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask, - struct lbx_colour *palette) + unsigned char *pixels, unsigned char *mask, + struct lbx_colour *palette) { unsigned x, y; @@ -262,7 +259,7 @@ int img_output_ppm(FILE *f, const char *filename, * For no-palette mode, write a PGM instead which is basically * the same format but has only one value per pixel. */ - if (write_pgm(f, width, height, framedata, mask) < 0) + if (write_pgm(f, width, height, pixels, mask) < 0) goto err; return 0; } @@ -271,19 +268,18 @@ int img_output_ppm(FILE *f, const char *filename, goto err; for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { + unsigned long offset = (unsigned long) y * width + x; + bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + struct lbx_colour c = { 0 }; + if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF) goto err; - if (!mask[y][x]) { - if (fprintf_ascii(f, " 0 0 0") < 0) - goto err; - } else { - struct lbx_colour *c = &palette[framedata[y][x]]; + if (vis) + c = palette[pixels[offset]]; - if (fprintf_ascii(f, "%2d %2d %2d", - c->red, c->green, c->blue) < 0) - goto err; - } + if (fprintf_ascii(f, "%2d %2d %2d", c.red, c.green, c.blue) < 0) + goto err; } if (fputc(to_ascii('\n'), f) == EOF) @@ -301,8 +297,8 @@ err: */ int img_output_pam(FILE *f, const char *filename, unsigned width, unsigned height, - unsigned char **framedata, unsigned char **mask, - struct lbx_colour *palette) + unsigned char *pixels, unsigned char *mask, + struct lbx_colour *palette) { bool masked = img_is_masked(mask, width, height); unsigned x, y; @@ -320,13 +316,16 @@ int img_output_pam(FILE *f, const char *filename, goto err; for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { - struct lbx_colour *c = &palette[framedata[y][x]]; + unsigned long offset = (unsigned long) y * width + x; + struct lbx_colour *c = &palette[pixels[offset]]; unsigned char buf[4]; + bool vis; + vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); buf[0] = c->red; buf[1] = c->green; buf[2] = c->blue; - buf[3] = mask[y][x] ? 63 : 0; + buf[3] = vis ? 63 : 0; if (fwrite(buf, 1, depth, f) < depth) goto err; @@ -340,10 +339,13 @@ int img_output_pam(FILE *f, const char *filename, goto err; for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { + unsigned long offset = (unsigned long) y * width + x; unsigned char buf[2]; + bool vis; - buf[0] = framedata[y][x]; - buf[1] = mask[y][x] ? 0xff : 0; + vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); + buf[0] = pixels[offset]; + buf[1] = vis ? 255 : 0; if (fwrite(buf, 1, depth, f) < depth) goto err;