/*
 *	fromrad.c -
 *		convert a Radiance floating point image to Iris format.
 *
 *			Greg Ward - 1992
 */

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

#ifdef WIN32

#else
#include <sys/fcntl.h>
#endif

#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "fromrad.h"

int expadj = 0;
char  FMTSTR[] = "FORMAT=";


#if 0
void ignoremain(int argc,char **argv)
{
    IMAGE *image;
    FILE *inf;
    int xsize, ysize;
    int y;
    int  dogamma = 0;

    if(argc<3) {
        fprintf(stderr,"usage: fromrad [-g gamma][-e +/-stops] radimage irisimage\n");
        exit(1);
    }
    while (argc > 4)
	    if (!strcmp(argv[1], "-g")) {
            setcolrgam(atof(argv[2]));
            dogamma++;
            argv += 2; argc -= 2;
	    } else if (!strcmp(argv[1], "-e")) {
            expadj = atoi(argv[2]);
            argv += 2; argc -= 2;
	    }
    inf = fopen(argv[1],"rb");
    if(!inf) {
        fprintf(stderr,"fromrad: can't open %s\n",argv[1]);
        exit(1);
    }
    if (checkheader(inf,COLRFMT,(FILE *)NULL) < 0 ||
		fgetresolu(&xsize, &ysize, inf) < 0) {
        fprintf(stderr,"fromrad: input not in Radiance format\n");
        exit(1);
    }
    image = iopen(argv[2],"w",RLE(1),3,xsize,ysize,3);
    for(y=0; y<ysize; y++) {
        freadcolrs(cbuf,xsize,inf);
        if (dogamma) {
            if (expadj)
            shiftcolrs(cbuf,xsize,expadj);
            colrs_gambs(cbuf,xsize);
        } else
            normcolrs(cbuf,xsize,expadj);
        unswizzleColr((unsigned char*)cbuf,rbuf,gbuf,bbuf,xsize);
        putrow(image,rbuf,(ysize-1)-y,0);
        putrow(image,gbuf,(ysize-1)-y,1);
        putrow(image,bbuf,(ysize-1)-y,2);
    }
    iclose(image);

    exit(0);
}
#endif

void unswizzleColr(register unsigned char *cptr,register unsigned short *rptr,register unsigned short *gptr,register unsigned short *bptr,register int n)
{
    while(n--) {
        *rptr++ = *cptr++;
        *gptr++ = *cptr++;
        *bptr++ = *cptr++;
        cptr++; // Skip the alpha channel
    }
}

//********************* IMPORTED CODE **************************?

// The following was imported from color.c in ray/src/common of Radiance 2.2


int freadcolrs(register COLR  *scanline, int  len, register FILE  *fp)		/* read in an encoded colr scanline */
{
	register int  i, j;
	int  code;

    /* determine scanline type */
	if (len < MINELEN | len > 0x7fff)
		return(oldreadcolrs(scanline, len, fp));
	if ((i = getc(fp)) == EOF)
		return(-1);
	if (i != 2) {
		ungetc(i, fp);
		return(oldreadcolrs(scanline, len, fp));
	}
	scanline[0][GRN] = getc(fp);
	scanline[0][BLU] = getc(fp);
	if ((i = getc(fp)) == EOF)
		return(-1);
	if (scanline[0][GRN] != 2 || scanline[0][BLU] & 128) {
		scanline[0][RED] = 2;
		scanline[0][EXP] = i;
		return(oldreadcolrs(scanline+1, len-1, fp));
	}
	if ((scanline[0][BLU]<<8 | i) != len)
		return(-1);		/* length mismatch! */

    /* read each component */
	for (i = 0; i < 4; i++) {
	    for (j = 0; j < len; ) {
            if ((code = getc(fp)) == EOF)
                return(-1);
            if (code > 128) {	/* run */
                scanline[j++][i] = getc(fp);
                for (code &= 127; --code; j++)
                scanline[j][i] = scanline[j-1][i];
            } else			/* non-run */
            while (code--)
                scanline[j++][i] = getc(fp);
	    }
    }
	return(feof(fp) ? -1 : 0);
}


int oldreadcolrs(register COLR  *scanline, int len, register FILE  *fp)		
/* read in an old colr scanline */
{
	int  rshift;
	register int  i;
	
	rshift = 0;
	
	while (len > 0) {
		scanline[0][RED] = getc(fp);
		scanline[0][GRN] = getc(fp);
		scanline[0][BLU] = getc(fp);
		scanline[0][EXP] = getc(fp);
		if (feof(fp) || ferror(fp))
			return(-1);
		if (scanline[0][RED] == 1 &&
            scanline[0][GRN] == 1 &&
            scanline[0][BLU] == 1) {
			for (i = scanline[0][EXP] << rshift; i > 0; i--) {
				copycolr(scanline[0], scanline[-1]);
				scanline++;
				len--;
			}
			rshift += 8;
		} else {
			scanline++;
			len--;
			rshift = 0;
		}
	}
	return(0);
}

/* End of color.c includes */

/* The following was imported from colrops.c in ray/src/common of Radiance 2.2 */

//int setcolrcor(double (*f)(), double a2)		/* set brightness correction */
int setcolrcor(double a2)		/* set brightness correction */
{
	double	mult;
	register int	i, j;

    /* allocate tables */
	if (g_bval == NULL && (g_bval =
			(BYTE (*)[256])bmalloc((MAXGSHIFT+1)*256)) == NULL)
		return(-1);

    /* compute colr -> gamb mapping */
	mult = 1.0/256.0;
	for (i = 0; i <= MAXGSHIFT; i++) {
		for (j = 0; j < 256; j++)
			//g_bval[i][j] = 256.0 * (*f)((j+.5)*mult, a2);
			g_bval[i][j] = 256.0 * pow((j+.5)*mult, a2);
		mult *= 0.5;
	}
	return(0);
}


int setcolrgam(double g)			/* set gamma conversion */
{
	//return(setcolrcor(pow, 1.0/g));
	return(setcolrcor(1.0/g));
}


int colrs_gambs(register COLR *scan, int len) /* convert scanline of colrs to gamma bytes */
{
	register int	i, expo;

	if (g_bval == NULL)
		return(-1);
	while (len-- > 0) {
		expo = scan[0][EXP] - COLXS;
		if (expo < -MAXGSHIFT) {
			if (expo < -MAXGSHIFT-8) {
				scan[0][RED] =
				scan[0][GRN] =
				scan[0][BLU] = 0;
			} else {
				i = (-MAXGSHIFT-1) - expo;
				scan[0][RED] = 
				g_bval[MAXGSHIFT][((scan[0][RED]>>i)+1)>>1];
				scan[0][GRN] =
				g_bval[MAXGSHIFT][((scan[0][GRN]>>i)+1)>>1];
				scan[0][BLU] =
				g_bval[MAXGSHIFT][((scan[0][BLU]>>i)+1)>>1];
			}
		} else if (expo > 0) {
			if (expo > 8) {
				scan[0][RED] =
				scan[0][GRN] =
				scan[0][BLU] = 255;
			} else {
				i = (scan[0][RED]<<1 | 1) << (expo-1);
				scan[0][RED] = i > 255 ? 255 : g_bval[0][i];
				i = (scan[0][GRN]<<1 | 1) << (expo-1);
				scan[0][GRN] = i > 255 ? 255 : g_bval[0][i];
				i = (scan[0][BLU]<<1 | 1) << (expo-1);
				scan[0][BLU] = i > 255 ? 255 : g_bval[0][i];
			}
		} else {
			scan[0][RED] = g_bval[-expo][scan[0][RED]];
			scan[0][GRN] = g_bval[-expo][scan[0][GRN]];
			scan[0][BLU] = g_bval[-expo][scan[0][BLU]];
		}
		scan[0][EXP] = COLXS;
		scan++;
	}
	return(0);
}


void
shiftcolrs(register COLR* scan, register int len, register int adjust)	/* shift a scanline of colors by 2^adjust */
{
	int	minexp;

	if (adjust == 0)
		return;
	minexp = adjust < 0 ? -adjust : 0;
	while (len-- > 0) {
		if (scan[0][EXP] <= minexp)
			scan[0][RED] = scan[0][GRN] = scan[0][BLU] =
			scan[0][EXP] = 0;
		else
			scan[0][EXP] += adjust;
		scan++;
	}
}

void
normcolrs(register COLR* scan, int len, int adjust)	/* normalize a scanline of colrs */
{
	register int  c;
	register int  shift;

	while (len-- > 0) {
		shift = scan[0][EXP] + adjust - COLXS;
		if (shift > 0) {
			if (shift > 8) {
				scan[0][RED] =
				scan[0][GRN] =
				scan[0][BLU] = 255;
			} else {
				shift--;
				c = (scan[0][RED]<<1 | 1) << shift;
				scan[0][RED] = c > 255 ? 255 : c;
				c = (scan[0][GRN]<<1 | 1) << shift;
				scan[0][GRN] = c > 255 ? 255 : c;
				c = (scan[0][BLU]<<1 | 1) << shift;
				scan[0][BLU] = c > 255 ? 255 : c;
			}
		} else if (shift < 0) {
			if (shift < -8) {
				scan[0][RED] =
				scan[0][GRN] =
				scan[0][BLU] = 0;
			} else {
				shift = -1-shift;
				scan[0][RED] = ((scan[0][RED]>>shift)+1)>>1;
				scan[0][GRN] = ((scan[0][GRN]>>shift)+1)>>1;
				scan[0][BLU] = ((scan[0][BLU]>>shift)+1)>>1;
			}
		}
		scan[0][EXP] = COLXS - adjust;
		scan++;
	}
}

/* End of colrops.c includes */

/* The following was imported from header.c in ray/src/common of Radiance 2.2 */

/*
 *  header.c - routines for reading and writing information headers.
 *
 *	8/19/88
 *
 *  isformat(s)		returns true if s is of the form "FORMAT=*"
 *  formatval(r,s)	copy the format value in s to r
 *  getheader(fp,f,p)	read header from fp, calling f(s,p) on each line
 *  checkheader(i,p,o)	check header format from i against p and copy to o
 *
 *  To copy header from input to output, use getheader(fin, fputs, fout)
 */



void
formatval(register char* r, register char*  s) /* return format value */
{
	s += FMTSTRL;
	while (isspace(*s)) s++;
	if (!*s) { *r = '\0'; return; }
	while(*s) *r++ = *s++;
	while (isspace(r[-1])) r--;
	*r = '\0';
}


int
getheader(FILE* fp, void (*f)(char *, struct check *), struct check* p)		/* get header from file */
{
	char  buf[MAXLINE];

	for ( ; ; ) {
		buf[MAXLINE-2] = '\n';
		if (fgets(buf, MAXLINE, fp) == NULL)
			return(-1);
		if (buf[0] == '\n')
			return(0);
		if (buf[MAXLINE-2] != '\n') {
			ungetc(buf[MAXLINE-2], fp);	/* prevent false end */
			buf[MAXLINE-2] = '\0';
		}
		if (f != NULL)
			(*f)(buf, p);
	}
}



static void
mycheck(char* s, register struct check* cp)			/* check a header line for format info. */
{
	if (!strncmp(s,FMTSTR,FMTSTRL))
		formatval(cp->fs, s);
	else if (cp->fp != NULL)	/* don't copy format info. */
		fputs(s, cp->fp);
}


/*
 * Checkheader(fin,fmt,fout) returns a value of 1 if the input format
 * matches the specification in fmt, 0 if no input format was found,
 * and -1 if the input format does not match or there is an
 * error reading the header.  If fmt is empty, then -1 is returned
 * if any input format is found (or there is an error), and 0 otherwise.
 * If fmt contains any '*' or '?' characters, then checkheader
 * does wildcard expansion and copies a matching result into fmt.
 * Be sure that fmt is big enough to hold the match in such cases!
 * The input header (minus any format lines) is copied to fout
 * if fout is not NULL.
 */

int checkheader(FILE  *fin, char  *fmt, FILE  *fout)
{
	struct check	cdat;

	cdat.fp = fout;
	cdat.fs[0] = '\0';
	if (getheader(fin, mycheck, &cdat) < 0)
		return(-1);
	if (cdat.fs[0] != '\0')
		return(copymatch(fmt, cdat.fs) ? 1 : -1);
	return(0);
}

/* End of header.c includes */

/* The following was imported from resolu.c in ray/src/common of Radiance 2.2 */

/*
 * Read and write image resolutions.
 */


int
fgetresolu(int *sl, int  *ns, FILE  *fp)  /* get picture dimensions */
                                          /* scanline length and number */
{
	RESOLU  rs;

	if (!fgetsresolu(&rs, fp))
		return(-1);
	if (rs.or & YMAJOR) {
		*sl = rs.xr;
		*ns = rs.yr;
	} else {
		*sl = rs.yr;
		*ns = rs.xr;
	}
	return(rs.or);
}


int str2resolu(register RESOLU *rp, char  *buf)		/* convert resolution line to struct */
{
	register char  *xndx, *yndx;
	register char  *cp;

	if (buf == NULL)
		return(0);
	xndx = yndx = NULL;
	for (cp = buf; *cp; cp++)
		if (*cp == 'X')
			xndx = cp;
		else if (*cp == 'Y')
			yndx = cp;
	if (xndx == NULL || yndx == NULL)
		return(0);
	rp->or = 0;
	if (xndx > yndx) rp->or |= YMAJOR;
	if (xndx[-1] == '-') rp->or |= XDECR;
	if (yndx[-1] == '-') rp->or |= YDECR;
	if ((rp->xr = atoi(xndx+1)) <= 0)
		return(0);
	if ((rp->yr = atoi(yndx+1)) <= 0)
		return(0);
	return(1);
}

/* End of header.c includes */
