/* wasp - Copyright 1991 by Steven Reiz
 * see COPYING and wasp.c for further info
 * wriffcount.c, 4/12/90 - 29/12/90,
 * 2/6/91, 23/6/91, 1/7/91 - 10/7/91, 8/12/91
 */

static char *sourcefile=__FILE__;

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

static u_char *xor_tab=NULL;


#ifndef AZTEC_C	
/* this function has been rewritten in assembler for the Aztec C
 * assembler, see wriffasm.c
 */

void
clear_counts(void)
{
	short i;
	u_long *p;
	long r0, r1, r2, r3, r4, r5, r6, r7;

	p=counts; i=NRGB/16-1;
	r0=0;  r1=r0; r2=r0; r3=r0;
	r4=r0; r5=r0; r6=r0; r7=r0;
	do {
		*p++ =r0; *p++ =r1; *p++ =r2; *p++ =r3;
		*p++ =r4; *p++ =r5; *p++ =r6; *p++ =r7;
		*p++ =r0; *p++ =r1; *p++ =r2; *p++ =r3;
		*p++ =r4; *p++ =r5; *p++ =r6; *p++ =r7;
	} while (--i>=0);
}
#endif /* AZTEC_C */


void
fill_xor_tab(void)
{
	short i;
	u_char *tab;

	xor_tab=Malloc(NRGB);
	tab=xor_tab+NRGB;
	i=NRGB-1;
	do {
		if ((i&0xf0f)==0 || (i&0xff0)==0 || (i&0x0ff)==0)
			*--tab=0;
		else
			*--tab=1;
	} while (--i>=0);
}


void
call1(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;

    pp=rgb[row];
    cp=counts;
    x=xsz-1;
    do {
        ++cp[*pp++];
    } while (--x>=0);
}


void
calldif(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;
    short prevcolor, color;
    short d, t;

    pp=rgb[row];
    cp=counts;
    x=xsz-1;
    prevcolor=0;
    do {
        color= *pp++;
        t=(color-prevcolor)>>8;
        if (t<0) d= -t; else d=t;
        t=((color&0x00f0)-(prevcolor&0x00f0))>>4;
        if (t<0) d-=t;  else d+=t;
        t=(color&0x000f)-(prevcolor&0x000f);
        if (t<0) d-=t;  else d+=t;
        cp[color]+=d;
        prevcolor=color;
    } while (--x>=0);
}


void
callfixdif(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;
    short prevcolor, color;
    short r, g, b;

    pp=rgb[row];
    cp=counts;
    x=xsz-1;
    prevcolor=0;
    do {
        color= *pp++;
        r=(color-prevcolor)>>8;
        if (r<0) r= -r;
        g=((color&0x00f0)-(prevcolor&0x00f0))>>4;
        if (g<0) g= -g;
        b=(color&0x000f)-(prevcolor&0x000f);
        if (b<0) b= -b;
        if (r>g) {
            if (b>r)
                cp[color]+=r+g;
            else
                cp[color]+=b+g;
        } else {
            if (b>g)
                cp[color]+=r+g;
            else
                cp[color]+=r+b;
        }
        prevcolor=color;
    } while (--x>=0);
}


void
cjump1(int row)
{
    u_short *pp;
    u_long  *cp;
    u_char *tab;
    short x;
    long prevcolor, color;

    if (!xor_tab)
	fill_xor_tab();
    tab=xor_tab;
    pp=rgb[row];
    cp=counts;
    prevcolor= *pp++;
    ++cp[prevcolor];
    x=xsz-2;
    do {
        color= *pp++;
	if (tab[color^prevcolor])
	    ++cp[color];
        prevcolor=color;
    } while (--x>=0);
}


void
cjump21(int row)
{
    u_short *pp;
    u_long  *cp;
    u_char *tab;
    short x;
    long prevcolor, color, add;

    if (!xor_tab)
	fill_xor_tab();
    tab=xor_tab;
    pp=rgb[row];
    cp=counts;
    add=1;
    prevcolor= *pp++;
    cp[prevcolor]+=2;
    x=xsz-2;
    do {
        color= *pp++;
        if (tab[color^prevcolor])
	    add=2;
        cp[color]+=add;
        add>>=1;
        prevcolor=color;
    } while (--x>=0);
}


void
cjumpdif(int row)
{
    u_short *pp;
    u_long  *cp;
    u_char *tab;
    short x;
    long prevcolor, color, d, t;

    if (!xor_tab)
	fill_xor_tab();
    tab=xor_tab;
    pp=rgb[row];
    cp=counts;
    prevcolor= *pp++;
    cp[prevcolor]+=12;
    x=xsz-2;
    do {
        color= *pp++;
        if (tab[color^prevcolor]) {
            if ((d=(color-prevcolor)>>8)<0)
		d= -d;
            if ((t=((color&0x00f0)-(prevcolor&0x00f0))>>4)<0)
		d-=t;
	    else
		d+=t;
            if ((t=(color&0x000f)-(prevcolor&0x000f))<0)
		d-=t;
	    else
		d+=t;
            cp[color]+=d;
        }
        prevcolor=color;
    } while (--x>=0);
}


void
cjumpdifsh(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;
    short prevcolor, color;
    short d, t;
    long add;

    pp=rgb[row];
    cp=counts;
    color= *pp;
    add=(color>>8)+((color>>4)&0x0f)+(color&0x0f);
    prevcolor=0;
    x=xsz-1;
    do {
        color= *pp++;
        t=color^prevcolor;
        if (!( (t&0xf0f)==0 || (t&0xff0)==0 || (t&0x0ff)==0 )) {
            t=(color-prevcolor)>>8;
            if (t<0) d= -t; else d=t;
            t=((color&0x00f0)-(prevcolor&0x00f0))>>4;
            if (t<0) d-=t;  else d+=t;
            t=(color&0x000f)-(prevcolor&0x000f);
            if (t<0) d-=t;  else d+=t;
            if (add<d)
                add=d;
        }
        cp[color]+=add;
        add>>=1;
        prevcolor=color;
    } while (--x>=0);
}


void
cjumpfixdif(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;
    short prevcolor, color;
    short r, g, b;

    pp=rgb[row];
    cp=counts;
    prevcolor= *pp++;
    r=prevcolor>>8;
    g=(prevcolor>>4)&0x0f;
    b=prevcolor&0x0f;
    if (r>g) {
        if (b>r)
            cp[prevcolor]+=r+g;
        else
            cp[prevcolor]+=b+g;
    } else {
        if (b>g)
            cp[prevcolor]+=r+g;
        else
            cp[prevcolor]+=r+b;
    }
    x=xsz-2;
    do {
        color= *pp++;
        r=color^prevcolor;
        if (!( (r&0xf0f)==0 || (r&0xff0)==0 || (r&0x0ff)==0 )) {
            r=(color-prevcolor)>>8;
            if (r<0) r= -r;
            g=((color&0x00f0)-(prevcolor&0x00f0))>>4;
            if (g<0) g= -g;
            b=(color&0x000f)-(prevcolor&0x000f);
            if (b<0) b= -b;
            if (r>g) {
                if (b>r)
                    cp[color]+=r+g;
                else
                    cp[color]+=b+g;
            } else {
                if (b>g)
                    cp[color]+=r+g;
                else
                    cp[color]+=r+b;
            }
        }
        prevcolor=color;
    } while (--x>=0);
}


void
cjumpfixdifsh(int row)
{
    u_short *pp;
    u_long  *cp;
    short x;
    short prevcolor, color;
    short r, g, b;
    long add;

    pp=rgb[row];
    cp=counts;
    prevcolor=0;
    color= *pp;
    r=color>>8;
    g=(color>>4)&0x0f;
    b=color&0x0f;
    if (r>g) {
        if (b>r)
            add=r+g;
        else
            add=b+g;
    } else {
        if (b>g)
            add=r+g;
        else
            add=r+b;
    }
    x=xsz-1;
    do {
        color= *pp++;
        r=color^prevcolor;
        if (!( (r&0xf0f)==0 || (r&0xff0)==0 || (r&0x0ff)==0 )) {
            r=(color-prevcolor)>>8;
            if (r<0) r= -r;
            g=((color&0x00f0)-(prevcolor&0x00f0))>>4;
            if (g<0) g= -g;
            b=(color&0x000f)-(prevcolor&0x000f);
            if (b<0) b= -b;
            if (r>g) {
                if (b>r)
                    add=r+g;
                else
                    add=b+g;
            } else {
                if (b>g)
                    add=r+g;
                else
                    add=r+b;
            }
        }
        cp[color]+=add;
        add>>=1;
        prevcolor=color;
    } while (--x>=0);
}


void
chammap(int row)
{
	u_short *pp;
	u_long *cp;
	short x;
	long curcolor;
	long r, g, b, pr, pg, pb, d1, d2, d3;

	pp=rgb[row];
	cp=counts;
	x=xsz-1;
	pb=curcm[0];
	pr=pb>>8;
	pg=(pb>>4)&0xf;
	pb&=0xf;
	do {
		curcolor= *pp++;
		b=curcolor;
		r=b>>8;
		g=(b>>4)&0xf;
		b&=0xf;
		if (r==pr) {
			if (g==pg) {		/* r ok, g ok */
				pb=b;
			} else {
				if (b==pb) {	/* r ok, g wrong, b ok */
					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)) {
						cp[curcolor]+=error[curcolor];
						curcolor=newcol[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>=d2) {
						cp[curcolor]+=d2;
						pg=g;
					} else {
						cp[curcolor]+=d1;
						pb=b;
					}
				}
			}
		} else {
			if (g==pg) {
				if (b==pb) {	/* r wrong, g ok, b ok */
					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)) {
						cp[curcolor]+=error[curcolor];
						curcolor=newcol[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>=d2) {
						cp[curcolor]+=d2;
						pr=r;
					} else {
						cp[curcolor]+=d1;
						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)) {
						cp[curcolor]+=error[curcolor];
						curcolor=newcol[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>d2) {
						cp[curcolor]+=d2;
						pr=r;
					} else {
						cp[curcolor]+=d1;
						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))) {
						cp[curcolor]+=error[curcolor];
						curcolor=newcol[curcolor];
						pb=curcm[curcolor];
						pr=pb>>8;
						pg=(pb>>4)&0xf;
						pb&=0xf;
					} else if (d1>d2 && d1>=d3) {
						cp[curcolor]+=d2+d3;
						pr=r;
					} else if (d2>=d3) {
						cp[curcolor]+=d1+d3;
						pg=g;
					} else {
						cp[curcolor]+=d1+d2;
						pb=b;
					}
				}
			}
		}
	} while (--x>=0);
}


void
fill_curcm(short meth)
{
	int i;
	short c=0;
	static short cubic[]={
		0x222, 0xe22, 0x2e2, 0x22e,
		0x666, 0xa66, 0x6a6, 0x66a,
		0x6aa, 0xa6a, 0xaa6, 0xaaa,
		0x2ee, 0xe2e, 0xee2, 0xeee
	};

	for (i=0; i<16; ++i) {
		switch (meth) {
		case COUNTMETH_HAMMAP_GS:
			curcm[i]=c;
			c+=0x111;
		break;
		case COUNTMETH_HAMMAP_CUBIC:
			curcm[i]=cubic[i];
		break;
		}
	}
}


int
count_colors(unsigned long thr)
{
    short i;
    long n;
    u_long *p, th;

    assert((p=counts)!=NULL1);
    i=NRGB/8-1; th=thr;
    if (th==0)
        n=NRGB;
    else if (th==1) {
	n=0;
        do {
            if (*p++) ++n; if (*p++) ++n; if (*p++) ++n; if (*p++) ++n;
            if (*p++) ++n; if (*p++) ++n; if (*p++) ++n; if (*p++) ++n;
        } while (--i>=0);
    } else {
	n=0;
        do {
            if (*p++ >=th) ++n; if (*p++ >=th) ++n; if (*p++ >=th) ++n; if (*p++ >=th) ++n;
            if (*p++ >=th) ++n; if (*p++ >=th) ++n; if (*p++ >=th) ++n; if (*p++ >=th) ++n;
        } while (--i>=0);
    }
    return (int)n;
}


void
count_pixels(int firstrow, int lastrow, int moverride)
{
    static int sfirstrow= -1, slastrow= -1;
    short meth; static short smeth= -1;
    int row;
    void (*countfunc)(int);

    if (counts==NULL1)
        counts=Malloc(NRGB*sizeof(u_long));
    meth=countmeth;
    if (moverride)
        meth=COUNTMETH_ALL_1;
    if (!inoperation && firstrow==sfirstrow && lastrow==slastrow && meth==smeth)
        return;
    sfirstrow=firstrow; slastrow=lastrow; smeth=meth;
    switch (meth) {
    case COUNTMETH_ALL_1        : countfunc=call1;         break;
    case COUNTMETH_ALL_DIF      : countfunc=calldif;       break;
    case COUNTMETH_ALL_FIXDIF   : countfunc=callfixdif;    break;
    case COUNTMETH_JUMP_1       : countfunc=cjump1;        break;
    case COUNTMETH_JUMP_21      : countfunc=cjump21;       break;
    case COUNTMETH_JUMP_DIF     : countfunc=cjumpdif;      break;
    case COUNTMETH_JUMP_DIFSH   : countfunc=cjumpdifsh;    break;
    case COUNTMETH_JUMP_FIXDIF  : countfunc=cjumpfixdif;   break;
    case COUNTMETH_JUMP_FIXDIFSH: countfunc=cjumpfixdifsh; break;
    case COUNTMETH_HAMMAP_GS:
    case COUNTMETH_HAMMAP_CUBIC:
	if (xmode!=HAM)
		error0(E0_FATAL, E1_IFF, E2_OPTION, E3_ITMETH);
	count_pixels(firstrow, lastrow, 1);
	fill_curcm(meth);
	fill_newcol_count();
	countfunc=chammap;
    break;
    default:
	error0(E0_FATAL, E1_IFF, E2_OPTION, E3_COUNTMETH);
    break;
    }
    clear_counts();
    for (row=firstrow; row<=lastrow; ++row) {
        countfunc(row);
    }
}
