/*	thirds.c

	This is the sequential reference version
	of thirdy.c

	1999 by H. Dietz
*/

#include <stdio.h>

extern unsigned char *
read_P6(register char *filename,
int *xdim,
int *ydim,
int *maxval);

extern int
write_P6(register char *filename,
register char *comment,
register int xdim,
register int ydim,
register int maxval,
register unsigned char *data);


unsigned char *
rgb1(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[0] = ((r[0] >> 2) +
				(r[3] >> 1) +
				(r[6] >> 2));
			q[1] = ((r[4] >> 2) +
				(r[7] >> 1) +
				(r[10] >> 2));
			q[2] = ((r[8] >> 2) +
				(r[11] >> 1) +
				(r[14] >> 2));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


unsigned char *
rgb2(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[0] = ((r[0] / 3.0) +
				(r[3] / 3.0) +
				(r[6] / 3.0));
			q[1] = ((r[4] / 3.0) +
				(r[7] / 3.0) +
				(r[10] / 3.0));
			q[2] = ((r[8] / 3.0) +
				(r[11] / 3.0) +
				(r[14] / 3.0));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


unsigned char *
rgb3(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[0] = ((r[0] * (1 / 9.0)) +
				(r[3] * (2 / 9.0)) +
				(r[6] * (3 / 9.0)) +
				(r[9] * (2 / 9.0)) +
				(r[12] * (1 / 9.0)));
			q[1] = ((r[4] * (1 / 9.0)) +
				(r[7] * (2 / 9.0)) +
				(r[10] * (3 / 9.0)) +
				(r[13] * (2 / 9.0)) +
				(r[16] * (1 / 9.0)));
			q[2] = ((r[8] * (1 / 9.0)) +
				(r[11] * (2 / 9.0)) +
				(r[14] * (3 / 9.0)) +
				(r[17] * (2 / 9.0)) +
				(r[20] * (1 / 9.0)));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


unsigned char *
bgr1(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[2] = ((r[0] >> 2) +
				(r[3] >> 1) +
				(r[6] >> 2));
			q[1] = ((r[4] >> 2) +
				(r[7] >> 1) +
				(r[10] >> 2));
			q[0] = ((r[8] >> 2) +
				(r[11] >> 1) +
				(r[14] >> 2));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


unsigned char *
bgr2(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[0] = ((r[0] / 3.0) +
				(r[3] / 3.0) +
				(r[6] / 3.0));
			q[1] = ((r[4] / 3.0) +
				(r[7] / 3.0) +
				(r[10] / 3.0));
			q[2] = ((r[8] / 3.0) +
				(r[11] / 3.0) +
				(r[14] / 3.0));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


unsigned char *
bgr3(register unsigned char *p,
register int xdim,
register int ydim)
{
	register unsigned char *result;
	register unsigned char *q;
	register int xout = (xdim / 3);
	register int i, j;

	q = ((unsigned char *) malloc(3 * xout * ydim));
	if (!q) {
		fprintf(stderr,
			"Insufficient memory for image\n");
		exit(3);
	}
	result = q;

	for (j=0; j<ydim; ++j) {
		register unsigned char *r = (p + (j * xdim * 3));

		for (i=0; i<xout; ++i) {
			q[2] = ((r[0] * (1 / 9.0)) +
				(r[3] * (2 / 9.0)) +
				(r[6] * (3 / 9.0)) +
				(r[9] * (2 / 9.0)) +
				(r[12] * (1 / 9.0)));
			q[1] = ((r[4] * (1 / 9.0)) +
				(r[7] * (2 / 9.0)) +
				(r[10] * (3 / 9.0)) +
				(r[13] * (2 / 9.0)) +
				(r[16] * (1 / 9.0)));
			q[0] = ((r[8] * (1 / 9.0)) +
				(r[11] * (2 / 9.0)) +
				(r[14] * (3 / 9.0)) +
				(r[17] * (2 / 9.0)) +
				(r[20] * (1 / 9.0)));
			r += 9;
			q += 3;
		}
	}

	return(result);
}


#include <fcntl.h>
#include <sys/mman.h>


unsigned char *
read_P6(register char *filename,
int *xdim,
int *ydim,
int *maxval)
{
	/* The following is a fast and sloppy way to
	   read a color raw PPM (P6) image file
	*/
	register int fd;
	register int fsize;
	register unsigned char *map;
	register unsigned char *p;

	/* First, open the file... */
	if ((fd = open(filename, O_RDONLY)) < 0) {
		return((unsigned char *) 0);
	}

	/* Read size and map the whole file... */
	fsize = lseek(fd, ((off_t) 0), SEEK_END);
	map = ((unsigned char *)
	       mmap(0,		/* Put it anywhere */
		    fsize,	/* Map the whole file */
		    PROT_READ,	/* Read only */
		    MAP_SHARED,	/* Not just for me */
		    fd,		/* The file */
		    0));	/* Right from the start */
	if (((int) map) == -1) {
		close(fd);
		return((unsigned char *) 0);
	}

	/* File should now be mapped; read magic value */
	p = map;
	if (*(p++) != 'P') goto ppm_exit;
	if (*(p++) != '6') goto ppm_exit;

#define	Eat_Space \
	while ((*p == ' ') || \
	       (*p == '\t') || \
	       (*p == '\n') || \
	       (*p == '\r') || \
	       (*p == '#')) { \
		if (*p == '#') while (*(++p) != '\n') ; \
		++p; \
	}

	Eat_Space;		/* Eat white space and comments */

#define	Get_Number(n) \
	{ \
		register int charval = *p; \
 \
		if ((charval < '0') || (charval > '9')) goto ppm_exit; \
 \
		n = (charval - '0'); \
		charval = *(++p); \
		while ((charval >= '0') && (charval <= '9')) { \
			n *= 10; \
			n += (charval - '0'); \
			charval = *(++p); \
		} \
	}

	Get_Number(*xdim);	/* Get image width */

	Eat_Space;		/* Eat white space and comments */
	Get_Number(*ydim);	/* Get image width */

	Eat_Space;		/* Eat white space and comments */
	Get_Number(*maxval);	/* Get image max value */

	/* Should be 8-bit binary after one whitespace char... */
	if (*maxval > 255) {
ppm_exit:
		close(fd);
		munmap(map, fsize);
		return((unsigned char *) 0);
	}
	if ((*p != ' ') &&
	    (*p != '\t') &&
	    (*p != '\n') &&
	    (*p != '\r')) goto ppm_exit;

	/* Here we are... next byte begins the 24-bit data */
	return(p + 1);

	/* Notice that we never clean-up after this:

	   close(fd);
	   munmap(map, fsize);

	   However, this is relatively harmless;
	   they will go away when this process dies.
	*/
}

#undef	Eat_Space
#undef	Get_Number


int
write_P6(register char *filename,
register char *comment,
register int xdim,
register int ydim,
register int maxval,
register unsigned char *data)
{
	register int fd;
	register int len;
	char buf[512];

	/* First, open the file... */
	fd = open(filename, (O_WRONLY | O_CREAT));
	if (fd < 0) {
		return(1);
	}

	/* Then write the header... */
	sprintf(&(buf[0]),
		"P6\n#%s\n%d %d\n%d\n",
		(comment ?
		 comment :
		 filename),
		xdim,
		ydim,
		maxval);
	len = strlen(&(buf[0]));
	if (write(fd, &(buf[0]), len) != len) {
		close(fd);
		return(2);
	}

	/* Then write the data... */
	len = (xdim * ydim * 3);
	if (write(fd, data, len) != len) {
		close(fd);
		return(3);
	}

	/* All looks good */
	close(fd);
	return(0);
}


main(register int argc,
register char **argv)
{
	register int filter = 1;
	register int rgb = 1;
	register unsigned char * p;
	int xdim, ydim, maxval;
	register int i;

	/* Process command line... */
	if (argc < 3) {
		fprintf(stderr,
			"Usage: %s {options} input_ppm output_ppm\n",
			argv[0]);
		exit(1);
	}
	for (i=1; i<(argc-2); ++i) {
		if (!strcmp("-f1", argv[i])) {
			filter = 1;
		} else if (!strcmp("-f2", argv[i])) {
			filter = 2;
		} else if (!strcmp("-f3", argv[i])) {
			filter = 3;
		} else if (!strcmp("-rgb", argv[i])) {
			rgb = 1;
		} else if (!strcmp("-bgr", argv[i])) {
			rgb = 0;
		} else {
			fprintf(stderr,
				"%s: bad option %s\n",
				argv[0],
				argv[i]);
			exit(2);
		}
	}

	/* Read the input */
	p = read_P6(argv[argc - 2],
		    &xdim,
		    &ydim,
		    &maxval);
	if (p == 0) {
		fprintf(stderr,
			"%s: could not read %s\n",
			argv[0],
			argv[argc - 2]);
		exit(2);
	}

	/* Invoke the appropriate subpixel rendering */
	if (rgb) {
		switch (filter) {
		case 1:	p = rgb1(p, xdim, ydim); break;
		case 2:	p = rgb2(p, xdim, ydim); break;
		case 3:	p = rgb3(p, xdim, ydim); break;
		}
	} else {
		switch (filter) {
		case 1:	p = bgr1(p, xdim, ydim); break;
		case 2:	p = bgr2(p, xdim, ydim); break;
		case 3:	p = bgr3(p, xdim, ydim); break;
		}
	}

	/* Write the output */
	if (write_P6(argv[argc - 1],
		     "just a test",
		     (xdim / 3),
		     ydim,
		     maxval,
		     p)) {
		fprintf(stderr,
			"%s: could not write %s\n",
			argv[0],
			argv[argc - 1]);
		exit(4);
	}

	/* All's well, let's end well */
	exit(0);
}
