static char *SccsId = "@(#)cacdcmap.c 4.1 (TU-Delft) 05/24/90";
/**********************************************************

Name/Version      : cacdcmap/4.1

Language          : C
Operating system  : BSD4.2 | BSD4.3 | SYS5
Host machine      : HP9000s[358]00 | GOULD PN6000 | APOLLO DN[34]000

Author(s)         : P. Bingley
Creation date     : 18-Aug-1988
Modified by       : P. Bingley
Modification date : 10-Jan-1990


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 781708

        COPYRIGHT (C) 1988-1989, All rights reserved
**********************************************************/

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "cacdcmap.h"
#include "cmaperror.h"

/* External Declarations */
extern	int	cacdcmaperrno;
extern	float	_cacdcmap3[];
extern	float	_cacdcmap5[];
extern	float	_cacdcmap7[];

/* Global Declarations */
int	InitCacdCmap();
int	CreateGCforCmap();
int	CoupleIdsToColors();
int	CoupleIdToColor();
int	CoupleIdsToRGBs();
int	CoupleIdToRGB();
int	DecoupleIds();
int	DecoupleId();
int	SetForegroundFromIds();
int	SetForegroundFromId();
int	SetBackgroundFromIds();
int	SetBackgroundFromId();
int	GetAffectedIds();
int	GetPixelFromIds();
int	GetPixelFromId();

/* Local Declarations */
static	Display			*_Dpy;
static	int			_S_nr;
static	int			_Initialized = 0;
static	IdElem			*_Idtable = NULL;
static	int			*_pixused = NULL;
static	int			_NIds = 0;
static	int			_tabmax = 0;
static	int			_tabsize = 0;
static	Colormap		_cmap;
static	unsigned long		_basep;
static	int			_basesh;
static	int			_planes;
static	int			_contig;
static	int			_ncolors;
static	unsigned long		_planemask;
static	float			*_cacdcmap;
static	XStandardColormap	_scmap;
static	unsigned long		(*_convint2pix)();
static	unsigned long		(*_convpix2int)();

static	int		_CoupleIdToColor();
static	int		_CoupleIdToRGB();
static	int		_DecoupleId();
static	unsigned long	_GetClosestDisjunctPixel();
static	int		_BitCnt();
static	int		_ResizeIdTable();
static	int		_GetPixelFromIds();
static	unsigned long	_ContigIntToPixel();
static	unsigned long	_ContigPixelToInt();
static	unsigned long	_NonContigIntToPixel();
static	unsigned long	_NonContigPixelToInt();

/*********************************************************/


int InitCacdCmap(dpy, s_nr)
Display	*dpy;
int	s_nr;
{
    register int	i;
    register IdElem	*id;
    Atom		atom;
    int			first = 1;
    char		cmd[256];

    /* if initialized free resources */
    if(_Initialized) {
	if(dpy == _Dpy && s_nr == _S_nr) {
	    return(1);
	}
	else {
	    if(_Idtable) (void) free((char *) _Idtable);
	    if(_pixused) (void) free((char *) _pixused);
	    _Dpy = NULL;
	    _S_nr = 0;
	    _NIds = 0;
	    _tabmax = 0;
	    _tabsize = 0;
	    _Initialized = 0;
	}
    }

Again:
    /* check if the 'RGB_CACD_MAP' atom exists */
    if(atom = XInternAtom(dpy, RGB_CACD_MAP, True)) {

	/* atom exists: try to get the standard cacd colormap */
	if(XGetStandardColormap(dpy, RootWindow(dpy, s_nr), &_scmap, atom)) {

	    /* the standard cacd cmap exists ! */
	    /* set static vars */
	    _cmap = _scmap.colormap;
	    _basep = _scmap.base_pixel;
	    _basesh = _scmap.red_mult;
	    _contig = _scmap.red_max;
	    _planes = _scmap.blue_mult;
	    _planemask = _scmap.blue_max;
	    _ncolors = 1 << _planes;

#ifdef DEBUG
fprintf(stderr,
"planes = %d, mask = 0x%x, ncols = %d, basep = %u, basesh = %d, contig = %d\n",
	    _planes, _planemask, _ncolors, _basep, _basesh, _contig);
#endif

	    /* set the virtual to real colormap conversion functions */
	    if(_contig) {
		_convint2pix = _ContigIntToPixel;
		_convpix2int = _ContigPixelToInt;
	    }
	    else {
		_convint2pix = _NonContigIntToPixel;
		_convpix2int = _NonContigPixelToInt;
	    }

	    /* set float color table */
	    if(_planes == 3) _cacdcmap = _cacdcmap3;
	    else if(_planes == 5) _cacdcmap = _cacdcmap5;
	    else if(_planes == 7) _cacdcmap = _cacdcmap7;
	    else {
		cacdcmaperrno = BADCMAP;
		return(0);
	    }

	    /* create Id mapping array */
	    if((_Idtable = Calloc(TABSIZE, IdElem)) == NULL) {
		cacdcmaperrno = NOCORE;
		return(0);
	    }
	    /* init Id mapping array */
	    _tabsize = TABSIZE;
	    for(i = 0, id = _Idtable; i < _tabsize; ++i, ++id) {
		id->state = NOTUSED;
	    }

	    /* create _pixused array */
	    if((_pixused = Calloc(_ncolors, int)) == NULL) {
		cacdcmaperrno = NOCORE;
		return(0);
	    }
	    /* init _pixused array */
	    for(i = 0; i < _ncolors; ++i) {
		_pixused[i] = 0;
	    }

	    _Dpy = dpy;
	    _S_nr = s_nr;
	    _Initialized = 1;
	    return(1);
	}
    }

    /* it did not exist */
    if(!_Initialized) {
	/* if this was the first try -> try to create the cacdcmap */
	if(first) {
	    first = 0;

	    /* now try to create the cacdcmap */
	    sprintf(cmd, "setcmap -s -f -display %s", DisplayString(dpy));
	    system(cmd);

	    /* retry */
	    goto Again;
	}
	else {	/* second try: creating the cacdcmap failed */
	    cacdcmaperrno = NOCMAP;
	    return(0);
	}
    }
    /* should not get here */
    return(0);
}

int CreateGCforCmap(d, pgc)
Drawable	d;
GC		*pgc;
{
    GC gc;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    /* create graphics context */
    if(gc = XCreateGC(_Dpy, d, 0L, NULL)) {
	/* set plane mask */
	XSetPlaneMask(_Dpy, gc, _planemask);
	*pgc = gc;
	return(1);
    }
    else {	/* gc creation failed */
	cacdcmaperrno = NOGC;
	return(0);
    }
}

int CoupleIdsToColors(Ids, colors, n)
int	*Ids;
char	**colors;
int	n;
{
    register int i;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    /* for all Id's */
    for(i = 0; i < n; ++i) {
	if(!_CoupleIdToColor(Ids[i], colors[i])) return(0);
    }

    return(1);
}

int CoupleIdToColor(Id, color)
int	Id;
char	*color;
{
    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    return(_CoupleIdToColor(Id, color));
}

static int _CoupleIdToColor(Id, color)
int	Id;
char	*color;
{
    XColor	def;

    if(XParseColor(_Dpy, _cmap, color, &def)) {
	return(_CoupleIdToRGB(Id, (float) (((double) def.red) / 65535.0),
				(float) (((double) def.green) / 65535.0),
				(float) (((double) def.blue) / 65535.0)));
    }
    else {	/* color does not exist */
	cacdcmaperrno = BADCOLOR;
	return(0);
    }
}

int CoupleIdsToRGBs(Ids, Rs, Gs, Bs, n)
int	*Ids;
float	*Rs;
float	*Gs;
float	*Bs;
int	n;
{
    register int i;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    /* for all Id's */
    for(i = 0; i < n; ++i) {
	if(!_CoupleIdToRGB(Ids[i], Rs[i], Gs[i], Bs[i])) return(0);
    }

    return(1);
}

int CoupleIdToRGB(Id, R, G, B)
int	Id;
float	R;
float	G;
float	B;
{
    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    return(_CoupleIdToRGB(Id, R, G, B));
}

static int _CoupleIdToRGB(Id, R, G, B)
int	Id;
float	R;
float	G;
float	B;
{
    unsigned long pix;

    /* if legal color */
    if( R >= 0.0 && R <= 1.0 &&
        G >= 0.0 && G <= 1.0 &&
        B >= 0.0 && B <= 1.0) {

	/* do work: first check Id */
	if(Id < 0) {
	    cacdcmaperrno = BADID;
	    return(0);
	}

	/* if Id does not fit in current array: resize it */
	if(Id >= _tabsize) {
	    if(!_ResizeIdTable(Id)) return(0);
	}

	/* get best pixel value */
	pix = _GetClosestDisjunctPixel(R, G, B);

	/* set _tabmax */
	if(Id > _tabmax) _tabmax = Id;

	/* couple Id to pix */

	/* if Id was already used */
	if(_Idtable[Id].state == USED) {
	    _pixused[ (*_convpix2int) (_Idtable[Id].pixel) ]--;
	}
	else {
	    ++_NIds;
	    _Idtable[Id].state = USED;
	}
	_Idtable[Id].pixel = pix;
	_pixused[ (*_convpix2int) (pix) ]++;

#ifdef DEBUG
fprintf(stderr, "coupled Id %d to pixel %u (R = %f, G = %f, B = %f)\n",
	Id, pix, R, G, B);
#endif

	return(1);
    }
    else {	/* color no good */
	cacdcmaperrno = BADCOLOR;
	return(0);
    }
}

static unsigned long _GetClosestDisjunctPixel(R, G, B)
float R;
float G;
float B;
{
    register int		j;
    register unsigned long	i;
    register float		d;
    register float		diff;
    float			mindiff = 4.0;
    unsigned long		bestpix = 0;
    int				cnt;
    int				minbits = _planes+1;
    int				minused = MAXINT;

    /* for all colors */
    j = -1;
    for(i = 0; i < _ncolors; ++i) {
	/* calculate difference */
	d = R - _cacdcmap[++j];
	diff = d * d;
	d = G - _cacdcmap[++j];
	diff += (d * d);
	d = B - _cacdcmap[++j];
	diff += (d * d);

	/* first criteria: minimize color difference */
	if(diff < mindiff) {
	    bestpix = i;
	    mindiff = diff;
	    minbits = _BitCnt(i);
	    minused = _pixused[i];
	}
	/* second criteria: minimize pixel dependancies */
	else if(diff == mindiff) {
	    cnt = _BitCnt(i);
	    if(cnt < minbits) {
		bestpix = i;
		minbits = cnt;
		minused = _pixused[i];
	    }
	    /* third criteria: minimize usage of same pixel */
	    else if(cnt == minbits && _pixused[i] < minused) {
		bestpix = i;
		minused = _pixused[i];
	    }
	}
    }
    /* return best pixel found */
    return( (*_convint2pix) (bestpix) );
}

static int _BitCnt(pix)
unsigned long pix;
{
    register unsigned long	i;
    register int		cnt = 0;

    /* count number of one bits in pix */
    for(i = 0; i < 32; ++i) {
	if(pix & (1 << i)) ++cnt;
    }
    return(cnt);
}

static int _ResizeIdTable(Id)
int Id;
{
    register IdElem	*ntab;
    register IdElem	*last;
    int			newsize = ((Id + (2 * TABSIZE)) & ~(TABSIZE - 1));

    /* resize Id table */
    if((ntab = Realloc(_Idtable, newsize, IdElem)) == NULL) {
	cacdcmaperrno = NOCORE;
	return(0);
    }

    /* init new part of Id array */
    _Idtable = ntab;
    ntab = _Idtable + _tabsize;
    last = _Idtable + newsize;
    do {
	ntab->state = NOTUSED;
    } while(++ntab != last);

    /* set new table size */
    _tabsize = newsize;

    return(1);
}

int DecoupleIds(Ids, n)
int	*Ids;
int	n;
{
    register int i;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    /* delete Id's from the table */
    for(i = 0; i < n; ++i) {
	if(!_DecoupleId(Ids[i])) return(0);
    }

    return(1);
}

int DecoupleId(Id)
int	Id;
{
    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    return(_DecoupleId(Id));
}

static int _DecoupleId(Id)
int	Id;
{
    /* if good Id */
    if(Id >= 0 && Id <= _tabmax && _Idtable[Id].state == USED) {
	_Idtable[Id].state = NOTUSED; 
	_pixused[ (*_convpix2int) (_Idtable[Id].pixel) ]--;
	--_NIds;
	return(1);
    }
    else {	/* Id no good */
	cacdcmaperrno = BADID;
	return(0);
    }
}

int SetForegroundFromId(gc, Id)
GC	gc;
int	Id;
{
    return(SetForegroundFromIds(gc, &Id, 1));
}

int SetForegroundFromIds(gc, Ids, n)
GC	gc;
int	*Ids;
int	n;
{
    unsigned long pix;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    if(!_GetPixelFromIds(Ids, n, &pix)) return(0);

#ifdef DEBUG
fprintf(stderr, "SetForeground pix %d %x\n", pix, pix);
#endif

    XSetForeground(_Dpy, gc, pix);

    return(1);
}

int SetBackgroundFromId(gc, Id)
GC	gc;
int	Id;
{
    return(SetBackgroundFromIds(gc, &Id, 1));
}

int SetBackgroundFromIds(gc, Ids, n)
GC	gc;
int	*Ids;
int	n;
{
    unsigned long pix;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    if(!_GetPixelFromIds(Ids, n, &pix)) return(0);

    XSetBackground(_Dpy, gc, pix);

    return(1);
}

int GetAffectedIds(Ids, nids, AfIds, pnaffs)
int	*Ids;
int	nids;
int	*AfIds;
int	*pnaffs;
{
    register int	i;
    register int	naffs = 0;
    register IdElem	*id;
    unsigned long	pix;
    int			maxaffs = *pnaffs;

    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    /* retrieve combined pixel value for Id's */
    if(!_GetPixelFromIds(Ids, nids, &pix)) return(0);

    /* remove base pixel from pix (else all pixel would match) */
    pix &= ~_basep;

    /* for all Id's */
    for(i = 0, id = _Idtable; i <= _tabmax; ++i, ++id) {
	/* if this pixel is affected */
	if((id->state == USED) && (id->pixel & pix)) {
	    /* if affected id array is completely filled */
	    if(naffs >= maxaffs) {
		cacdcmaperrno = ARRAYTOSMALL;
		return(0);
	    }
	    AfIds[naffs++] = i;
	}
    }

    *pnaffs = naffs;
    return(1);
}

int GetPixelFromId(Id, pixel)
int		Id;
unsigned long	*pixel;
{
    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    return(_GetPixelFromIds(&Id, 1, pixel));
}

int GetPixelFromIds(Ids, n, pixel)
int		*Ids;
int		n;
unsigned long	*pixel;
{
    /* if not initialized */
    if(!_Initialized) {
	cacdcmaperrno = NOINIT;
	return(0);
    }

    return(_GetPixelFromIds(Ids, n, pixel));
}

static int _GetPixelFromIds(Ids, n, pixel)
int		*Ids;
int		n;
unsigned long	*pixel;
{
    register int		i;
    register int		id;
    register unsigned long	pix = 0;

    /* OR pixels associated with Id's together */
    for(i = 0; i < n; ++i) {
	id = Ids[i];
	/* if good id */
	if(id >= 0 && id <= _tabmax && _Idtable[id].state == USED) {
	    pix |= _Idtable[id].pixel;
	}
	else {	/* Id no good */
	    cacdcmaperrno = BADID;
	    return(0);
	}
    }

    *pixel = pix;
    return(1);
}

/* convert virtual cmap index ('ind') to real pixel value */
/* (for contiguous bitplanes) */
static unsigned long _ContigIntToPixel(ind)
unsigned long	ind;
{
    return((ind << _basesh) | _basep);
}

/* convert real pixel value to virtual cmap index ('ind') */
/* (for contiguous bitplanes) */
static unsigned long _ContigPixelToInt(pixel)
unsigned long	pixel;
{
    return((pixel & ~_basep) >> _basesh);
}

/* convert virtual cmap index ('ind') to real pixel value */
/* (for non-contiguous bitplanes) */
static unsigned long _NonContigIntToPixel(ind)
register unsigned long	ind;
{
    register unsigned long      pixel = 0;
    register unsigned long      real_ind = 1;
    register unsigned long      mask = _planemask;

    while(ind) {
	while(!(mask & real_ind)) real_ind <<= 1;

	if(ind & 0x1) pixel |= real_ind;

	ind >>= 1;
	real_ind <<= 1;
    }
    return(pixel | _basep);
}

/* convert virtual cmap indices ('ind') to real pixels values */
/* convert real pixel value to virtual cmap index ('ind') */
/* (for non-contiguous bitplanes) */
static unsigned long _NonContigPixelToInt(pixel)
register unsigned long	pixel;
{
    register unsigned long      vir_ind = 1;
    register unsigned long      mask = _planemask;
    register unsigned long      ind = 0;

    pixel &= ~_basep;

    while(pixel) {
	while(!(mask & 0x1)) {
	    mask >>= 1;
	    pixel >>= 1;
	}
	if(pixel & 0x1) ind |= vir_ind;

	vir_ind <<= 1;
	mask >>= 1;
	pixel >>= 1;
    }
    return(ind);
}
