/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: colormap.c 
 * Date: 01/30/91
 * 
 * Description:
 *   This file contains the functions necessary to setup a 
 *   pseudo colormap.
 *
 * Notes:
 *   1. The functions contains in this file are built upon the
 *      Xlib so it is Toolkit independent.
 *   2. The function VtSetPseudoColormap initializes several basic
 *      X parameters in static variables declared at the top of 
 *      page. These "saved" parameters are then reqused by the
 *      public function "get_pix".
 *
 * Revisions:
 ****************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <math.h>

#include "VtP.h"

/****************************************************************
 *                 PRIVATE DEFINTIONS
 ****************************************************************/

typedef struct {
	int r,g,b ;
} ColorEntry ;

static Display *saved_dpy ;
static Visual *saved_visual ;
static Colormap def_cmap ;
static unsigned long *pix_map ;
static ColorEntry *actual_colors ;
static unsigned long black ;
static unsigned long white ;
static int num_red ;
static int num_green ;
static int num_blue ;
static int baseR ;
static int baseG ;
static int baseB ;

/****************************************************************
 *                 PRIVATE FUNCTIONS
 ****************************************************************/

PRIVATE
int
find_base(n)
	unsigned long n ;
{
	int i = 0 ;
	while(!((n >> i) & 1))
	{
		i++ ;
	}
	return i ;
}

PRIVATE
int
int_scale(r,scl1,scl2)
{
	return r * scl1 / scl2 ;
}

PRIVATE
int
int_scale_ru(r,scl1,scl2)
{
	int val = int_scale(r,scl1,scl2) + 1 ;
	return (val == scl1) ? (scl1 - 1) : val ;
}

/****************************************************************
 *                 PUBLIC FUNCTIONS
 ****************************************************************/

/****************************************************************
 * Function: get_pix  
 * Date: 01/30/91
 *
 * Description:
 *   This function returns the actual rgb values given a set
 *   of rgb values.
 *
 * Linkage: unsigned lon get_pix(r,g,b,actual_r,actual_g,actual_b)
 *   int r         - red value
 *   int g         - green value
 *   int b         - blue value
 *   int *actual_r - the actual red value (returned)
 *   int *actual_g - the actual green value (returned)
 *   int *actual_b - the actual blue value (returned)
 *
 * Revisions:
 ****************************************************************/
PUBLIC
unsigned long
get_pix(r,g,b,actual_r,actual_g,actual_b)
	register int r,g,b ;
	int *actual_r ;
	int *actual_g ;
	int *actual_b ;
{

	if (r > 255) r = 255 ;
	else if (r < 0) r = 0 ;
	if (g > 255) g = 255 ;
	else if (g < 0) g = 0 ;
	if (b > 255) b = 255 ;
	else if (b < 0) b = 0 ;

	if (r == 255 && g == 255 && b == 255)
	{
		*actual_r =
		*actual_g =
		*actual_b = 0 ;
		return white ;
	}
	else if (!r && !g && !b)
	{
		*actual_r =
		*actual_g =
		*actual_b = 0 ;
		return black ;
	}
	else if (saved_visual->class == StaticGray)
	{
		int val = (299 * r + 587 * g + 114 * b) / 1000 ;
		*actual_r = r - val ;
		*actual_g = g - val ;
		*actual_b = b - val ;
		return int_scale(val,saved_visual->map_entries,256) ;
	}
	else if (saved_visual->red_mask && saved_visual->green_mask && saved_visual->blue_mask)
	{
		register unsigned long R = int_scale(r,num_red,256) ;
		register unsigned long G = int_scale(g,num_green,256) ;
		register unsigned long B = int_scale(b,num_blue,256) ;
		*actual_r = r - int_scale(R,256,num_red) ;
		*actual_g = g - int_scale(G,256,num_green) ;
		*actual_b = b - int_scale(B,256,num_blue) ;
		return (R << baseR) | (G << baseG) | (B << baseB) ;
	}
	else if (pix_map != NULL)
	{
		register int R = int_scale(r,num_red,256) ;
		register int G = int_scale(g,num_green,256) ;
		register int B = int_scale(b,num_blue,256) ;
		int pos = B + num_blue * (G + num_green * R) ;
		*actual_r = r - actual_colors[pos].r ;
		*actual_g = g - actual_colors[pos].g ;
		*actual_b = b - actual_colors[pos].b ;
		return pix_map[pos] ;
	}
	else
	{
		XColor col ;
		col.flags = DoRed | DoGreen | DoBlue ;
		col.red = int_scale(r,65536,256) ;
		col.green = int_scale(g,65536,256) ;
		col.blue = int_scale(b,65536,256) ;
		XAllocColor(saved_dpy,def_cmap,&col) ;
		*actual_r = r - int_scale(col.red,256,65536) ;
		*actual_g = g - int_scale(col.green,256,65536) ;
		*actual_b = b - int_scale(col.blue,256,65536) ;
		return col.pixel ;
	}
}

/****************************************************************
 * Function: VtSetPseudoColormap
 * Date: 01/30/91
 *
 * Description:
 *   This function sets the pseudo colormap and also initializes
 *   the private static parameters declared above.
 *
 * Linkage: VtSetPseudoColormap(dpy)
 *   Display *dpy - the X display pointer
 *
 * Revisions:
 ****************************************************************/
PUBLIC
void
VtSetPseudoColormap(dpy)
	Display *dpy ;
{
	int depth;
	int screen ;
	Visual *visual ;
	XColor col ;
	int npix ;
	int r ;

	/* determine basic X parameters */
	screen = DefaultScreen(dpy);
	visual = DefaultVisual(dpy,screen);
	depth = DefaultDepth(dpy,screen);
	def_cmap = DefaultColormap(dpy,screen);

	saved_dpy = dpy ;
	saved_visual = visual ;
	white = WhitePixel(saved_dpy,screen) ;
	black = BlackPixel(saved_dpy,screen) ;
	if (visual->red_mask && visual->green_mask && visual->blue_mask)
	{
		baseR = find_base(visual->red_mask) ;
		baseG = find_base(visual->green_mask) ;
		baseB = find_base(visual->blue_mask) ;
		num_red = (visual->red_mask >> baseR) + 1 ;
		num_green = (visual->green_mask >> baseG) + 1 ;
		num_blue = (visual->blue_mask >> baseB) + 1 ;
		return ;
	}
	if (visual->class == StaticGray)
	{
		return ;
	}
	if (depth <= 4)
	{
		return ;
	}
	npix = visual->map_entries ;
	num_green = num_red = num_blue = pow((double)npix,1 / 3.0) ;

	pix_map = (unsigned long *)malloc((unsigned)(npix * sizeof(unsigned long))) ;
	actual_colors = (ColorEntry *)malloc((unsigned)(npix * sizeof(ColorEntry))) ;
	col.flags = DoRed | DoGreen | DoBlue ;

	for(r = 0; r < num_red; r++)
	{
		int g ;
		int real_r = int_scale_ru(r,65536,num_red) ;
		for(g = 0; g < num_green; g++)
		{
			int b ;
			int real_green = int_scale_ru(g,65536,num_green) ;
			for(b = 0; b < num_blue; b++)
			{
				int pos ;
				col.red = real_r ;
				col.green = real_green ;
				col.blue = int_scale_ru(b,65536,num_blue) ;
				XAllocColor(dpy,def_cmap,&col) ;
				pos = b + num_blue * (g + num_green * r) ;
				pix_map[pos] = col.pixel ;
				actual_colors[pos].r = int_scale(col.red,256,65536) ;
				actual_colors[pos].g = int_scale(col.green,256,65536) ;
				actual_colors[pos].b = int_scale(col.blue,256,65536) ;
			}
		}
	}
}
