# HG changeset patch # User John Tsiombikas # Date 1509282509 -7200 # Node ID a9edbbdb8213e3e58cbc7a12ca07518de3f6b563 # Parent 4c36d0f44aa6d99a0a50b429f03f6d48397db932 forgot to add the ILBM image loader diff -r 4c36d0f44aa6 -r a9edbbdb8213 src/image_ilbm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_ilbm.c Sun Oct 29 15:08:29 2017 +0200 @@ -0,0 +1,391 @@ +#include +#include +#include +#include +#include +#include "image.h" +#include "logger.h" +#include "endian.h" + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#define 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; +}; + +#ifdef __WATCOMC__ +#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; +#ifdef __WATCOMC__ +#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 read_header(FILE *fp, struct chdr *hdr); +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img); +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd); +static int read_crng(FILE *fp, struct crng *crng); +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img); +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img); +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width); + +int file_is_lbm(FILE *fp) +{ + uint32_t type; + struct chdr hdr; + + while(read_header(fp, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + if(fread(&type, 4, 1, fp) < 1) { + break; + } + type = ntohl(type); + + if(type == IFF_ILBM || type == IFF_PBM ) { + rewind(fp); + return 1; + } + hdr.size -= sizeof type; /* so we will seek fwd correctly */ + } + fseek(fp, hdr.size, SEEK_CUR); + } + fseek(fp, 0, SEEK_SET); + return 0; +} + +struct ham_image *load_ilbm(const char *fname) +{ + uint32_t type; + struct chdr hdr; + FILE *fp = 0; + struct ham_image *img = 0; + + if(!(fp = fopen(fname, "rb"))) { + logmsg("failed to open %s: %s\n", fname, strerror(errno)); + return 0; + } + if(!(img = calloc(1, sizeof *img))) { + logmsg("failed to allocate image structure\n"); + goto err; + } + + while(read_header(fp, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + if(fread(&type, 4, 1, fp) < 1) { + break; + } + type = ntohl(type); + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ + + if(type == IFF_ILBM) { + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { + goto err; + } + return img; + } + if(type == IFF_PBM) { + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { + goto err; + } + return img; + } + } + fseek(fp, hdr.size, SEEK_CUR); + } + +err: + fclose(fp); + free(img); + return 0; +} + +static int read_header(FILE *fp, struct chdr *hdr) +{ + if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) { + return -1; + } + hdr->id = ntohl(hdr->id); + hdr->size = ntohl(hdr->size); + return 0; +} + +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img) +{ + int i, palsz, res = -1; + struct chdr hdr; + struct bitmap_header bmhd; + struct crng crng; + struct colrange *crnode; + unsigned char pal[3 * 256]; + unsigned char *pptr; + long start = ftell(fp); + + while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) { + switch(hdr.id) { + case IFF_BMHD: + assert(hdr.size == 20); + if(read_bmhd(fp, &bmhd) == -1) { + return -1; + } + img->width = bmhd.width; + img->height = bmhd.height; + if(bmhd.nplanes > 8) { + logmsg("%d planes found, only paletized LBM files supported\n", (int)bmhd.nplanes); + return -1; + } + img->nbitplanes = bmhd.nplanes; + + if(!(img->pixels = malloc(img->width * img->height / 8 * img->nbitplanes))) { + logmsg("failed to allocate %dx%d (%d bitplane) image\n", img->width, img->height, img->nbitplanes); + return -1; + } + break; + + case IFF_CMAP: + assert(hdr.size / 3 <= 256); + + palsz = hdr.size / 3; + + if(fread(pal, 1, hdr.size, fp) < hdr.size) { + logmsg("failed to read colormap\n"); + return -1; + } + pptr = pal; + for(i=0; i> 4) & 0xf; + img->palette[i] = r | g | b; + } + break; + + /* + case IFF_CRNG: + assert(hdr.size == sizeof crng); + + if(read_crng(fp, &crng) == -1) { + logmsg("failed to read color cycling range chunk\n"); + return -1; + } + if(crng.low != crng.high && crng.rate > 0) { + if(!(crnode = malloc(sizeof *crnode))) { + logmsg("failed to allocate color range node\n"); + 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) { + logmsg("malformed ILBM image: encountered BODY chunk before BMHD\n"); + return -1; + } + if(type == IFF_ILBM) { + if(read_body_ilbm(fp, &bmhd, img) == -1) { + logmsg("failed to read interleaved pixel data\n"); + return -1; + } + } else { + assert(type == IFF_PBM); + if(read_body_pbm(fp, &bmhd, img) == -1) { + logmsg("failed to read linear pixel data\n"); + return -1; + } + } + res = 0; /* sucessfully read image */ + break; + + default: + /* skip unknown chunks */ + fseek(fp, hdr.size, SEEK_CUR); + if(ftell(fp) & 1) { + /* chunks must start at even offsets */ + fseek(fp, 1, SEEK_CUR); + } + } + } + + return res; +} + + +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd) +{ + if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) { + return -1; + } + bmhd->width = ntohs(bmhd->width); + bmhd->height = ntohs(bmhd->height); + bmhd->xoffs = ntohs(bmhd->xoffs); + bmhd->yoffs = ntohs(bmhd->yoffs); + bmhd->colorkey = ntohs(bmhd->colorkey); + bmhd->pgwidth = ntohs(bmhd->pgwidth); + bmhd->pgheight = ntohs(bmhd->pgheight); + return 0; +} + +static int read_crng(FILE *fp, struct crng *crng) +{ + if(fread(crng, sizeof *crng, 1, fp) < 1) { + return -1; + } + crng->rate = ntohs(crng->rate); + crng->flags = ntohs(crng->flags); + 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(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img) +{ + int i, j; + int rowsz = img->width / 8; + unsigned char *dest = img->pixels; + + assert(bmhd->width == img->width); + assert(bmhd->height == img->height); + assert(img->pixels); + + for(i=0; iheight; i++) { + + for(j=0; jnplanes; j++) { + /* read a row corresponding to bitplane j */ + if(bmhd->compression) { + if(read_compressed_scanline(fp, dest, rowsz) == -1) { + return -1; + } + } else { + if(fread(dest, 1, rowsz, fp) < rowsz) { + return -1; + } + } + dest += rowsz; + } + + if(bmhd->masking & MASK_PLANE) { + /* skip the mask (1bpp) */ + fseek(fp, rowsz, SEEK_CUR); + } + } + return 0; +} + +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *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(fp, dptr, img->width) == -1) { + return -1; + } + dptr += img->width; + } + + } else { + if(fread(img->pixels, 1, npixels, fp) < npixels) { + return -1; + } + } + return 0; + */ + return -1; /* TODO */ +} + +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width) +{ + int i, count, x = 0; + signed char ctl; + + while(x < width) { + if(fread(&ctl, 1, 1, fp) < 1) return -1; + + if(ctl == -128) continue; + + if(ctl >= 0) { + count = ctl + 1; + if(fread(scanline, 1, count, fp) < count) return -1; + scanline += count; + + } else { + unsigned char pixel; + count = 1 - ctl; + if(fread(&pixel, 1, 1, fp) < 1) return -1; + + for(i=0; i