X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=demo_prior;a=blobdiff_plain;f=libs%2Fimago2%2Fsrc%2Ffile_lbm.c;fp=libs%2Fimago2%2Fsrc%2Ffile_lbm.c;h=de18831c90a0d06f6369937a51bf7980b3c628c9;hp=0000000000000000000000000000000000000000;hb=3a9f6854df479d81442273c9d0b133c49c5c8f66;hpb=0b24071f728b7c8550daa1b7aa7c4012cb70ef4c diff --git a/libs/imago2/src/file_lbm.c b/libs/imago2/src/file_lbm.c new file mode 100644 index 0000000..de18831 --- /dev/null +++ b/libs/imago2/src/file_lbm.c @@ -0,0 +1,452 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2017 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* -- LBM (PNM/ILBM) module -- */ +#include +#include +#include +#include +#if defined(__WATCOMC__) || defined(WIN32) +#include +#else +#include +#endif +#include "imago2.h" +#include "ftype_module.h" +#include "byteord.h" + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#endif + +#define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +#define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST) + +enum { + IFF_FORM = MKID('F', 'O', 'R', 'M'), + IFF_CAT = MKID('C', 'A', 'T', ' '), + IFF_LIST = MKID('L', 'I', 'S', 'T'), + IFF_ILBM = MKID('I', 'L', 'B', 'M'), + IFF_PBM = MKID('P', 'B', 'M', ' '), + IFF_BMHD = MKID('B', 'M', 'H', 'D'), + IFF_CMAP = MKID('C', 'M', 'A', 'P'), + IFF_BODY = MKID('B', 'O', 'D', 'Y'), + IFF_CRNG = MKID('C', 'R', 'N', 'G') +}; + +struct chdr { + uint32_t id; + uint32_t size; +}; + +#if defined(__WATCOMC__) || defined(_MSC_VER) +#pragma push(pack, 1) +#endif +struct bitmap_header { + uint16_t width, height; + int16_t xoffs, yoffs; + uint8_t nplanes; + uint8_t masking; + uint8_t compression; + uint8_t padding; + uint16_t colorkey; + uint8_t aspect_num, aspect_denom; + int16_t pgwidth, pgheight; +} PACKED; +#if defined(__WATCOMC__) || defined(_MSC_VER) +#pragma pop(pack) +#endif + +enum { + MASK_NONE, + MASK_PLANE, + MASK_COLORKEY, + MASK_LASSO +}; + +struct crng { + uint16_t padding; + uint16_t rate; + uint16_t flags; + uint8_t low, high; +}; + +enum { + CRNG_ENABLE = 1, + CRNG_REVERSE = 2 +}; + + +static int check_file(struct img_io *io); +static int read_file(struct img_pixmap *img, struct img_io *io); +static int write_file(struct img_pixmap *img, struct img_io *io); + +static int read_header(struct img_io *io, struct chdr *hdr); +static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img); +static void convert_rgb(struct img_pixmap *img, unsigned char *pal); +static int read_bmhd(struct img_io *io, struct bitmap_header *bmhd); +static int read_crng(struct img_io *io, struct crng *crng); +static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img); +static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img); +static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width); + +#ifdef IMAGO_LITTLE_ENDIAN +static uint16_t swap16(uint16_t x); +static uint32_t swap32(uint32_t x); +#endif + + +int img_register_lbm(void) +{ + static struct ftype_module mod = {".lbm:.ilbm:.iff", check_file, read_file, write_file }; + return img_register_module(&mod); +} + +static int check_file(struct img_io *io) +{ + uint32_t type; + struct chdr hdr; + long pos = io->seek(0, SEEK_CUR, io->uptr); + + while(read_header(io, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + type = img_read_uint32_be(io); + if(type == IFF_ILBM || type == IFF_PBM ) { + io->seek(pos, SEEK_SET, io->uptr); + return 0; + } + hdr.size -= sizeof type; /* so we will seek fwd correctly */ + } + io->seek(hdr.size, SEEK_CUR, io->uptr); + } + + io->seek(pos, SEEK_SET, io->uptr); + return -1; +} + +static int read_file(struct img_pixmap *img, struct img_io *io) +{ + uint32_t type; + struct chdr hdr; + + while(read_header(io, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + type = img_read_uint32_be(io); + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ + + if(type == IFF_ILBM) { + if(read_ilbm_pbm(io, type, hdr.size, img) == -1) { + return -1; + } + return 0; + } + if(type == IFF_PBM) { + if(read_ilbm_pbm(io, type, hdr.size, img) == -1) { + return -1; + } + return 0; + } + } + io->seek(hdr.size, SEEK_CUR, io->uptr); + } + return 0; +} + +static int write_file(struct img_pixmap *img, struct img_io *io) +{ + return -1; /* TODO */ +} + +static int read_header(struct img_io *io, struct chdr *hdr) +{ + if(io->read(hdr, sizeof *hdr, io->uptr) < sizeof *hdr) { + return -1; + } +#ifdef IMAGO_LITTLE_ENDIAN + hdr->id = swap32(hdr->id); + hdr->size = swap32(hdr->size); +#endif + return 0; +} + +static int read_ilbm_pbm(struct img_io *io, uint32_t type, uint32_t size, struct img_pixmap *img) +{ + int res = -1; + struct chdr hdr; + struct bitmap_header bmhd; + struct crng crng; + /*struct colrange *crnode;*/ + unsigned char pal[3 * 256]; + long start = io->seek(0, SEEK_CUR, io->uptr); + + memset(img, 0, sizeof *img); + + while(read_header(io, &hdr) != -1 && io->seek(0, SEEK_CUR, io->uptr) - start < size) { + switch(hdr.id) { + case IFF_BMHD: + assert(hdr.size == 20); + if(read_bmhd(io, &bmhd) == -1) { + return -1; + } + img->width = bmhd.width; + img->height = bmhd.height; + if(bmhd.nplanes > 8) { + /* TODO */ + fprintf(stderr, "libimago: %d planes found, only paletized LBM files supported\n", bmhd.nplanes); + return -1; + } + if(img_set_pixels(img, img->width, img->height, IMG_FMT_RGB24, 0)) { + return -1; + } + break; + + case IFF_CMAP: + assert(hdr.size / 3 <= 256); + + if(io->read(pal, hdr.size, io->uptr) < hdr.size) { + return -1; + } + break; + + case IFF_CRNG: + assert(hdr.size == sizeof crng); + + if(read_crng(io, &crng) == -1) { + return -1; + } + if(crng.low != crng.high && crng.rate > 0) { + /* XXX color cycling not currently supported + if(!(crnode = malloc(sizeof *crnode))) { + return -1; + } + crnode->low = crng.low; + crnode->high = crng.high; + crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL; + crnode->rate = crng.rate; + crnode->next = img->range; + img->range = crnode; + ++img->num_ranges; + */ + } + break; + + case IFF_BODY: + if(!img->pixels) { + fprintf(stderr, "libimago: malformed LBM image: encountered BODY chunk before BMHD\n"); + return -1; + } + if(type == IFF_ILBM) { + if(read_body_ilbm(io, &bmhd, img) == -1) { + return -1; + } + } else { + assert(type == IFF_PBM); + if(read_body_pbm(io, &bmhd, img) == -1) { + return -1; + } + } + + convert_rgb(img, pal); + + res = 0; /* sucessfully read image */ + break; + + default: + /* skip unknown chunks */ + io->seek(hdr.size, SEEK_CUR, io->uptr); + if(io->seek(0, SEEK_CUR, io->uptr) & 1) { + /* chunks must start at even offsets */ + io->seek(1, SEEK_CUR, io->uptr); + } + } + } + + return res; +} + +static void convert_rgb(struct img_pixmap *img, unsigned char *pal) +{ + int i, npixels = img->width * img->height; + unsigned char *sptr, *dptr = img->pixels; + + dptr = (unsigned char*)img->pixels + npixels * 3; + sptr = (unsigned char*)img->pixels + npixels; + + for(i=0; iread(bmhd, sizeof *bmhd, io->uptr) < 1) { + return -1; + } +#ifdef IMAGO_LITTLE_ENDIAN + bmhd->width = swap16(bmhd->width); + bmhd->height = swap16(bmhd->height); + bmhd->xoffs = swap16(bmhd->xoffs); + bmhd->yoffs = swap16(bmhd->yoffs); + bmhd->colorkey = swap16(bmhd->colorkey); + bmhd->pgwidth = swap16(bmhd->pgwidth); + bmhd->pgheight = swap16(bmhd->pgheight); +#endif + return 0; +} + +static int read_crng(struct img_io *io, struct crng *crng) +{ + if(io->read(crng, sizeof *crng, io->uptr) < 1) { + return -1; + } +#ifdef IMAGO_LITTLE_ENDIAN + crng->rate = swap16(crng->rate); + crng->flags = swap16(crng->flags); +#endif + return 0; +} + +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row] + * each uncompressed row is width / 8 bytes + */ +static int read_body_ilbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img) +{ + int i, j, k, bitidx; + int rowsz = img->width / 8; + unsigned char *src, *dest = img->pixels; + unsigned char *rowbuf = alloca(rowsz); + + assert(bmhd->width == img->width); + assert(bmhd->height == img->height); + assert(img->pixels); + + for(i=0; iheight; i++) { + + memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */ + + for(j=0; jnplanes; j++) { + /* read a row corresponding to bitplane j */ + if(bmhd->compression) { + if(read_compressed_scanline(io, rowbuf, rowsz) == -1) { + return -1; + } + } else { + if(io->read(rowbuf, rowsz, io->uptr) < rowsz) { + return -1; + } + } + + /* distribute all bits across the linear output scanline */ + src = rowbuf; + bitidx = 0; + + for(k=0; kwidth; k++) { + dest[k] |= ((*src >> (7 - bitidx)) & 1) << j; + + if(++bitidx >= 8) { + bitidx = 0; + ++src; + } + } + } + + if(bmhd->masking & MASK_PLANE) { + /* skip the mask (1bpp) */ + io->seek(rowsz, SEEK_CUR, io->uptr); + } + + dest += img->width; + } + return 0; +} + +static int read_body_pbm(struct img_io *io, struct bitmap_header *bmhd, struct img_pixmap *img) +{ + int i; + int npixels = img->width * img->height; + unsigned char *dptr = img->pixels; + + assert(bmhd->width == img->width); + assert(bmhd->height == img->height); + assert(img->pixels); + + if(bmhd->compression) { + for(i=0; iheight; i++) { + if(read_compressed_scanline(io, dptr, img->width) == -1) { + return -1; + } + dptr += img->width; + } + + } else { + /* uncompressed */ + if(io->read(img->pixels, npixels, io->uptr) < npixels) { + return -1; + } + } + + return 0; +} + +static int read_compressed_scanline(struct img_io *io, unsigned char *scanline, int width) +{ + int i, count, x = 0; + signed char ctl; + + while(x < width) { + if(io->read(&ctl, 1, io->uptr) < 1) return -1; + + if(ctl == -128) continue; + + if(ctl >= 0) { + count = ctl + 1; + if(io->read(scanline, count, io->uptr) < count) return -1; + scanline += count; + + } else { + unsigned char pixel; + count = 1 - ctl; + if(io->read(&pixel, 1, io->uptr) < 1) return -1; + + for(i=0; i> 8); +} + +static uint32_t swap32(uint32_t x) +{ + return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); +} +#endif