winlivebg_test1

diff src/imagelbm.c @ 2:a9025f31ae2d

sortof works, testing with colcycle hack
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 28 Oct 2019 16:07:25 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/imagelbm.c	Mon Oct 28 16:07:25 2019 +0200
     1.3 @@ -0,0 +1,463 @@
     1.4 +/*
     1.5 +colcycle - color cycling image viewer
     1.6 +Copyright (C) 2016-2017  John Tsiombikas <nuclear@member.fsf.org>
     1.7 +
     1.8 +This program is free software: you can redistribute it and/or modify
     1.9 +it under the terms of the GNU General Public License as published by
    1.10 +the Free Software Foundation, either version 3 of the License, or
    1.11 +(at your option) any later version.
    1.12 +
    1.13 +This program is distributed in the hope that it will be useful,
    1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.16 +GNU General Public License for more details.
    1.17 +
    1.18 +You should have received a copy of the GNU General Public License
    1.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1.20 +*/
    1.21 +#include <stdio.h>
    1.22 +#include <stdlib.h>
    1.23 +#include <assert.h>
    1.24 +#include <inttypes.h>
    1.25 +#include <string.h>
    1.26 +#if defined(__WATCOMC__) || defined(WIN32)
    1.27 +#include <malloc.h>
    1.28 +#else
    1.29 +#include <alloca.h>
    1.30 +#endif
    1.31 +#include "imagelbm.h"
    1.32 +
    1.33 +#ifdef __GNUC__
    1.34 +#define PACKED	__attribute__((packed))
    1.35 +#endif
    1.36 +
    1.37 +#ifdef __BYTE_ORDER__
    1.38 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    1.39 +#define LENDIAN
    1.40 +#else
    1.41 +#define BENDIAN
    1.42 +#endif
    1.43 +#endif
    1.44 +
    1.45 +#define MKID(a, b, c, d)	(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
    1.46 +
    1.47 +#define IS_IFF_CONTAINER(id)	((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
    1.48 +
    1.49 +enum {
    1.50 +	IFF_FORM = MKID('F', 'O', 'R', 'M'),
    1.51 +	IFF_CAT = MKID('C', 'A', 'T', ' '),
    1.52 +	IFF_LIST = MKID('L', 'I', 'S', 'T'),
    1.53 +	IFF_ILBM = MKID('I', 'L', 'B', 'M'),
    1.54 +	IFF_PBM = MKID('P', 'B', 'M', ' '),
    1.55 +	IFF_BMHD = MKID('B', 'M', 'H', 'D'),
    1.56 +	IFF_CMAP = MKID('C', 'M', 'A', 'P'),
    1.57 +	IFF_BODY = MKID('B', 'O', 'D', 'Y'),
    1.58 +	IFF_CRNG = MKID('C', 'R', 'N', 'G')
    1.59 +};
    1.60 +
    1.61 +
    1.62 +struct chdr {
    1.63 +	uint32_t id;
    1.64 +	uint32_t size;
    1.65 +};
    1.66 +
    1.67 +#ifdef __WATCOMC__
    1.68 +#pragma push(pack, 1)
    1.69 +#endif
    1.70 +struct bitmap_header {
    1.71 +	uint16_t width, height;
    1.72 +	int16_t xoffs, yoffs;
    1.73 +	uint8_t nplanes;
    1.74 +	uint8_t masking;
    1.75 +	uint8_t compression;
    1.76 +	uint8_t padding;
    1.77 +	uint16_t colorkey;
    1.78 +	uint8_t aspect_num, aspect_denom;
    1.79 +	int16_t pgwidth, pgheight;
    1.80 +} PACKED;
    1.81 +#ifdef __WATCOMC__
    1.82 +#pragma pop(pack)
    1.83 +#endif
    1.84 +
    1.85 +enum {
    1.86 +	MASK_NONE,
    1.87 +	MASK_PLANE,
    1.88 +	MASK_COLORKEY,
    1.89 +	MASK_LASSO
    1.90 +};
    1.91 +
    1.92 +struct crng {
    1.93 +	uint16_t padding;
    1.94 +	uint16_t rate;
    1.95 +	uint16_t flags;
    1.96 +	uint8_t low, high;
    1.97 +};
    1.98 +
    1.99 +enum {
   1.100 +	CRNG_ENABLE = 1,
   1.101 +	CRNG_REVERSE = 2
   1.102 +};
   1.103 +
   1.104 +static int read_header(FILE *fp, struct chdr *hdr);
   1.105 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img);
   1.106 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd);
   1.107 +static int read_crng(FILE *fp, struct crng *crng);
   1.108 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
   1.109 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
   1.110 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width);
   1.111 +static int read16(FILE *fp, uint16_t *res);
   1.112 +static int read32(FILE *fp, uint32_t *res);
   1.113 +static uint16_t swap16(uint16_t x);
   1.114 +static uint32_t swap32(uint32_t x);
   1.115 +
   1.116 +int file_is_lbm(FILE *fp)
   1.117 +{
   1.118 +	uint32_t type;
   1.119 +	struct chdr hdr;
   1.120 +
   1.121 +	while(read_header(fp, &hdr) != -1) {
   1.122 +		if(IS_IFF_CONTAINER(hdr.id)) {
   1.123 +			if(read32(fp, &type) == -1) {
   1.124 +				break;
   1.125 +			}
   1.126 +
   1.127 +			if(type == IFF_ILBM || type == IFF_PBM ) {
   1.128 +				rewind(fp);
   1.129 +				return 1;
   1.130 +			}
   1.131 +			hdr.size -= sizeof type;	/* so we will seek fwd correctly */
   1.132 +		}
   1.133 +		fseek(fp, hdr.size, SEEK_CUR);
   1.134 +	}
   1.135 +	fseek(fp, 0, SEEK_SET);
   1.136 +	return 0;
   1.137 +}
   1.138 +
   1.139 +void print_chunkid(uint32_t id)
   1.140 +{
   1.141 +	char str[5] = {0};
   1.142 +#ifdef LENDIAN
   1.143 +	id = swap32(id);
   1.144 +#endif
   1.145 +	memcpy(str, &id, 4);
   1.146 +	puts(str);
   1.147 +}
   1.148 +
   1.149 +int load_image_lbm(struct image *img, FILE *fp)
   1.150 +{
   1.151 +	uint32_t type;
   1.152 +	struct chdr hdr;
   1.153 +
   1.154 +	while(read_header(fp, &hdr) != -1) {
   1.155 +		if(IS_IFF_CONTAINER(hdr.id)) {
   1.156 +			if(read32(fp, &type) == -1) {
   1.157 +				break;
   1.158 +			}
   1.159 +			hdr.size -= sizeof type;	/* to compensate for having advanced 4 more bytes */
   1.160 +
   1.161 +			if(type == IFF_ILBM) {
   1.162 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   1.163 +					return -1;
   1.164 +				}
   1.165 +				return 0;
   1.166 +			}
   1.167 +			if(type == IFF_PBM) {
   1.168 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   1.169 +					return -1;
   1.170 +				}
   1.171 +				return 0;
   1.172 +			}
   1.173 +		}
   1.174 +		fseek(fp, hdr.size, SEEK_CUR);
   1.175 +	}
   1.176 +	return 0;
   1.177 +}
   1.178 +
   1.179 +static int read_header(FILE *fp, struct chdr *hdr)
   1.180 +{
   1.181 +	if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) {
   1.182 +		return -1;
   1.183 +	}
   1.184 +#ifdef LENDIAN
   1.185 +	hdr->id = swap32(hdr->id);
   1.186 +	hdr->size = swap32(hdr->size);
   1.187 +#endif
   1.188 +	return 0;
   1.189 +}
   1.190 +
   1.191 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img)
   1.192 +{
   1.193 +	int i, res = -1;
   1.194 +	struct chdr hdr;
   1.195 +	struct bitmap_header bmhd;
   1.196 +	struct crng crng;
   1.197 +	struct colrange *crnode;
   1.198 +	unsigned char pal[3 * 256];
   1.199 +	unsigned char *pptr;
   1.200 +	long start = ftell(fp);
   1.201 +
   1.202 +	memset(img, 0, sizeof *img);
   1.203 +
   1.204 +	while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) {
   1.205 +		switch(hdr.id) {
   1.206 +		case IFF_BMHD:
   1.207 +			assert(hdr.size == 20);
   1.208 +			if(read_bmhd(fp, &bmhd) == -1) {
   1.209 +				return -1;
   1.210 +			}
   1.211 +			img->width = bmhd.width;
   1.212 +			img->height = bmhd.height;
   1.213 +			img->bpp = bmhd.nplanes;
   1.214 +			if(bmhd.nplanes > 8) {
   1.215 +				fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes);
   1.216 +				return -1;
   1.217 +			}
   1.218 +			if(!(img->pixels = malloc(img->width * img->height))) {
   1.219 +				fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height);
   1.220 +				return -1;
   1.221 +			}
   1.222 +			break;
   1.223 +
   1.224 +		case IFF_CMAP:
   1.225 +			assert(hdr.size / 3 <= 256);
   1.226 +
   1.227 +			if(fread(pal, 1, hdr.size, fp) < hdr.size) {
   1.228 +				fprintf(stderr, "failed to read colormap\n");
   1.229 +				return -1;
   1.230 +			}
   1.231 +			pptr = pal;
   1.232 +			for(i=0; i<256; i++) {
   1.233 +				img->palette[i].r = *pptr++;
   1.234 +				img->palette[i].g = *pptr++;
   1.235 +				img->palette[i].b = *pptr++;
   1.236 +			}
   1.237 +			break;
   1.238 +
   1.239 +		case IFF_CRNG:
   1.240 +			assert(hdr.size == sizeof crng);
   1.241 +
   1.242 +			if(read_crng(fp, &crng) == -1) {
   1.243 +				fprintf(stderr, "failed to read color cycling range chunk\n");
   1.244 +				return -1;
   1.245 +			}
   1.246 +			if(crng.low != crng.high && crng.rate > 0) {
   1.247 +				if(!(crnode = malloc(sizeof *crnode))) {
   1.248 +					fprintf(stderr, "failed to allocate color range node\n");
   1.249 +					return -1;
   1.250 +				}
   1.251 +				crnode->low = crng.low;
   1.252 +				crnode->high = crng.high;
   1.253 +				crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
   1.254 +				crnode->rate = crng.rate;
   1.255 +				crnode->next = img->range;
   1.256 +				img->range = crnode;
   1.257 +				++img->num_ranges;
   1.258 +			}
   1.259 +			break;
   1.260 +
   1.261 +		case IFF_BODY:
   1.262 +			if(!img->pixels) {
   1.263 +				fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n");
   1.264 +				return -1;
   1.265 +			}
   1.266 +			if(type == IFF_ILBM) {
   1.267 +				if(read_body_ilbm(fp, &bmhd, img) == -1) {
   1.268 +					fprintf(stderr, "failed to read interleaved pixel data\n");
   1.269 +					return -1;
   1.270 +				}
   1.271 +			} else {
   1.272 +				assert(type == IFF_PBM);
   1.273 +				if(read_body_pbm(fp, &bmhd, img) == -1) {
   1.274 +					fprintf(stderr, "failed to read linear pixel data\n");
   1.275 +					return -1;
   1.276 +				}
   1.277 +			}
   1.278 +			res = 0;	/* sucessfully read image */
   1.279 +			break;
   1.280 +
   1.281 +		default:
   1.282 +			/* skip unknown chunks */
   1.283 +			fseek(fp, hdr.size, SEEK_CUR);
   1.284 +			if(ftell(fp) & 1) {
   1.285 +				/* chunks must start at even offsets */
   1.286 +				fseek(fp, 1, SEEK_CUR);
   1.287 +			}
   1.288 +		}
   1.289 +	}
   1.290 +
   1.291 +	return res;
   1.292 +}
   1.293 +
   1.294 +
   1.295 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd)
   1.296 +{
   1.297 +	if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) {
   1.298 +		return -1;
   1.299 +	}
   1.300 +#ifdef LENDIAN
   1.301 +	bmhd->width = swap16(bmhd->width);
   1.302 +	bmhd->height = swap16(bmhd->height);
   1.303 +	bmhd->xoffs = swap16(bmhd->xoffs);
   1.304 +	bmhd->yoffs = swap16(bmhd->yoffs);
   1.305 +	bmhd->colorkey = swap16(bmhd->colorkey);
   1.306 +	bmhd->pgwidth = swap16(bmhd->pgwidth);
   1.307 +	bmhd->pgheight = swap16(bmhd->pgheight);
   1.308 +#endif
   1.309 +	return 0;
   1.310 +}
   1.311 +
   1.312 +static int read_crng(FILE *fp, struct crng *crng)
   1.313 +{
   1.314 +	if(fread(crng, sizeof *crng, 1, fp) < 1) {
   1.315 +		return -1;
   1.316 +	}
   1.317 +#ifdef LENDIAN
   1.318 +	crng->rate = swap16(crng->rate);
   1.319 +	crng->flags = swap16(crng->flags);
   1.320 +#endif
   1.321 +	return 0;
   1.322 +}
   1.323 +
   1.324 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
   1.325 + * each uncompressed row is width / 8 bytes
   1.326 + */
   1.327 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
   1.328 +{
   1.329 +	int i, j, k, bitidx;
   1.330 +	int rowsz = img->width / 8;
   1.331 +	unsigned char *src, *dest = img->pixels;
   1.332 +	unsigned char *rowbuf = alloca(rowsz);
   1.333 +
   1.334 +	assert(bmhd->width == img->width);
   1.335 +	assert(bmhd->height == img->height);
   1.336 +	assert(img->pixels);
   1.337 +
   1.338 +	for(i=0; i<img->height; i++) {
   1.339 +
   1.340 +		memset(dest, 0, img->width);	/* clear the whole scanline to OR bits into place */
   1.341 +
   1.342 +		for(j=0; j<bmhd->nplanes; j++) {
   1.343 +			// read a row corresponding to bitplane j
   1.344 +			if(bmhd->compression) {
   1.345 +				if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) {
   1.346 +					return -1;
   1.347 +				}
   1.348 +			} else {
   1.349 +				if(fread(rowbuf, 1, rowsz, fp) < rowsz) {
   1.350 +					return -1;
   1.351 +				}
   1.352 +			}
   1.353 +
   1.354 +			// distribute all bits across the linear output scanline
   1.355 +			src = rowbuf;
   1.356 +			bitidx = 0;
   1.357 +
   1.358 +			for(k=0; k<img->width; k++) {
   1.359 +				dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
   1.360 +
   1.361 +				if(++bitidx >= 8) {
   1.362 +					bitidx = 0;
   1.363 +					++src;
   1.364 +				}
   1.365 +			}
   1.366 +		}
   1.367 +
   1.368 +		if(bmhd->masking & MASK_PLANE) {
   1.369 +			/* skip the mask (1bpp) */
   1.370 +			fseek(fp, rowsz, SEEK_CUR);
   1.371 +		}
   1.372 +
   1.373 +		dest += img->width;
   1.374 +	}
   1.375 +	return 0;
   1.376 +}
   1.377 +
   1.378 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
   1.379 +{
   1.380 +	int i;
   1.381 +	int npixels = img->width * img->height;
   1.382 +	unsigned char *dptr = img->pixels;
   1.383 +
   1.384 +	assert(bmhd->width == img->width);
   1.385 +	assert(bmhd->height == img->height);
   1.386 +	assert(img->pixels);
   1.387 +
   1.388 +	if(bmhd->compression) {
   1.389 +		for(i=0; i<img->height; i++) {
   1.390 +			if(read_compressed_scanline(fp, dptr, img->width) == -1) {
   1.391 +				return -1;
   1.392 +			}
   1.393 +			dptr += img->width;
   1.394 +		}
   1.395 +
   1.396 +	} else {
   1.397 +		/* uncompressed */
   1.398 +		if(fread(img->pixels, 1, npixels, fp) < npixels) {
   1.399 +			return -1;
   1.400 +		}
   1.401 +	}
   1.402 +	return 0;
   1.403 +}
   1.404 +
   1.405 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width)
   1.406 +{
   1.407 +	int i, count, x = 0;
   1.408 +	signed char ctl;
   1.409 +
   1.410 +	while(x < width) {
   1.411 +		if(fread(&ctl, 1, 1, fp) < 1) return -1;
   1.412 +
   1.413 +		if(ctl == -128) continue;
   1.414 +
   1.415 +		if(ctl >= 0) {
   1.416 +			count = ctl + 1;
   1.417 +			if(fread(scanline, 1, count, fp) < count) return -1;
   1.418 +			scanline += count;
   1.419 +
   1.420 +		} else {
   1.421 +			unsigned char pixel;
   1.422 +			count = 1 - ctl;
   1.423 +			if(fread(&pixel, 1, 1, fp) < 1) return -1;
   1.424 +
   1.425 +			for(i=0; i<count; i++) {
   1.426 +				*scanline++ = pixel;
   1.427 +			}
   1.428 +		}
   1.429 +
   1.430 +		x += count;
   1.431 +	}
   1.432 +
   1.433 +	return 0;
   1.434 +}
   1.435 +
   1.436 +static int read16(FILE *fp, uint16_t *res)
   1.437 +{
   1.438 +	if(fread(res, sizeof *res, 1, fp) < 1) {
   1.439 +		return -1;
   1.440 +	}
   1.441 +#ifdef LENDIAN
   1.442 +	*res = swap16(*res);
   1.443 +#endif
   1.444 +	return 0;
   1.445 +}
   1.446 +
   1.447 +static int read32(FILE *fp, uint32_t *res)
   1.448 +{
   1.449 +	if(fread(res, sizeof *res, 1, fp) < 1) {
   1.450 +		return -1;
   1.451 +	}
   1.452 +#ifdef LENDIAN
   1.453 +	*res = swap32(*res);
   1.454 +#endif
   1.455 +	return 0;
   1.456 +}
   1.457 +
   1.458 +static uint16_t swap16(uint16_t x)
   1.459 +{
   1.460 +	return (x << 8) | (x >> 8);
   1.461 +}
   1.462 +
   1.463 +static uint32_t swap32(uint32_t x)
   1.464 +{
   1.465 +	return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
   1.466 +}