lbm2bin

changeset 0:bfd8aa2ca393

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 15 Jul 2017 13:26:32 +0300
parents
children 2b02b0f288b5
files Makefile src/image.c src/image.h src/imagelbm.c src/imagelbm.h src/main.c
diffstat 6 files changed, 1198 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Makefile	Sat Jul 15 13:26:32 2017 +0300
     1.3 @@ -0,0 +1,13 @@
     1.4 +src = $(wildcard src/*.c)
     1.5 +obj = $(src:.c=.o)
     1.6 +
     1.7 +bin = ilbm2bin
     1.8 +
     1.9 +LDFLAGS = -lm
    1.10 +
    1.11 +$(bin): $(obj)
    1.12 +	$(CC) -o $@ $(obj) $(LDFLAGS)
    1.13 +
    1.14 +.PHONY: clean
    1.15 +clean:
    1.16 +	rm -f $(obj) $(bin)
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/image.c	Sat Jul 15 13:26:32 2017 +0300
     2.3 @@ -0,0 +1,485 @@
     2.4 +/*
     2.5 +colcycle - color cycling image viewer
     2.6 +Copyright (C) 2016-2017  John Tsiombikas <nuclear@member.fsf.org>
     2.7 +
     2.8 +This program is free software: you can redistribute it and/or modify
     2.9 +it under the terms of the GNU General Public License as published by
    2.10 +the Free Software Foundation, either version 3 of the License, or
    2.11 +(at your option) any later version.
    2.12 +
    2.13 +This program is distributed in the hope that it will be useful,
    2.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.16 +GNU General Public License for more details.
    2.17 +
    2.18 +You should have received a copy of the GNU General Public License
    2.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    2.20 +*/
    2.21 +#include <stdio.h>
    2.22 +#include <stdlib.h>
    2.23 +#include <string.h>
    2.24 +#include <errno.h>
    2.25 +#include <ctype.h>
    2.26 +#include <math.h>
    2.27 +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(WIN32)
    2.28 +#include <malloc.h>
    2.29 +#else
    2.30 +#include <alloca.h>
    2.31 +#endif
    2.32 +#include "image.h"
    2.33 +#include "imagelbm.h"
    2.34 +
    2.35 +#ifndef M_PI
    2.36 +#define M_PI 3.141593
    2.37 +#endif
    2.38 +
    2.39 +static int flatten_crange_list(struct image *img);
    2.40 +
    2.41 +int gen_test_image(struct image *img)
    2.42 +{
    2.43 +	int i, j;
    2.44 +	unsigned char *pptr;
    2.45 +
    2.46 +	img->width = 640;
    2.47 +	img->height = 480;
    2.48 +	img->bpp = 8;
    2.49 +
    2.50 +	if(!(img->range = malloc(sizeof *img->range))) {
    2.51 +		return -1;
    2.52 +	}
    2.53 +	img->num_ranges = 1;
    2.54 +	img->range[0].low = 0;
    2.55 +	img->range[0].high = 255;
    2.56 +	img->range[0].cmode = CYCLE_NORMAL;
    2.57 +	img->range[0].rate = 5000;
    2.58 +
    2.59 +	if(!(img->pixels = malloc(img->width * img->height))) {
    2.60 +		free(img->range);
    2.61 +		return -1;
    2.62 +	}
    2.63 +
    2.64 +	for(i=0; i<256; i++) {
    2.65 +		float theta = M_PI * 2.0 * (float)i / 256.0;
    2.66 +		float r = cos(theta) * 0.5 + 0.5;
    2.67 +		float g = sin(theta) * 0.5 + 0.5;
    2.68 +		float b = -cos(theta) * 0.5 + 0.5;
    2.69 +		img->palette[i].r = (int)(r * 255.0);
    2.70 +		img->palette[i].g = (int)(g * 255.0);
    2.71 +		img->palette[i].b = (int)(b * 255.0);
    2.72 +	}
    2.73 +
    2.74 +	pptr = img->pixels;
    2.75 +	for(i=0; i<img->height; i++) {
    2.76 +		int c = (i << 8) / img->height;
    2.77 +		for(j=0; j<img->width; j++) {
    2.78 +			int chess = ((i >> 6) & 1) == ((j >> 6) & 1);
    2.79 +			*pptr++ = (chess ? c : c + 128) & 0xff;
    2.80 +		}
    2.81 +	}
    2.82 +	return 0;
    2.83 +}
    2.84 +
    2.85 +#define MAX_TOKEN_SIZE	256
    2.86 +static int image_block(FILE *fp, struct image *img);
    2.87 +static int nextc = -1;
    2.88 +static char token[MAX_TOKEN_SIZE];
    2.89 +
    2.90 +int load_image(struct image *img, const char *fname)
    2.91 +{
    2.92 +	FILE *fp;
    2.93 +	int c;
    2.94 +
    2.95 +	if(!(fp = fopen(fname, "rb"))) {
    2.96 +		fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno));
    2.97 +		return -1;
    2.98 +	}
    2.99 +
   2.100 +	if(file_is_lbm(fp)) {
   2.101 +		if(load_image_lbm(img, fp) == -1) {
   2.102 +			fclose(fp);
   2.103 +			return -1;
   2.104 +		}
   2.105 +		fclose(fp);
   2.106 +		flatten_crange_list(img);
   2.107 +		return 0;
   2.108 +	}
   2.109 +
   2.110 +	/* find the start of the root block */
   2.111 +	while((c = fgetc(fp)) != -1 && c != '{');
   2.112 +	if(feof(fp)) {
   2.113 +		fprintf(stderr, "invalid image format, no image block found: %s\n", fname);
   2.114 +		return -1;
   2.115 +	}
   2.116 +
   2.117 +	nextc = c;	/* prime the pump with the extra char we read above */
   2.118 +	if(image_block(fp, img) == -1) {
   2.119 +		fprintf(stderr, "failed to read image block from: %s\n", fname);
   2.120 +		fclose(fp);
   2.121 +		return -1;
   2.122 +	}
   2.123 +	fclose(fp);
   2.124 +
   2.125 +	flatten_crange_list(img);
   2.126 +	return 0;
   2.127 +}
   2.128 +
   2.129 +void destroy_image(struct image *img)
   2.130 +{
   2.131 +	if(img) {
   2.132 +		free(img->pixels);
   2.133 +		free(img->range);
   2.134 +		memset(img, 0, sizeof *img);
   2.135 +	}
   2.136 +}
   2.137 +
   2.138 +static int flatten_crange_list(struct image *img)
   2.139 +{
   2.140 +	struct colrange *list = img->range;
   2.141 +	struct colrange *rptr;
   2.142 +
   2.143 +	if(img->num_ranges <= 0) {
   2.144 +		return 0;
   2.145 +	}
   2.146 +
   2.147 +	if(!(img->range = malloc(img->num_ranges * sizeof *img->range))) {
   2.148 +		perror("flatten_crange_list: failed to allocate range array\n");
   2.149 +		return -1;
   2.150 +	}
   2.151 +
   2.152 +	rptr = img->range;
   2.153 +	while(list) {
   2.154 +		struct colrange *rng = list;
   2.155 +		list = list->next;
   2.156 +		*rptr++ = *rng;
   2.157 +		free(rng);
   2.158 +	}
   2.159 +	return 0;
   2.160 +}
   2.161 +
   2.162 +/* ---- parser ---- */
   2.163 +
   2.164 +enum {
   2.165 +	TOKEN_NUM,
   2.166 +	TOKEN_NAME,
   2.167 +	TOKEN_STR
   2.168 +};
   2.169 +
   2.170 +static int next_char(FILE *fp)
   2.171 +{
   2.172 +	while((nextc = fgetc(fp)) != -1 && isspace(nextc));
   2.173 +	return nextc;
   2.174 +}
   2.175 +
   2.176 +static int next_token(FILE *fp)
   2.177 +{
   2.178 +	char *ptr;
   2.179 +
   2.180 +	if(nextc == -1) {
   2.181 +		return -1;
   2.182 +	}
   2.183 +
   2.184 +	switch(nextc) {
   2.185 +	case '{':
   2.186 +	case '}':
   2.187 +	case ',':
   2.188 +	case '[':
   2.189 +	case ']':
   2.190 +	case ':':
   2.191 +		token[0] = nextc;
   2.192 +		token[1] = 0;
   2.193 +		nextc = next_char(fp);
   2.194 +		return token[0];
   2.195 +
   2.196 +	case '\'':
   2.197 +		ptr = token;
   2.198 +		nextc = next_char(fp);
   2.199 +		while(nextc != -1 && nextc != '\'') {
   2.200 +			*ptr++ = nextc;
   2.201 +			nextc = fgetc(fp);
   2.202 +		}
   2.203 +		nextc = next_char(fp);
   2.204 +		return TOKEN_STR;
   2.205 +
   2.206 +	default:
   2.207 +		break;
   2.208 +	}
   2.209 +
   2.210 +	if(isalpha(nextc)) {
   2.211 +		ptr = token;
   2.212 +		while(nextc != -1 && isalpha(nextc)) {
   2.213 +			*ptr++ = nextc;
   2.214 +			nextc = next_char(fp);
   2.215 +		}
   2.216 +		*ptr = 0;
   2.217 +		return TOKEN_NAME;
   2.218 +	}
   2.219 +	if(isdigit(nextc)) {
   2.220 +		ptr = token;
   2.221 +		while(nextc != -1 && isdigit(nextc)) {
   2.222 +			*ptr++ = nextc;
   2.223 +			nextc = next_char(fp);
   2.224 +		}
   2.225 +		*ptr = 0;
   2.226 +		return TOKEN_NUM;
   2.227 +	}
   2.228 +
   2.229 +	token[0] = nextc;
   2.230 +	token[1] = 0;
   2.231 +	fprintf(stderr, "next_token: unexpected character: %c\n", nextc);
   2.232 +	return -1;
   2.233 +}
   2.234 +
   2.235 +static int expect(FILE *fp, int tok)
   2.236 +{
   2.237 +	if(next_token(fp) != tok) {
   2.238 +		return 0;
   2.239 +	}
   2.240 +	return 1;
   2.241 +}
   2.242 +
   2.243 +static const char *toktypestr(int tok)
   2.244 +{
   2.245 +	static char buf[] = "' '";
   2.246 +	switch(tok) {
   2.247 +	case TOKEN_NUM:
   2.248 +		return "number";
   2.249 +	case TOKEN_NAME:
   2.250 +		return "name";
   2.251 +	case TOKEN_STR:
   2.252 +		return "string";
   2.253 +	default:
   2.254 +		break;
   2.255 +	}
   2.256 +	buf[1] = tok;
   2.257 +	return buf;
   2.258 +}
   2.259 +
   2.260 +#define EXPECT(fp, x) \
   2.261 +	do { \
   2.262 +		if(!expect(fp, x)) { \
   2.263 +			fprintf(stderr, "%s: expected: %s, found: %s\n", __FUNCTION__, toktypestr(x), token); \
   2.264 +			return -1; \
   2.265 +		} \
   2.266 +	} while(0)
   2.267 +
   2.268 +static int palette(FILE *fp, struct image *img)
   2.269 +{
   2.270 +	int tok, cidx = 0;
   2.271 +	struct color col, *cptr;
   2.272 +
   2.273 +	EXPECT(fp, '[');
   2.274 +
   2.275 +	while((tok = next_token(fp)) == '[') {
   2.276 +		cptr = cidx < 256 ? &img->palette[cidx] : &col;
   2.277 +		++cidx;
   2.278 +
   2.279 +		EXPECT(fp, TOKEN_NUM);
   2.280 +		cptr->r = atoi(token);
   2.281 +		EXPECT(fp, ',');
   2.282 +		EXPECT(fp, TOKEN_NUM);
   2.283 +		cptr->g = atoi(token);
   2.284 +		EXPECT(fp, ',');
   2.285 +		EXPECT(fp, TOKEN_NUM);
   2.286 +		cptr->b = atoi(token);
   2.287 +		EXPECT(fp, ']');
   2.288 +
   2.289 +		if(nextc == ',') {
   2.290 +			next_token(fp);	/* skip comma */
   2.291 +		}
   2.292 +	}
   2.293 +
   2.294 +	if(tok != ']') {
   2.295 +		fprintf(stderr, "palette must be closed by a ']' token\n");
   2.296 +		return -1;
   2.297 +	}
   2.298 +	return 0;
   2.299 +}
   2.300 +
   2.301 +static int crange(FILE *fp, struct colrange *rng)
   2.302 +{
   2.303 +	int val;
   2.304 +	char name[MAX_TOKEN_SIZE];
   2.305 +
   2.306 +	EXPECT(fp, '{');
   2.307 +
   2.308 +	while(nextc != -1 && nextc != '}') {
   2.309 +		EXPECT(fp, TOKEN_NAME);
   2.310 +		strcpy(name, token);
   2.311 +		EXPECT(fp, ':');
   2.312 +		EXPECT(fp, TOKEN_NUM);
   2.313 +		val = atoi(token);
   2.314 +
   2.315 +		if(strcmp(name, "reverse") == 0) {
   2.316 +			rng->cmode = val;
   2.317 +		} else if(strcmp(name, "rate") == 0) {
   2.318 +			rng->rate = val;
   2.319 +		} else if(strcmp(name, "low") == 0) {
   2.320 +			rng->low = val;
   2.321 +		} else if(strcmp(name, "high") == 0) {
   2.322 +			rng->high = val;
   2.323 +		} else {
   2.324 +			fprintf(stderr, "invalid attribute %s in cycles range\n", name);
   2.325 +			return -1;
   2.326 +		}
   2.327 +
   2.328 +		if(nextc == ',') {
   2.329 +			next_token(fp);
   2.330 +		}
   2.331 +	}
   2.332 +
   2.333 +	EXPECT(fp, '}');
   2.334 +	return 0;
   2.335 +}
   2.336 +
   2.337 +static int cycles(FILE *fp, struct image *img)
   2.338 +{
   2.339 +	struct colrange *list = 0, *rng;
   2.340 +
   2.341 +	EXPECT(fp, '[');
   2.342 +
   2.343 +	img->num_ranges = 0;
   2.344 +	while(nextc == '{') {
   2.345 +		if(!(rng = malloc(sizeof *rng))) {
   2.346 +			perror("failed to allocate color range");
   2.347 +			goto err;
   2.348 +		}
   2.349 +		if(crange(fp, rng) == -1) {
   2.350 +			free(rng);
   2.351 +			goto err;
   2.352 +		}
   2.353 +		if(rng->low != rng->high && rng->rate > 0) {
   2.354 +			rng->next = list;
   2.355 +			list = rng;
   2.356 +			++img->num_ranges;
   2.357 +		} else {
   2.358 +			free(rng);
   2.359 +		}
   2.360 +
   2.361 +		if(nextc == ',') {
   2.362 +			next_token(fp);	/* eat the comma */
   2.363 +		}
   2.364 +	}
   2.365 +
   2.366 +	img->range = list;
   2.367 +
   2.368 +	if(!expect(fp, ']')) {
   2.369 +		fprintf(stderr, "cycles: missing closing bracket\n");
   2.370 +		goto err;
   2.371 +	}
   2.372 +	return 0;
   2.373 +
   2.374 +err:
   2.375 +	while(list) {
   2.376 +		rng = list;
   2.377 +		list = list->next;
   2.378 +		free(rng);
   2.379 +	}
   2.380 +	img->num_ranges = 0;
   2.381 +	return -1;
   2.382 +}
   2.383 +
   2.384 +static int pixels(FILE *fp, struct image *img)
   2.385 +{
   2.386 +	int tok, num_pixels;
   2.387 +	unsigned char *pptr;
   2.388 +
   2.389 +	if(img->width <= 0 || img->height <= 0) {
   2.390 +		fprintf(stderr, "pixel block found before defining the image dimensions!\n");
   2.391 +		return -1;
   2.392 +	}
   2.393 +	num_pixels = img->width * img->height;
   2.394 +	if(!(img->pixels = malloc(num_pixels))) {
   2.395 +		perror("failed to allocate pixels");
   2.396 +		return -1;
   2.397 +	}
   2.398 +	pptr = img->pixels;
   2.399 +
   2.400 +	EXPECT(fp, '[');
   2.401 +
   2.402 +	while((tok = next_token(fp)) == TOKEN_NUM) {
   2.403 +		if(pptr - img->pixels >= num_pixels) {
   2.404 +			pptr = 0;
   2.405 +			fprintf(stderr, "more pixel data provided than the specified image dimensions\n");
   2.406 +		}
   2.407 +		if(!pptr) continue;
   2.408 +		*pptr++ = atoi(token);
   2.409 +
   2.410 +		if(nextc == ',') {
   2.411 +			next_token(fp);	/* eat comma */
   2.412 +		}
   2.413 +	}
   2.414 +
   2.415 +	if(tok != ']') {
   2.416 +		printf("pixels: missing closing bracket\n");
   2.417 +		return -1;
   2.418 +	}
   2.419 +	return 0;
   2.420 +}
   2.421 +
   2.422 +static int attribute(FILE *fp, struct image *img)
   2.423 +{
   2.424 +	char *attr_name;
   2.425 +
   2.426 +	if(!expect(fp, TOKEN_NAME)) {
   2.427 +		return -1;
   2.428 +	}
   2.429 +	attr_name = alloca(strlen(token) + 1);
   2.430 +	strcpy(attr_name, token);
   2.431 +
   2.432 +	if(!expect(fp, ':')) {
   2.433 +		return -1;
   2.434 +	}
   2.435 +
   2.436 +	if(strcmp(attr_name, "filename") == 0) {
   2.437 +		if(!expect(fp, TOKEN_STR)) {
   2.438 +			fprintf(stderr, "attribute: filename should be a string\n");
   2.439 +			return -1;
   2.440 +		}
   2.441 +
   2.442 +	} else if(strcmp(attr_name, "width") == 0 || strcmp(attr_name, "height") == 0) {
   2.443 +		int *dst = attr_name[0] == 'w' ? &img->width : &img->height;
   2.444 +		if(!expect(fp, TOKEN_NUM)) {
   2.445 +			fprintf(stderr, "attribute: %s should be a number\n", attr_name);
   2.446 +			return -1;
   2.447 +		}
   2.448 +		*dst = atoi(token);
   2.449 +
   2.450 +	} else if(strcmp(attr_name, "colors") == 0) {
   2.451 +		if(palette(fp, img) == -1) {
   2.452 +			fprintf(stderr, "attribute: colors should be a palette\n");
   2.453 +			return -1;
   2.454 +		}
   2.455 +
   2.456 +	} else if(strcmp(attr_name, "cycles") == 0) {
   2.457 +		if(cycles(fp, img) == -1) {
   2.458 +			fprintf(stderr, "attribute: cycles should be a list of palranges\n");
   2.459 +			return -1;
   2.460 +		}
   2.461 +
   2.462 +	} else if(strcmp(attr_name, "pixels") == 0) {
   2.463 +		if(pixels(fp, img) == -1) {
   2.464 +			fprintf(stderr, "attribute: pixels should be a list of indices\n");
   2.465 +			return -1;
   2.466 +		}
   2.467 +
   2.468 +	} else {
   2.469 +		fprintf(stderr, "unknown attribute: %s\n", attr_name);
   2.470 +		return -1;
   2.471 +	}
   2.472 +	return 0;
   2.473 +}
   2.474 +
   2.475 +static int image_block(FILE *fp, struct image *img)
   2.476 +{
   2.477 +	EXPECT(fp, '{');
   2.478 +
   2.479 +	img->width = img->height = -1;
   2.480 +	img->bpp = 8;
   2.481 +
   2.482 +	while(attribute(fp, img) != -1) {
   2.483 +		if(nextc == ',') {
   2.484 +			next_token(fp);	/* eat comma */
   2.485 +		}
   2.486 +	}
   2.487 +	return 0;
   2.488 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/image.h	Sat Jul 15 13:26:32 2017 +0300
     3.3 @@ -0,0 +1,54 @@
     3.4 +/*
     3.5 +colcycle - color cycling image viewer
     3.6 +Copyright (C) 2016  John Tsiombikas <nuclear@member.fsf.org>
     3.7 +
     3.8 +This program is free software: you can redistribute it and/or modify
     3.9 +it under the terms of the GNU General Public License as published by
    3.10 +the Free Software Foundation, either version 3 of the License, or
    3.11 +(at your option) any later version.
    3.12 +
    3.13 +This program is distributed in the hope that it will be useful,
    3.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.16 +GNU General Public License for more details.
    3.17 +
    3.18 +You should have received a copy of the GNU General Public License
    3.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    3.20 +*/
    3.21 +#ifndef IMAGE_H_
    3.22 +#define IMAGE_H_
    3.23 +
    3.24 +enum cycle_mode {
    3.25 +	CYCLE_NORMAL = 0,
    3.26 +	CYCLE_UNUSED,	/* ? */
    3.27 +	CYCLE_REVERSE = 2,
    3.28 +	CYCLE_PINGPONG = 3,
    3.29 +	CYCLE_SINE_HALF = 4,	/* sine -> [0, range/2] */
    3.30 +	CYCLE_SINE = 5			/* sine -> [0, range] */
    3.31 +};
    3.32 +
    3.33 +struct color {
    3.34 +	unsigned char r, g, b;
    3.35 +};
    3.36 +
    3.37 +struct colrange {
    3.38 +	int low, high;
    3.39 +	int cmode;
    3.40 +	unsigned int rate;
    3.41 +	struct colrange *next;
    3.42 +};
    3.43 +
    3.44 +struct image {
    3.45 +	int width, height;
    3.46 +	int bpp;
    3.47 +	struct color palette[256];
    3.48 +	struct colrange *range;
    3.49 +	int num_ranges;
    3.50 +	unsigned char *pixels;
    3.51 +};
    3.52 +
    3.53 +int gen_test_image(struct image *img);
    3.54 +int load_image(struct image *img, const char *fname);
    3.55 +void destroy_image(struct image *img);
    3.56 +
    3.57 +#endif	/* IMAGE_H_ */
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/imagelbm.c	Sat Jul 15 13:26:32 2017 +0300
     4.3 @@ -0,0 +1,463 @@
     4.4 +/*
     4.5 +colcycle - color cycling image viewer
     4.6 +Copyright (C) 2016-2017  John Tsiombikas <nuclear@member.fsf.org>
     4.7 +
     4.8 +This program is free software: you can redistribute it and/or modify
     4.9 +it under the terms of the GNU General Public License as published by
    4.10 +the Free Software Foundation, either version 3 of the License, or
    4.11 +(at your option) any later version.
    4.12 +
    4.13 +This program is distributed in the hope that it will be useful,
    4.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.16 +GNU General Public License for more details.
    4.17 +
    4.18 +You should have received a copy of the GNU General Public License
    4.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    4.20 +*/
    4.21 +#include <stdio.h>
    4.22 +#include <stdlib.h>
    4.23 +#include <assert.h>
    4.24 +#include <inttypes.h>
    4.25 +#include <string.h>
    4.26 +#if defined(__WATCOMC__) || defined(_MSC_VER)
    4.27 +#include <malloc.h>
    4.28 +#else
    4.29 +#include <alloca.h>
    4.30 +#endif
    4.31 +#include "imagelbm.h"
    4.32 +
    4.33 +#ifdef __GNUC__
    4.34 +#define PACKED	__attribute__((packed))
    4.35 +#endif
    4.36 +
    4.37 +#ifdef __BYTE_ORDER__
    4.38 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    4.39 +#define LENDIAN
    4.40 +#else
    4.41 +#define BENDIAN
    4.42 +#endif
    4.43 +#endif
    4.44 +
    4.45 +#define MKID(a, b, c, d)	(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
    4.46 +
    4.47 +#define IS_IFF_CONTAINER(id)	((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST)
    4.48 +
    4.49 +enum {
    4.50 +	IFF_FORM = MKID('F', 'O', 'R', 'M'),
    4.51 +	IFF_CAT = MKID('C', 'A', 'T', ' '),
    4.52 +	IFF_LIST = MKID('L', 'I', 'S', 'T'),
    4.53 +	IFF_ILBM = MKID('I', 'L', 'B', 'M'),
    4.54 +	IFF_PBM = MKID('P', 'B', 'M', ' '),
    4.55 +	IFF_BMHD = MKID('B', 'M', 'H', 'D'),
    4.56 +	IFF_CMAP = MKID('C', 'M', 'A', 'P'),
    4.57 +	IFF_BODY = MKID('B', 'O', 'D', 'Y'),
    4.58 +	IFF_CRNG = MKID('C', 'R', 'N', 'G')
    4.59 +};
    4.60 +
    4.61 +
    4.62 +struct chdr {
    4.63 +	uint32_t id;
    4.64 +	uint32_t size;
    4.65 +};
    4.66 +
    4.67 +#ifdef __WATCOMC__
    4.68 +#pragma push(pack, 1)
    4.69 +#endif
    4.70 +struct bitmap_header {
    4.71 +	uint16_t width, height;
    4.72 +	int16_t xoffs, yoffs;
    4.73 +	uint8_t nplanes;
    4.74 +	uint8_t masking;
    4.75 +	uint8_t compression;
    4.76 +	uint8_t padding;
    4.77 +	uint16_t colorkey;
    4.78 +	uint8_t aspect_num, aspect_denom;
    4.79 +	int16_t pgwidth, pgheight;
    4.80 +} PACKED;
    4.81 +#ifdef __WATCOMC__
    4.82 +#pragma pop(pack)
    4.83 +#endif
    4.84 +
    4.85 +enum {
    4.86 +	MASK_NONE,
    4.87 +	MASK_PLANE,
    4.88 +	MASK_COLORKEY,
    4.89 +	MASK_LASSO
    4.90 +};
    4.91 +
    4.92 +struct crng {
    4.93 +	uint16_t padding;
    4.94 +	uint16_t rate;
    4.95 +	uint16_t flags;
    4.96 +	uint8_t low, high;
    4.97 +};
    4.98 +
    4.99 +enum {
   4.100 +	CRNG_ENABLE = 1,
   4.101 +	CRNG_REVERSE = 2
   4.102 +};
   4.103 +
   4.104 +static int read_header(FILE *fp, struct chdr *hdr);
   4.105 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img);
   4.106 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd);
   4.107 +static int read_crng(FILE *fp, struct crng *crng);
   4.108 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
   4.109 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img);
   4.110 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width);
   4.111 +static int read16(FILE *fp, uint16_t *res);
   4.112 +static int read32(FILE *fp, uint32_t *res);
   4.113 +static uint16_t swap16(uint16_t x);
   4.114 +static uint32_t swap32(uint32_t x);
   4.115 +
   4.116 +int file_is_lbm(FILE *fp)
   4.117 +{
   4.118 +	uint32_t type;
   4.119 +	struct chdr hdr;
   4.120 +
   4.121 +	while(read_header(fp, &hdr) != -1) {
   4.122 +		if(IS_IFF_CONTAINER(hdr.id)) {
   4.123 +			if(read32(fp, &type) == -1) {
   4.124 +				break;
   4.125 +			}
   4.126 +
   4.127 +			if(type == IFF_ILBM || type == IFF_PBM ) {
   4.128 +				rewind(fp);
   4.129 +				return 1;
   4.130 +			}
   4.131 +			hdr.size -= sizeof type;	/* so we will seek fwd correctly */
   4.132 +		}
   4.133 +		fseek(fp, hdr.size, SEEK_CUR);
   4.134 +	}
   4.135 +	fseek(fp, 0, SEEK_SET);
   4.136 +	return 0;
   4.137 +}
   4.138 +
   4.139 +void print_chunkid(uint32_t id)
   4.140 +{
   4.141 +	char str[5] = {0};
   4.142 +#ifdef LENDIAN
   4.143 +	id = swap32(id);
   4.144 +#endif
   4.145 +	memcpy(str, &id, 4);
   4.146 +	puts(str);
   4.147 +}
   4.148 +
   4.149 +int load_image_lbm(struct image *img, FILE *fp)
   4.150 +{
   4.151 +	uint32_t type;
   4.152 +	struct chdr hdr;
   4.153 +
   4.154 +	while(read_header(fp, &hdr) != -1) {
   4.155 +		if(IS_IFF_CONTAINER(hdr.id)) {
   4.156 +			if(read32(fp, &type) == -1) {
   4.157 +				break;
   4.158 +			}
   4.159 +			hdr.size -= sizeof type;	/* to compensate for having advanced 4 more bytes */
   4.160 +
   4.161 +			if(type == IFF_ILBM) {
   4.162 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   4.163 +					return -1;
   4.164 +				}
   4.165 +				return 0;
   4.166 +			}
   4.167 +			if(type == IFF_PBM) {
   4.168 +				if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) {
   4.169 +					return -1;
   4.170 +				}
   4.171 +				return 0;
   4.172 +			}
   4.173 +		}
   4.174 +		fseek(fp, hdr.size, SEEK_CUR);
   4.175 +	}
   4.176 +	return 0;
   4.177 +}
   4.178 +
   4.179 +static int read_header(FILE *fp, struct chdr *hdr)
   4.180 +{
   4.181 +	if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) {
   4.182 +		return -1;
   4.183 +	}
   4.184 +#ifdef LENDIAN
   4.185 +	hdr->id = swap32(hdr->id);
   4.186 +	hdr->size = swap32(hdr->size);
   4.187 +#endif
   4.188 +	return 0;
   4.189 +}
   4.190 +
   4.191 +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img)
   4.192 +{
   4.193 +	int i, res = -1;
   4.194 +	struct chdr hdr;
   4.195 +	struct bitmap_header bmhd;
   4.196 +	struct crng crng;
   4.197 +	struct colrange *crnode;
   4.198 +	unsigned char pal[3 * 256];
   4.199 +	unsigned char *pptr;
   4.200 +	long start = ftell(fp);
   4.201 +
   4.202 +	memset(img, 0, sizeof *img);
   4.203 +
   4.204 +	while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) {
   4.205 +		switch(hdr.id) {
   4.206 +		case IFF_BMHD:
   4.207 +			assert(hdr.size == 20);
   4.208 +			if(read_bmhd(fp, &bmhd) == -1) {
   4.209 +				return -1;
   4.210 +			}
   4.211 +			img->width = bmhd.width;
   4.212 +			img->height = bmhd.height;
   4.213 +			img->bpp = bmhd.nplanes;
   4.214 +			if(bmhd.nplanes > 8) {
   4.215 +				fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes);
   4.216 +				return -1;
   4.217 +			}
   4.218 +			if(!(img->pixels = malloc(img->width * img->height))) {
   4.219 +				fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height);
   4.220 +				return -1;
   4.221 +			}
   4.222 +			break;
   4.223 +
   4.224 +		case IFF_CMAP:
   4.225 +			assert(hdr.size / 3 <= 256);
   4.226 +
   4.227 +			if(fread(pal, 1, hdr.size, fp) < hdr.size) {
   4.228 +				fprintf(stderr, "failed to read colormap\n");
   4.229 +				return -1;
   4.230 +			}
   4.231 +			pptr = pal;
   4.232 +			for(i=0; i<256; i++) {
   4.233 +				img->palette[i].r = *pptr++;
   4.234 +				img->palette[i].g = *pptr++;
   4.235 +				img->palette[i].b = *pptr++;
   4.236 +			}
   4.237 +			break;
   4.238 +
   4.239 +		case IFF_CRNG:
   4.240 +			assert(hdr.size == sizeof crng);
   4.241 +
   4.242 +			if(read_crng(fp, &crng) == -1) {
   4.243 +				fprintf(stderr, "failed to read color cycling range chunk\n");
   4.244 +				return -1;
   4.245 +			}
   4.246 +			if(crng.low != crng.high && crng.rate > 0) {
   4.247 +				if(!(crnode = malloc(sizeof *crnode))) {
   4.248 +					fprintf(stderr, "failed to allocate color range node\n");
   4.249 +					return -1;
   4.250 +				}
   4.251 +				crnode->low = crng.low;
   4.252 +				crnode->high = crng.high;
   4.253 +				crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL;
   4.254 +				crnode->rate = crng.rate;
   4.255 +				crnode->next = img->range;
   4.256 +				img->range = crnode;
   4.257 +				++img->num_ranges;
   4.258 +			}
   4.259 +			break;
   4.260 +
   4.261 +		case IFF_BODY:
   4.262 +			if(!img->pixels) {
   4.263 +				fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n");
   4.264 +				return -1;
   4.265 +			}
   4.266 +			if(type == IFF_ILBM) {
   4.267 +				if(read_body_ilbm(fp, &bmhd, img) == -1) {
   4.268 +					fprintf(stderr, "failed to read interleaved pixel data\n");
   4.269 +					return -1;
   4.270 +				}
   4.271 +			} else {
   4.272 +				assert(type == IFF_PBM);
   4.273 +				if(read_body_pbm(fp, &bmhd, img) == -1) {
   4.274 +					fprintf(stderr, "failed to read linear pixel data\n");
   4.275 +					return -1;
   4.276 +				}
   4.277 +			}
   4.278 +			res = 0;	/* sucessfully read image */
   4.279 +			break;
   4.280 +
   4.281 +		default:
   4.282 +			/* skip unknown chunks */
   4.283 +			fseek(fp, hdr.size, SEEK_CUR);
   4.284 +			if(ftell(fp) & 1) {
   4.285 +				/* chunks must start at even offsets */
   4.286 +				fseek(fp, 1, SEEK_CUR);
   4.287 +			}
   4.288 +		}
   4.289 +	}
   4.290 +
   4.291 +	return res;
   4.292 +}
   4.293 +
   4.294 +
   4.295 +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd)
   4.296 +{
   4.297 +	if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) {
   4.298 +		return -1;
   4.299 +	}
   4.300 +#ifdef LENDIAN
   4.301 +	bmhd->width = swap16(bmhd->width);
   4.302 +	bmhd->height = swap16(bmhd->height);
   4.303 +	bmhd->xoffs = swap16(bmhd->xoffs);
   4.304 +	bmhd->yoffs = swap16(bmhd->yoffs);
   4.305 +	bmhd->colorkey = swap16(bmhd->colorkey);
   4.306 +	bmhd->pgwidth = swap16(bmhd->pgwidth);
   4.307 +	bmhd->pgheight = swap16(bmhd->pgheight);
   4.308 +#endif
   4.309 +	return 0;
   4.310 +}
   4.311 +
   4.312 +static int read_crng(FILE *fp, struct crng *crng)
   4.313 +{
   4.314 +	if(fread(crng, sizeof *crng, 1, fp) < 1) {
   4.315 +		return -1;
   4.316 +	}
   4.317 +#ifdef LENDIAN
   4.318 +	crng->rate = swap16(crng->rate);
   4.319 +	crng->flags = swap16(crng->flags);
   4.320 +#endif
   4.321 +	return 0;
   4.322 +}
   4.323 +
   4.324 +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row]
   4.325 + * each uncompressed row is width / 8 bytes
   4.326 + */
   4.327 +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
   4.328 +{
   4.329 +	int i, j, k, bitidx;
   4.330 +	int rowsz = img->width / 8;
   4.331 +	unsigned char *src, *dest = img->pixels;
   4.332 +	unsigned char *rowbuf = alloca(rowsz);
   4.333 +
   4.334 +	assert(bmhd->width == img->width);
   4.335 +	assert(bmhd->height == img->height);
   4.336 +	assert(img->pixels);
   4.337 +
   4.338 +	for(i=0; i<img->height; i++) {
   4.339 +
   4.340 +		memset(dest, 0, img->width);	/* clear the whole scanline to OR bits into place */
   4.341 +
   4.342 +		for(j=0; j<bmhd->nplanes; j++) {
   4.343 +			// read a row corresponding to bitplane j
   4.344 +			if(bmhd->compression) {
   4.345 +				if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) {
   4.346 +					return -1;
   4.347 +				}
   4.348 +			} else {
   4.349 +				if(fread(rowbuf, 1, rowsz, fp) < rowsz) {
   4.350 +					return -1;
   4.351 +				}
   4.352 +			}
   4.353 +
   4.354 +			// distribute all bits across the linear output scanline
   4.355 +			src = rowbuf;
   4.356 +			bitidx = 0;
   4.357 +
   4.358 +			for(k=0; k<img->width; k++) {
   4.359 +				dest[k] |= ((*src >> (7 - bitidx)) & 1) << j;
   4.360 +
   4.361 +				if(++bitidx >= 8) {
   4.362 +					bitidx = 0;
   4.363 +					++src;
   4.364 +				}
   4.365 +			}
   4.366 +		}
   4.367 +
   4.368 +		if(bmhd->masking & MASK_PLANE) {
   4.369 +			/* skip the mask (1bpp) */
   4.370 +			fseek(fp, rowsz, SEEK_CUR);
   4.371 +		}
   4.372 +
   4.373 +		dest += img->width;
   4.374 +	}
   4.375 +	return 0;
   4.376 +}
   4.377 +
   4.378 +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img)
   4.379 +{
   4.380 +	int i;
   4.381 +	int npixels = img->width * img->height;
   4.382 +	unsigned char *dptr = img->pixels;
   4.383 +
   4.384 +	assert(bmhd->width == img->width);
   4.385 +	assert(bmhd->height == img->height);
   4.386 +	assert(img->pixels);
   4.387 +
   4.388 +	if(bmhd->compression) {
   4.389 +		for(i=0; i<img->height; i++) {
   4.390 +			if(read_compressed_scanline(fp, dptr, img->width) == -1) {
   4.391 +				return -1;
   4.392 +			}
   4.393 +			dptr += img->width;
   4.394 +		}
   4.395 +
   4.396 +	} else {
   4.397 +		/* uncompressed */
   4.398 +		if(fread(img->pixels, 1, npixels, fp) < npixels) {
   4.399 +			return -1;
   4.400 +		}
   4.401 +	}
   4.402 +	return 0;
   4.403 +}
   4.404 +
   4.405 +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width)
   4.406 +{
   4.407 +	int i, count, x = 0;
   4.408 +	signed char ctl;
   4.409 +
   4.410 +	while(x < width) {
   4.411 +		if(fread(&ctl, 1, 1, fp) < 1) return -1;
   4.412 +
   4.413 +		if(ctl == -128) continue;
   4.414 +
   4.415 +		if(ctl >= 0) {
   4.416 +			count = ctl + 1;
   4.417 +			if(fread(scanline, 1, count, fp) < count) return -1;
   4.418 +			scanline += count;
   4.419 +
   4.420 +		} else {
   4.421 +			unsigned char pixel;
   4.422 +			count = 1 - ctl;
   4.423 +			if(fread(&pixel, 1, 1, fp) < 1) return -1;
   4.424 +
   4.425 +			for(i=0; i<count; i++) {
   4.426 +				*scanline++ = pixel;
   4.427 +			}
   4.428 +		}
   4.429 +
   4.430 +		x += count;
   4.431 +	}
   4.432 +
   4.433 +	return 0;
   4.434 +}
   4.435 +
   4.436 +static int read16(FILE *fp, uint16_t *res)
   4.437 +{
   4.438 +	if(fread(res, sizeof *res, 1, fp) < 1) {
   4.439 +		return -1;
   4.440 +	}
   4.441 +#ifdef LENDIAN
   4.442 +	*res = swap16(*res);
   4.443 +#endif
   4.444 +	return 0;
   4.445 +}
   4.446 +
   4.447 +static int read32(FILE *fp, uint32_t *res)
   4.448 +{
   4.449 +	if(fread(res, sizeof *res, 1, fp) < 1) {
   4.450 +		return -1;
   4.451 +	}
   4.452 +#ifdef LENDIAN
   4.453 +	*res = swap32(*res);
   4.454 +#endif
   4.455 +	return 0;
   4.456 +}
   4.457 +
   4.458 +static uint16_t swap16(uint16_t x)
   4.459 +{
   4.460 +	return (x << 8) | (x >> 8);
   4.461 +}
   4.462 +
   4.463 +static uint32_t swap32(uint32_t x)
   4.464 +{
   4.465 +	return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
   4.466 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/imagelbm.h	Sat Jul 15 13:26:32 2017 +0300
     5.3 @@ -0,0 +1,27 @@
     5.4 +/*
     5.5 +colcycle - color cycling image viewer
     5.6 +Copyright (C) 2016-2017  John Tsiombikas <nuclear@member.fsf.org>
     5.7 +
     5.8 +This program is free software: you can redistribute it and/or modify
     5.9 +it under the terms of the GNU General Public License as published by
    5.10 +the Free Software Foundation, either version 3 of the License, or
    5.11 +(at your option) any later version.
    5.12 +
    5.13 +This program is distributed in the hope that it will be useful,
    5.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.16 +GNU General Public License for more details.
    5.17 +
    5.18 +You should have received a copy of the GNU General Public License
    5.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
    5.20 +*/
    5.21 +#ifndef IMAGE_LBM_H_
    5.22 +#define IMAGE_LBM_H_
    5.23 +
    5.24 +#include <stdio.h>
    5.25 +#include "image.h"
    5.26 +
    5.27 +int file_is_lbm(FILE *fp);
    5.28 +int load_image_lbm(struct image *img, FILE *fp);
    5.29 +
    5.30 +#endif	/* IMAGE_LBM_H_ */
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/main.c	Sat Jul 15 13:26:32 2017 +0300
     6.3 @@ -0,0 +1,156 @@
     6.4 +#include <stdio.h>
     6.5 +#include <stdlib.h>
     6.6 +#include <string.h>
     6.7 +#include <errno.h>
     6.8 +#include "image.h"
     6.9 +
    6.10 +static int proc_image(const char *fname);
    6.11 +static const char *modestr(int cmode);
    6.12 +static void output_planar(FILE *fp, struct image *img);
    6.13 +static void output_chunky(FILE *fp, struct image *img);
    6.14 +
    6.15 +static int planar = 0;
    6.16 +
    6.17 +int main(int argc, char **argv)
    6.18 +{
    6.19 +	int i;
    6.20 +
    6.21 +	for(i=1; i<argc; i++) {
    6.22 +		if(argv[i][0] == '-') {
    6.23 +			if(argv[i][2] == 0) {
    6.24 +				switch(argv[i][1]) {
    6.25 +				case 'p':
    6.26 +					planar = 1;
    6.27 +					break;
    6.28 +
    6.29 +				case 'c':
    6.30 +					planar = 0;
    6.31 +					break;
    6.32 +
    6.33 +				case 'h':
    6.34 +					printf("usage: %s [options] <file 1> <file 2> ... <file n>\n", argv[0]);
    6.35 +					printf("Options:\n");
    6.36 +					printf(" -p: output image in planar format (default)\n");
    6.37 +					printf(" -c: output image in chunky format\n");
    6.38 +					printf(" -h: print usage and exit\n");
    6.39 +					return 0;
    6.40 +
    6.41 +				default:
    6.42 +					fprintf(stderr, "invalid option: %s\n", argv[i]);
    6.43 +					return 1;
    6.44 +				}
    6.45 +			} else {
    6.46 +				fprintf(stderr, "invalid option: %s\n", argv[i]);
    6.47 +			}
    6.48 +		} else {
    6.49 +			if(proc_image(argv[i]) == -1) {
    6.50 +				return 1;
    6.51 +			}
    6.52 +		}
    6.53 +	}
    6.54 +	return 0;
    6.55 +}
    6.56 +
    6.57 +static int proc_image(const char *fname)
    6.58 +{
    6.59 +	int i;
    6.60 +	struct image img;
    6.61 +	char *outfname, *suffix;
    6.62 +	FILE *fp;
    6.63 +
    6.64 +	if(load_image(&img, fname) == -1) {
    6.65 +		fprintf(stderr, "failed to load image: %s\n", fname);
    6.66 +		return -1;
    6.67 +	}
    6.68 +
    6.69 +	if(!(outfname = malloc(strlen(fname) + 4))) {
    6.70 +		perror("failed to allocate output filename");
    6.71 +		return -1;
    6.72 +	}
    6.73 +	strcpy(outfname, fname);
    6.74 +	if(!(suffix = strrchr(outfname, '.'))) {
    6.75 +		suffix = outfname + strlen(outfname);
    6.76 +	}
    6.77 +	strcpy(suffix, ".bin");
    6.78 +
    6.79 +	printf("processing %s -> %s\n", fname, outfname);
    6.80 +
    6.81 +	if(!(fp = fopen(outfname, "wb"))) {
    6.82 +		fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno));
    6.83 +		destroy_image(&img);
    6.84 +		free(outfname);
    6.85 +		return -1;
    6.86 +	}
    6.87 +
    6.88 +	printf("  bits per pixel: %d\n", img.bpp);
    6.89 +	if(img.num_ranges) {
    6.90 +		printf("  color ranges:\n");
    6.91 +		for(i=0; i<img.num_ranges; i++) {
    6.92 +			printf("    [%d]: %d - %d, rate: %u, mode: %s\n", i, img.range[i].low, img.range[i].high,
    6.93 +					img.range[i].rate, modestr(img.range[i].cmode));
    6.94 +		}
    6.95 +	}
    6.96 +
    6.97 +	if(planar) {
    6.98 +		output_planar(fp, &img);
    6.99 +	} else {
   6.100 +		output_chunky(fp, &img);
   6.101 +	}
   6.102 +
   6.103 +	free(outfname);
   6.104 +	fclose(fp);
   6.105 +	destroy_image(&img);
   6.106 +	return 0;
   6.107 +}
   6.108 +
   6.109 +static void output_planar(FILE *fp, struct image *img)
   6.110 +{
   6.111 +	int i, x, y;
   6.112 +	unsigned char acc = 0;
   6.113 +	int acc_bit = 7;
   6.114 +	unsigned char *pptr = img->pixels;
   6.115 +
   6.116 +	for(i=0; i<img->bpp; i++) {
   6.117 +		for(y=0; y<img->height; y++) {
   6.118 +			for(x=0; x<img->width; x++) {
   6.119 +				acc |= (((*pptr++ >> i) & 1) << acc_bit);
   6.120 +				if(--acc_bit == 0) {
   6.121 +					fputc(acc, fp);
   6.122 +					acc_bit = 7;
   6.123 +					acc = 0;
   6.124 +				}
   6.125 +			}
   6.126 +		}
   6.127 +	}
   6.128 +}
   6.129 +
   6.130 +static void output_chunky(FILE *fp, struct image *img)
   6.131 +{
   6.132 +	int i, j;
   6.133 +	unsigned char *pptr = img->pixels;
   6.134 +
   6.135 +	for(i=0; i<img->height; i++) {
   6.136 +		for(j=0; j<img->width; j++) {
   6.137 +			fputc(*pptr++, fp);
   6.138 +		}
   6.139 +	}
   6.140 +}
   6.141 +
   6.142 +static const char *modestr(int cmode)
   6.143 +{
   6.144 +	switch(cmode) {
   6.145 +	case CYCLE_NORMAL:
   6.146 +		return "forward";
   6.147 +	case CYCLE_REVERSE:
   6.148 +		return "reverse";
   6.149 +	case CYCLE_PINGPONG:
   6.150 +		return "ping-pong";
   6.151 +	case CYCLE_SINE_HALF:
   6.152 +		return "half-sine";
   6.153 +	case CYCLE_SINE:
   6.154 +		return "sine";
   6.155 +	default:
   6.156 +		break;
   6.157 +	}
   6.158 +	return "unknown";
   6.159 +}