amiga_imgv

changeset 8:a9edbbdb8213

forgot to add the ILBM image loader
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 29 Oct 2017 15:08:29 +0200
parents 4c36d0f44aa6
children 01bd8bbc46d4
files src/image_ilbm.c
diffstat 1 files changed, 391 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/image_ilbm.c	Sun Oct 29 15:08:29 2017 +0200
     1.3 @@ -0,0 +1,391 @@
     1.4 +#include <stdio.h>
     1.5 +#include <stdlib.h>
     1.6 +#include <string.h>
     1.7 +#include <errno.h>
     1.8 +#include <assert.h>
     1.9 +#include "image.h"
    1.10 +#include "logger.h"
    1.11 +#include "endian.h"
    1.12 +
    1.13 +#ifdef __GNUC__
    1.14 +#define PACKED	__attribute__((packed))
    1.15 +#else
    1.16 +#define PACKED
    1.17 +#endif
    1.18 +
    1.19 +#define MKID(a, b, c, d)	(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
    1.20 +
    1.21 +#define IS_IFF_CONTAINER(id)	((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
    1.22 +
    1.23 +enum {
    1.24 +	IFF_FORM = MKID('F', 'O', 'R', 'M'),
    1.25 +	IFF_CAT = MKID('C', 'A', 'T', ' '),
    1.26 +	IFF_LIST = MKID('L', 'I', 'S', 'T'),
    1.27 +	IFF_ILBM = MKID('I', 'L', 'B', 'M'),
    1.28 +	IFF_PBM = MKID('P', 'B', 'M', ' '),
    1.29 +	IFF_BMHD = MKID('B', 'M', 'H', 'D'),
    1.30 +	IFF_CMAP = MKID('C', 'M', 'A', 'P'),
    1.31 +	IFF_BODY = MKID('B', 'O', 'D', 'Y'),
    1.32 +	IFF_CRNG = MKID('C', 'R', 'N', 'G')
    1.33 +};
    1.34 +
    1.35 +
    1.36 +struct chdr {
    1.37 +	uint32_t id;
    1.38 +	uint32_t size;
    1.39 +};
    1.40 +
    1.41 +#ifdef __WATCOMC__
    1.42 +#pragma push(pack, 1)
    1.43 +#endif
    1.44 +struct bitmap_header {
    1.45 +	uint16_t width, height;
    1.46 +	int16_t xoffs, yoffs;
    1.47 +	uint8_t nplanes;
    1.48 +	uint8_t masking;
    1.49 +	uint8_t compression;
    1.50 +	uint8_t padding;
    1.51 +	uint16_t colorkey;
    1.52 +	uint8_t aspect_num, aspect_denom;
    1.53 +	int16_t pgwidth, pgheight;
    1.54 +} PACKED;
    1.55 +#ifdef __WATCOMC__
    1.56 +#pragma pop(pack)
    1.57 +#endif
    1.58 +
    1.59 +enum {
    1.60 +	MASK_NONE,
    1.61 +	MASK_PLANE,
    1.62 +	MASK_COLORKEY,
    1.63 +	MASK_LASSO
    1.64 +};
    1.65 +
    1.66 +struct crng {
    1.67 +	uint16_t padding;
    1.68 +	uint16_t rate;
    1.69 +	uint16_t flags;
    1.70 +	uint8_t low, high;
    1.71 +};
    1.72 +
    1.73 +enum {
    1.74 +	CRNG_ENABLE = 1,
    1.75 +	CRNG_REVERSE = 2
    1.76 +};
    1.77 +
    1.78 +static int read_header(FILE *fp, struct chdr *hdr);
    1.79 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img);
    1.80 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd);
    1.81 +static int read_crng(FILE *fp, struct crng *crng);
    1.82 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img);
    1.83 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img);
    1.84 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width);
    1.85 +
    1.86 +int file_is_lbm(FILE *fp)
    1.87 +{
    1.88 +	uint32_t type;
    1.89 +	struct chdr hdr;
    1.90 +
    1.91 +	while(read_header(fp, &hdr) != -1) {
    1.92 +		if(IS_IFF_CONTAINER(hdr.id)) {
    1.93 +			if(fread(&type, 4, 1, fp) < 1) {
    1.94 +				break;
    1.95 +			}
    1.96 +			type = ntohl(type);
    1.97 +
    1.98 +			if(type == IFF_ILBM || type == IFF_PBM ) {
    1.99 +				rewind(fp);
   1.100 +				return 1;
   1.101 +			}
   1.102 +			hdr.size -= sizeof type;	/* so we will seek fwd correctly */
   1.103 +		}
   1.104 +		fseek(fp, hdr.size, SEEK_CUR);
   1.105 +	}
   1.106 +	fseek(fp, 0, SEEK_SET);
   1.107 +	return 0;
   1.108 +}
   1.109 +
   1.110 +struct ham_image *load_ilbm(const char *fname)
   1.111 +{
   1.112 +	uint32_t type;
   1.113 +	struct chdr hdr;
   1.114 +	FILE *fp = 0;
   1.115 +	struct ham_image *img = 0;
   1.116 +
   1.117 +	if(!(fp = fopen(fname, "rb"))) {
   1.118 +		logmsg("failed to open %s: %s\n", fname, strerror(errno));
   1.119 +		return 0;
   1.120 +	}
   1.121 +	if(!(img = calloc(1, sizeof *img))) {
   1.122 +		logmsg("failed to allocate image structure\n");
   1.123 +		goto err;
   1.124 +	}
   1.125 +
   1.126 +	while(read_header(fp, &hdr) != -1) {
   1.127 +		if(IS_IFF_CONTAINER(hdr.id)) {
   1.128 +			if(fread(&type, 4, 1, fp) < 1) {
   1.129 +				break;
   1.130 +			}
   1.131 +			type = ntohl(type);
   1.132 +			hdr.size -= sizeof type;	/* to compensate for having advanced 4 more bytes */
   1.133 +
   1.134 +			if(type == IFF_ILBM) {
   1.135 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   1.136 +					goto err;
   1.137 +				}
   1.138 +				return img;
   1.139 +			}
   1.140 +			if(type == IFF_PBM) {
   1.141 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   1.142 +					goto err;
   1.143 +				}
   1.144 +				return img;
   1.145 +			}
   1.146 +		}
   1.147 +		fseek(fp, hdr.size, SEEK_CUR);
   1.148 +	}
   1.149 +
   1.150 +err:
   1.151 +	fclose(fp);
   1.152 +	free(img);
   1.153 +	return 0;
   1.154 +}
   1.155 +
   1.156 +static int read_header(FILE *fp, struct chdr *hdr)
   1.157 +{
   1.158 +	if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) {
   1.159 +		return -1;
   1.160 +	}
   1.161 +	hdr->id = ntohl(hdr->id);
   1.162 +	hdr->size = ntohl(hdr->size);
   1.163 +	return 0;
   1.164 +}
   1.165 +
   1.166 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct ham_image *img)
   1.167 +{
   1.168 +	int i, palsz, res = -1;
   1.169 +	struct chdr hdr;
   1.170 +	struct bitmap_header bmhd;
   1.171 +	struct crng crng;
   1.172 +	struct colrange *crnode;
   1.173 +	unsigned char pal[3 * 256];
   1.174 +	unsigned char *pptr;
   1.175 +	long start = ftell(fp);
   1.176 +
   1.177 +	while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) {
   1.178 +		switch(hdr.id) {
   1.179 +		case IFF_BMHD:
   1.180 +			assert(hdr.size == 20);
   1.181 +			if(read_bmhd(fp, &bmhd) == -1) {
   1.182 +				return -1;
   1.183 +			}
   1.184 +			img->width = bmhd.width;
   1.185 +			img->height = bmhd.height;
   1.186 +			if(bmhd.nplanes > 8) {
   1.187 +				logmsg("%d planes found, only paletized LBM files supported\n", (int)bmhd.nplanes);
   1.188 +				return -1;
   1.189 +			}
   1.190 +			img->nbitplanes = bmhd.nplanes;
   1.191 +
   1.192 +			if(!(img->pixels = malloc(img->width * img->height / 8 * img->nbitplanes))) {
   1.193 +				logmsg("failed to allocate %dx%d (%d bitplane) image\n", img->width, img->height, img->nbitplanes);
   1.194 +				return -1;
   1.195 +			}
   1.196 +			break;
   1.197 +
   1.198 +		case IFF_CMAP:
   1.199 +			assert(hdr.size / 3 <= 256);
   1.200 +
   1.201 +			palsz = hdr.size / 3;
   1.202 +
   1.203 +			if(fread(pal, 1, hdr.size, fp) < hdr.size) {
   1.204 +				logmsg("failed to read colormap\n");
   1.205 +				return -1;
   1.206 +			}
   1.207 +			pptr = pal;
   1.208 +			for(i=0; i<palsz; i++) {
   1.209 +				uint16_t r = ((uint16_t)*pptr++ << 4) & 0xf00;
   1.210 +				uint16_t g = (uint16_t)*pptr++ & 0xf0;
   1.211 +				uint16_t b = ((uint16_t)*pptr++ >> 4) & 0xf;
   1.212 +				img->palette[i] = r | g | b;
   1.213 +			}
   1.214 +			break;
   1.215 +
   1.216 +			/*
   1.217 +		case IFF_CRNG:
   1.218 +			assert(hdr.size == sizeof crng);
   1.219 +
   1.220 +			if(read_crng(fp, &crng) == -1) {
   1.221 +				logmsg("failed to read color cycling range chunk\n");
   1.222 +				return -1;
   1.223 +			}
   1.224 +			if(crng.low != crng.high && crng.rate > 0) {
   1.225 +				if(!(crnode = malloc(sizeof *crnode))) {
   1.226 +					logmsg("failed to allocate color range node\n");
   1.227 +					return -1;
   1.228 +				}
   1.229 +				crnode->low = crng.low;
   1.230 +				crnode->high = crng.high;
   1.231 +				crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
   1.232 +				crnode->rate = crng.rate;
   1.233 +				crnode->next = img->range;
   1.234 +				img->range = crnode;
   1.235 +				++img->num_ranges;
   1.236 +			}
   1.237 +			break;
   1.238 +			*/
   1.239 +
   1.240 +		case IFF_BODY:
   1.241 +			if(!img->pixels) {
   1.242 +				logmsg("malformed ILBM image: encountered BODY chunk before BMHD\n");
   1.243 +				return -1;
   1.244 +			}
   1.245 +			if(type == IFF_ILBM) {
   1.246 +				if(read_body_ilbm(fp, &bmhd, img) == -1) {
   1.247 +					logmsg("failed to read interleaved pixel data\n");
   1.248 +					return -1;
   1.249 +				}
   1.250 +			} else {
   1.251 +				assert(type == IFF_PBM);
   1.252 +				if(read_body_pbm(fp, &bmhd, img) == -1) {
   1.253 +					logmsg("failed to read linear pixel data\n");
   1.254 +					return -1;
   1.255 +				}
   1.256 +			}
   1.257 +			res = 0;	/* sucessfully read image */
   1.258 +			break;
   1.259 +
   1.260 +		default:
   1.261 +			/* skip unknown chunks */
   1.262 +			fseek(fp, hdr.size, SEEK_CUR);
   1.263 +			if(ftell(fp) & 1) {
   1.264 +				/* chunks must start at even offsets */
   1.265 +				fseek(fp, 1, SEEK_CUR);
   1.266 +			}
   1.267 +		}
   1.268 +	}
   1.269 +
   1.270 +	return res;
   1.271 +}
   1.272 +
   1.273 +
   1.274 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd)
   1.275 +{
   1.276 +	if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) {
   1.277 +		return -1;
   1.278 +	}
   1.279 +	bmhd->width = ntohs(bmhd->width);
   1.280 +	bmhd->height = ntohs(bmhd->height);
   1.281 +	bmhd->xoffs = ntohs(bmhd->xoffs);
   1.282 +	bmhd->yoffs = ntohs(bmhd->yoffs);
   1.283 +	bmhd->colorkey = ntohs(bmhd->colorkey);
   1.284 +	bmhd->pgwidth = ntohs(bmhd->pgwidth);
   1.285 +	bmhd->pgheight = ntohs(bmhd->pgheight);
   1.286 +	return 0;
   1.287 +}
   1.288 +
   1.289 +static int read_crng(FILE *fp, struct crng *crng)
   1.290 +{
   1.291 +	if(fread(crng, sizeof *crng, 1, fp) < 1) {
   1.292 +		return -1;
   1.293 +	}
   1.294 +	crng->rate = ntohs(crng->rate);
   1.295 +	crng->flags = ntohs(crng->flags);
   1.296 +	return 0;
   1.297 +}
   1.298 +
   1.299 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
   1.300 + * each uncompressed row is width / 8 bytes
   1.301 + */
   1.302 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img)
   1.303 +{
   1.304 +	int i, j;
   1.305 +	int rowsz = img->width / 8;
   1.306 +	unsigned char *dest = img->pixels;
   1.307 +
   1.308 +	assert(bmhd->width == img->width);
   1.309 +	assert(bmhd->height == img->height);
   1.310 +	assert(img->pixels);
   1.311 +
   1.312 +	for(i=0; i<img->height; i++) {
   1.313 +
   1.314 +		for(j=0; j<bmhd->nplanes; j++) {
   1.315 +			/* read a row corresponding to bitplane j */
   1.316 +			if(bmhd->compression) {
   1.317 +				if(read_compressed_scanline(fp, dest, rowsz) == -1) {
   1.318 +					return -1;
   1.319 +				}
   1.320 +			} else {
   1.321 +				if(fread(dest, 1, rowsz, fp) < rowsz) {
   1.322 +					return -1;
   1.323 +				}
   1.324 +			}
   1.325 +			dest += rowsz;
   1.326 +		}
   1.327 +
   1.328 +		if(bmhd->masking & MASK_PLANE) {
   1.329 +			/* skip the mask (1bpp) */
   1.330 +			fseek(fp, rowsz, SEEK_CUR);
   1.331 +		}
   1.332 +	}
   1.333 +	return 0;
   1.334 +}
   1.335 +
   1.336 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct ham_image *img)
   1.337 +{
   1.338 +	/*
   1.339 +	int i;
   1.340 +	int npixels = img->width * img->height;
   1.341 +	unsigned char *dptr = img->pixels;
   1.342 +
   1.343 +	assert(bmhd->width == img->width);
   1.344 +	assert(bmhd->height == img->height);
   1.345 +	assert(img->pixels);
   1.346 +
   1.347 +	if(bmhd->compression) {
   1.348 +		for(i=0; i<img->height; i++) {
   1.349 +			if(read_compressed_scanline(fp, dptr, img->width) == -1) {
   1.350 +				return -1;
   1.351 +			}
   1.352 +			dptr += img->width;
   1.353 +		}
   1.354 +
   1.355 +	} else {
   1.356 +		if(fread(img->pixels, 1, npixels, fp) < npixels) {
   1.357 +			return -1;
   1.358 +		}
   1.359 +	}
   1.360 +	return 0;
   1.361 +	*/
   1.362 +	return -1;	/* TODO */
   1.363 +}
   1.364 +
   1.365 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width)
   1.366 +{
   1.367 +	int i, count, x = 0;
   1.368 +	signed char ctl;
   1.369 +
   1.370 +	while(x < width) {
   1.371 +		if(fread(&ctl, 1, 1, fp) < 1) return -1;
   1.372 +
   1.373 +		if(ctl == -128) continue;
   1.374 +
   1.375 +		if(ctl >= 0) {
   1.376 +			count = ctl + 1;
   1.377 +			if(fread(scanline, 1, count, fp) < count) return -1;
   1.378 +			scanline += count;
   1.379 +
   1.380 +		} else {
   1.381 +			unsigned char pixel;
   1.382 +			count = 1 - ctl;
   1.383 +			if(fread(&pixel, 1, 1, fp) < 1) return -1;
   1.384 +
   1.385 +			for(i=0; i<count; i++) {
   1.386 +				*scanline++ = pixel;
   1.387 +			}
   1.388 +		}
   1.389 +
   1.390 +		x += count;
   1.391 +	}
   1.392 +
   1.393 +	return 0;
   1.394 +}