/* wasp - Copyright 1991 by Steven Reiz
 * see COPYING and wasp.c for further info
 * wriffout.c, 4/12/90 - 29/12/90,
 * 4/5/91 - 2/6/91, 23/6/91 - 30/6/91, 3/7/91 - 8/7/91,
 * 15/11/91, 8/12/91, 11/3/92
 */

static char *sourcefile=__FILE__;

#include "wasp.h"
#include "wriff.h"

static u_char *prtab[16], *pgtab[16], *pbtab[16], *pxtab[16];
static long squares[]={
	0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225
};


void
row_out(int row)
{
    long currow; /* u_char *currow; */
    short *ps, i, x, bperrow;
    char regind;
    u_char *p, *q;

    counter();
    currow=(long)next_row(0);
    bperrow=bytesperrow;
    ps=(short *)currow;
    i=bperrow*nplanes/2-1;
    do {
        *ps++ =0;
    } while (--i>=0);
    ps=(short *)rgb[row];
    for (x=0; x<xsz; ++x) {
        i= *ps++;
        regind=newcol[i];
        err+=error[i];
        err2+=error2[i];
        p=conv+((regind<<3)|(x&7))*MAXNPLANES;
        q=(u_char *)currow+(x>>3);
        switch (nplanes) {
        case 8: *q|= *p++; q+=bperrow;
        case 7: *q|= *p++; q+=bperrow;
        case 6: *q|= *p++; q+=bperrow;
        case 5: *q|= *p++; q+=bperrow;
        case 4: *q|= *p++; q+=bperrow;
        case 3: *q|= *p++; q+=bperrow;
        case 2: *q|= *p++; q+=bperrow;
        case 1: *q|= *p;
        }
    }
}


void
rgb_row_out(int row)
{
	u_char *currow;
	short *ps, i, bitlast;

	counter();
	currow=next_row(0);
	ps=(short *)currow;
	i=bytesperrow*nplanes/2-1;
	do {
		*ps++ =0;
	} while (--i>=0);
	for (bitlast=12; bitlast>0; bitlast-=4) { /* walk over r, g, and b */
		for (i=0; i<nzero; ++i)
			currow+=bytesperrow;
		for (i=bitlast-ncolor; i<bitlast; ++i) {
			rgb_line(row, currow, (int)i);
			currow+=bytesperrow;
		}
	}
}


void
rgb_line(int y, unsigned char *inq, int bitnum)
{
	u_short *p, mask;
	u_char *q, byte;
	short i;
	
	i=bytesperrow-1;
	p=rgb[y];
	mask=1<<bitnum;
	q=inq;
	do {
		byte=0;
		if (*p++ & mask) byte|=0x80;
		if (*p++ & mask) byte|=0x40;
		if (*p++ & mask) byte|=0x20;
		if (*p++ & mask) byte|=0x10;
		if (*p++ & mask) byte|=0x08;
		if (*p++ & mask) byte|=0x04;
		if (*p++ & mask) byte|=0x02;
		if (*p++ & mask) byte|=0x01;
		*q++ =byte;
	} while (--i>=0);
}


void
ham_row_out(int row)
{
	NON_REG int alternate_rgb;
	long currow; /* actually u_char *currow; */
	short *ps;
	long x, curcolor; /* curcolor doubles as bperrow */
	long r, g, b, pr, pg, pb, d1, d2, d3;
	u_char *p, *q;

	counter();
	currow=(long)next_row(0);
	p=(u_char *)currow;
	r=bytesperrow*3-1;	/* nr of shorts, bytesperrow*nplanes/2 */
#ifdef LATTICE
	do {
		*(short *)p=0;
		p+=2;
	} while (--r>=0);
#else
	do {
		*((short *)p)++ =0;
	} while (--r>=0);
#endif
	ps=(short *)rgb[row];
	pb=curcm[0];
	pr=pb>>8;
	pg=(pb>>4)&0xf;
	pb&=0xf;
	alternate_rgb=0;
	for (x=0; x<xsz; ++x) {
		curcolor= *ps++;
		b=curcolor;
		r=b>>8;
		g=(b>>4)&0xf;
		b&=0xf;
		if (r==pr) {
			if (g==pg) {
				if (b==pb) {	/* r ok, g ok, b ok */
					if (++alternate_rgb==1) {
						p=pgtab[g];
					} else if (alternate_rgb==2) {
						p=prtab[r];
					} else {
						p=pbtab[b];
						alternate_rgb=0;
					}
				} else {	/* r ok, g ok, b wrong */
					p=pbtab[b];
					pb=b;
				}
			} else {
				if (b==pb) {	/* r ok, g wrong, b ok */
					p=pgtab[g];
					pg=g;
				} else {	/* r ok, g wrong, b wrong */
					if ((d1=g-pg)<0) d1= -d1;
					if ((d2=b-pb)<0) d2= -d2;
					if (error[curcolor]<(d1<d2 ? d1 : d2)) {
						err+=error[curcolor];
						err2+=error2[curcolor];
						curcolor=newcol[curcolor];
						p=pxtab[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>=d2) {
						err+=d2;
						err2+=squares[d2];
						p=pgtab[g];
						pg=g;
					} else {
						err+=d1;
						err2+=squares[d1];
						p=pbtab[b];
						pb=b;
					}
				}
			}
		} else {
			if (g==pg) {
				if (b==pb) {	/* r wrong, g ok, b ok */
					p=prtab[r];
					pr=r;
				} else {	/* r wrong, g ok, b wrong */
					if ((d1=r-pr)<0) d1= -d1;
					if ((d2=b-pb)<0) d2= -d2;
					if (error[curcolor]<(d1<d2 ? d1 : d2)) {
						err+=error[curcolor];
						err2+=error2[curcolor];
						curcolor=newcol[curcolor];
						p=pxtab[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>=d2) {
						err+=d2;
						err2+=squares[d2];
						p=prtab[r];
						pr=r;
					} else {
						err+=d1;
						err2+=squares[d1];
						p=pbtab[b];
						pb=b;
					}
				}
			} else {
				if (b==pb) {	/* r wrong, g wrong, b ok */
					if ((d1=r-pr)<0) d1= -d1;
					if ((d2=g-pg)<0) d2= -d2;
					if (error[curcolor]<(d1<d2 ? d1 : d2)) {
						err+=error[curcolor];
						err2+=error2[curcolor];
						curcolor=newcol[curcolor];
						p=pxtab[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>d2) {
						err+=d2;
						err2+=squares[d2];
						p=prtab[r];
						pr=r;
					} else {
						err+=d1;
						err2+=squares[d1];
						p=pgtab[g];
						pg=g;
					}
				} else {	/* r wrong, g wrong, b wrong */
					if ((d1=r-pr)<0) d1= -d1;
					if ((d2=g-pg)<0) d2= -d2;
					if ((d3=b-pb)<0) d3= -d3;
					if (error[curcolor]<(d1>d2 ? (d1>d3 ? d2+d3 : d1+d2) : (d2>d3 ? d1+d3 : d1+d2))) {
						err+=error[curcolor];
						err2+=error2[curcolor];
						curcolor=newcol[curcolor];
						p=pxtab[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>d2 && d1>=d3) {
						err+=d2+d3;
						err2+=squares[d2]+squares[d3];
						p=prtab[r];
						pr=r;
					} else if (d2>=d3) {
						err+=d1+d3;
						err2+=squares[d1]+squares[d3];
						p=pgtab[g];
						pg=g;
					} else {
						err+=d1+d2;
						err2+=squares[d1]+squares[d2];
						p=pbtab[b];
						pb=b;
					}
				}
			}
		}
		p+=(x&7)<<MAXPLANESHIFT;
		q=(u_char *)currow+(x>>3);
		curcolor=bytesperrow;
		*q|= *p++; q+=curcolor;
		*q|= *p++; q+=curcolor;
		*q|= *p++; q+=curcolor;
		*q|= *p++; q+=curcolor;
		*q|= *p++; q+=curcolor;
		*q|= *p;
	}
}


void
fill_prgbtab(void)
{
	int i;

	for (i=0; i<16; ++i) {
		prtab[i]=conv+((0x20+i)<<(3+MAXPLANESHIFT));
		pgtab[i]=conv+((0x30+i)<<(3+MAXPLANESHIFT));
		pbtab[i]=conv+((0x10+i)<<(3+MAXPLANESHIFT));
		pxtab[i]=conv+(i<<(3+MAXPLANESHIFT));
	}
}


unsigned char *
next_row(int flush)
{
    static int freerows= -1;
    static u_char *currow;

    if (flush) {
	char c;

        cwritec((int)((noutrows-freerows)*bytesperrow*nplanes));
	if (body_size&1) {
		c=0;
		cwrite(&c, 1);
		++body_size;
	}
	if (compression)
		erase_counter("IFF output; body compression: %ld%%",
		 body_size*100L/(xsz*ysz*(long)nplanes/8L));
        return NULL; /* return value not used */
    }
    if (--freerows<0) {
        if (freerows== -1)
            cwritec(outbufsz);
        freerows=noutrows-1;
        currow=outrows;
    } else 
        currow+=bytesperrow*nplanes;
    return currow;
}


void
cwritec(int inlen)
{
	REG char *p, *r;
	char *q, *countp, *curoutrows;
	short n, todo;
	char c;

	if (!compression) {
		cwrite(outrows, inlen);
		body_size+=inlen;
		return;
	}
	assert(!(inlen%bytesperrow));
	inlen/=bytesperrow;
	curoutrows=(char *)outrows;
	r=(char *)outcrows;
	while (--inlen>=0) {
		todo=bytesperrow;
		p=(char *)curoutrows;
		q=p+todo-2;
		while (p<q) {
			c= *p;
			if (*(p+1)==c) {
				countp=p;
				p+=2;
				n=todo-(p-curoutrows);
				if (n>126)
					n=126;
				while (*p==c && --n>=0)
					++p;
				*r++ =countp-p+1;
				*r++ =c;
			} else {
				countp=r++;
				n=todo-(p-curoutrows);
				if (n>128)
					n=128;
				do {
					++p;
					if (*p==c && *(p+1)==c) {
						--p;
						break;
					}
					*r++ =c;
					c= *p;
				} while (--n>0);
				*countp=r-countp-2;
			}
		}
		todo=curoutrows+todo-p;
		assert(todo<=2);
		if (todo==1) {
			*r++ =0;
			*r++ = *p;
		} else if (todo==2) {
			c= *p++;
			if (c== *p) {
				*r++ = -1;
				*r++=c;
			} else {
				*r++ =1;
				*r++ =c;
				*r++ = *p;
			}
		}
		curoutrows+=bytesperrow;
	}
	cwrite(outcrows, (int)(r-(char *)outcrows));
	body_size+=r-(char *)outcrows;
}
