/*
 * 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: get_img_pixel.c
 * Date: 04/02/91
 *
 * Description:
 *   This file contains several functions for handling X images.
 *
 * Revisions:
 * 05/09/91 (BDM) - removed dependence on vt context.
 ****************************************************************/
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <rast_img.h>
#include <malloc.h>
#include <math.h>

#include "VtP.h"

#define MaxGrey 256
#define Threshold (MaxGrey / 2)

/****************************************************************
 *           Pixel structire for 24 bit images
 ****************************************************************/
typedef struct {
	unsigned char r ;
	unsigned char g ;
	unsigned char b ;
} Pix24 ;

/****************************************************************
 *                  Private Functions 
 ****************************************************************/

/* Allocates a pixel via XAllocColor */
PRIVATE
unsigned long
alloc_pix(dpy,cmap,r,g,b)
	Display *dpy;
	Colormap cmap;
	int r;
	int g;
	int b;
{
	XColor col;

	col.red = r;
	col.green = g;
	col.blue = b;
	col.flags = DoRed | DoGreen | DoBlue;

	XAllocColor(dpy,cmap,&col);

	return col.pixel;
}

/* Creates an image and all data necessary to contain it */
PRIVATE
XImage *
YCreateImage(dpy,size_x,size_y)
	Display *dpy;
	int size_x;
	int size_y;
{
	Visual *visual = DefaultVisual(dpy,DefaultScreen(dpy));
	unsigned depth = DefaultDepth(dpy,DefaultScreen(dpy));

	XImage *image = XCreateImage(dpy,visual,depth,ZPixmap,0,
		(XtPointer)NULL,(unsigned)size_x,(unsigned)size_y,16,0);
	unsigned size = image->bytes_per_line * image->height;
	char *p = malloc(size);
	image->data = p;
	/* bug fix for DEC byte swapping problem (trial basis) */
	image->bitmap_bit_order = image->byte_order = MSBFirst;
	return image;
}

/* Just do it !  Helps appearance */
PRIVATE
int
tone_scale_adjust(val)
{
	return val ;
/*
		(val < Threshold) ?
			val / 2 :
			(((val - Threshold) * (MaxGrey - Threshold / 2)) /
				(MaxGrey - Threshold)) + Threshold / 2 ;
*/
}

/* Retrieves the base bit index of a binary value */
PRIVATE
int
find_base(n)
        unsigned long n ;
{
        int i = 0 ;
        while(!((n >> i) & 1))
        {
                i++ ;
        }
        return i ;
}

/****************************************************************
 *                  Public Functions 
 ****************************************************************/

/* Creates an image and a new colormap from an img */
PUBLIC
void
VtCreateCmapPixmapFromImg(dpy,img,pix,cmap,size_x,size_y)
	Display *dpy;
	Rast_Img *img;
	XImage **pix;
	Colormap *cmap;
	int size_x;
	int size_y;
{
	XImage *image = YCreateImage(dpy,size_x,size_y) ;
	Visual *visual  = DefaultVisual(dpy,DefaultScreen(dpy));
	Window root_window = DefaultRootWindow(dpy);
	unsigned long pix_cmap[256] ;
	int img_width = img->width ;
	int img_height = img->height ;
	int img_depth = img->depth ;

	/* Create the colormap */
	*cmap = XCreateColormap(dpy,root_window,visual,AllocNone) ;

	/* If an RGB image then allocate every color directly */
	if (img->cmap_type == CMAP_RGB)
	{
		int i ;
		int map_size = img->cmap_data.rgb.map_size ;
		for(i = 0; i < map_size; i++)
		{
			int r = img->cmap_data.rgb.map[i].r ;
			int g = img->cmap_data.rgb.map[i].g ;
			int b = img->cmap_data.rgb.map[i].b ;
			pix_cmap[i] = alloc_pix(dpy,*cmap,
				(r * 65536) / 256,(g * 65536) / 256,(b * 65536) / 256) ;
		}
	}

	/* If a gray scale image then allocate all shades of gray */
	else
	{
		int i ;
		int map_size = 256 ;
		for(i = 0; i < map_size; i++)
		{
			int level = i * 65536 / map_size ;
			pix_cmap[i] = alloc_pix(dpy,*cmap,level,level,level) ;
		}
	}

	/* Now translate the pixels into X format */
	{
		register int line_len = ((img_width * img_depth + 15) / 16) * 2 ;
		register unsigned char *pos = img->data ;
		register int i ;
		for(i = 0; i < size_y; i++)
		{
			register int j ;
			for(j = 0 ; j < size_x; j++)
			{
				int x = j * img_width / size_x ;
				int y = i * img_height / size_y ;
				XPutPixel(image,j,i,pix_cmap[pos[y * line_len + x]]) ;
			}
		}
	}

	*pix = image ;
}

/* Creates an image using the default colormap */
PUBLIC
void
VtCreateSubstPixmapFromImg(dpy,img,pix,size_x,size_y)
	Display *dpy;
	Rast_Img *img;
	XImage **pix;
	int size_x;
	int size_y;
{
	XImage *image = YCreateImage(dpy,size_x,size_y) ;
	int img_width = img->width ;
	int img_height = img->height ;
	int img_depth = img->depth ;

	/* Now transfer the image into X format */
	{
		int line_len = ((img_width * img_depth + 15) / 16) * 2 ;
		unsigned char *pos = img->data ;

		/* If we have a bitmap */
		if (img_depth == 1)
		{
			register unsigned long white ;
			register unsigned long black ;
			register int i ;

			/* Get the meanings of 1 and 0.  Colormap useful for systems where black and white are reversed */
			if (img->cmap_type == CMAP_NONE)
			{
				white = WhitePixel(dpy,DefaultScreen(dpy)) ;
				black = BlackPixel(dpy,DefaultScreen(dpy)) ;
			}
			else
			{
				struct rgb *map = img->cmap_data.rgb.map ;
				int ar,ag,ab ;
				unsigned long get_pix() ;
				white = get_pix(map[0].r,map[0].g,map[0].b,&ar,&ag,&ab) ;
				black = get_pix(map[1].r,map[1].g,map[1].b,&ar,&ag,&ab) ;
			}
			for(i = 0; i < size_y; i++)
			{
				register int j ;
				for(j = 0 ; j < size_x; j++)
				{
					int x = j * img_width / size_x ;
					int y = i * img_height / size_y ;
					XPutPixel(image,j,i,
						((pos[y * line_len + x / 8] >> (7 - x % 8)) & 1) ?
							black : white) ;
				}
			}
		}

		/* If we have an 8 bit image just substitute in */
		else if (img_depth == 8)
		{
			{
			register int i ;
			char *calloc() ;
			typedef struct { int r,g,b; } Tuple ;
			Tuple *buf = (Tuple *)calloc((unsigned)((size_x + 2) * 2),sizeof(Tuple)) ;
			Tuple *next = buf + 1 ;
			Tuple *curr = next + size_x + 2 ;
			register int k = 0 ;
			int direction = 1 ;
			struct rgb *map = img->cmap_data.rgb.map ;
			for(i = 0; i < size_y; i++)
			{
				register int j ;
				for(j = 0 ; j < size_x; j++,k += direction)
				{
					int x = k * img_width / size_x ;
					int y = i * img_height / size_y ;
					Tuple PictureIntensity ;
					Tuple Error ;
					unsigned long get_pix() ;

					if (img->cmap_type == CMAP_RGB)
					{
						struct rgb *pixel = map + pos[y * line_len + x] ;
						PictureIntensity.r = pixel->r + curr[k].r ;
						PictureIntensity.g = pixel->g + curr[k].g ;
						PictureIntensity.b = pixel->b + curr[k].b ;
					}
					else
					{
						int val = pos[y * line_len + x] ;
						PictureIntensity.r = val + curr[k].r ;
						PictureIntensity.g = val + curr[k].g ;
						PictureIntensity.b = val + curr[k].b ;
					}

					XPutPixel(image,k,i,get_pix(PictureIntensity.r,PictureIntensity.g,PictureIntensity.b,&Error.r,&Error.g,&Error.b)) ;

					curr[k + direction].r += (Error.r * 3) / 8 ;
					next[k].r += (Error.r * 3) / 8 ;
					next[k + direction].r += Error.r / 4 ;
					curr[k + direction].g += (Error.g * 3) / 8 ;
					next[k].g += (Error.g * 3) / 8 ;
					next[k + direction].g += Error.g / 4 ;
					curr[k + direction].b += (Error.b * 3) / 8 ;
					next[k].b += (Error.b * 3) / 8 ;
					next[k + direction].b += Error.b / 4 ;
				}
				direction = -direction ;
				k += direction ;
				{
					Tuple *t = next ;
					next = curr ;
					curr = t ;
					bzero((char *)(next - 1),(size_x + 2) * sizeof(Tuple)) ;
				}
			}
			free((char *)buf) ;
			}
		}

		/* Else we have a 24 bit image.  Just substitute in */
		else
		{
			register int i ;
			char *calloc() ;
			typedef struct { int r,g,b; } Tuple ;
			Tuple *buf = (Tuple *)calloc((unsigned)((size_x + 2) * 2),sizeof(Tuple)) ;
			Tuple *next = buf + 1 ;
			Tuple *curr = next + size_x + 2 ;
			register int k = 0 ;
			int direction = 1 ;
			for(i = 0; i < size_y; i++)
			{
				register int j ;
				for(j = 0 ; j < size_x; j++,k += direction)
				{
					unsigned long get_pix() ;
					int x = k * img_width / size_x ;
					int y = i * img_height / size_y ;
					Pix24 *pixel = ((Pix24 *)(pos + y * line_len)) + x ;
					Tuple PictureIntensity ;
					Tuple Error ;
					PictureIntensity.r = pixel->r + curr[k].r ;
					PictureIntensity.g = pixel->g + curr[k].g ;
					PictureIntensity.b = pixel->b + curr[k].b ;

					XPutPixel(image,k,i,get_pix(PictureIntensity.r,PictureIntensity.g,PictureIntensity.b,&Error.r,&Error.g,&Error.b)) ;

					curr[k + direction].r += (Error.r * 3) / 8 ;
					next[k].r += (Error.r * 3) / 8 ;
					next[k + direction].r += Error.r / 4 ;
					curr[k + direction].g += (Error.g * 3) / 8 ;
					next[k].g += (Error.g * 3) / 8 ;
					next[k + direction].g += Error.g / 4 ;
					curr[k + direction].b += (Error.b * 3) / 8 ;
					next[k].b += (Error.b * 3) / 8 ;
					next[k + direction].b += Error.b / 4 ;
				}
				direction = -direction ;
				k += direction ;
				{
					Tuple *t = next ;
					next = curr ;
					curr = t ;
					bzero((char *)(next - 1),(size_x + 2) * sizeof(Tuple)) ;
				}
			}
			free((char *)buf) ;
		}
	}

	*pix = image ;
}

/* Creates a dithered image from an img */
PUBLIC
void
VtCreateDitheredPixmapFromImg(dpy,img,pix,size_x,size_y)
	Display *dpy;
	Rast_Img *img;
	XImage **pix;
	int size_x;
	int size_y;
{
	int screen = DefaultScreen(dpy);
	Visual *visual = DefaultVisual(dpy,DefaultScreen(dpy));
	unsigned depth = DefaultDepth(dpy,DefaultScreen(dpy));
	XImage *image = YCreateImage(dpy,size_x,size_y) ;
	int img_width = img->width ;
	int img_height = img->height ;
	int img_depth = img->depth ;
	int line_len = ((img_width * img_depth + 15) / 16) * 2 ;
	unsigned char *pos = img->data ;
	int direction = 1 ;
	unsigned long white = WhitePixel(dpy,screen) ;
	unsigned long black = BlackPixel(dpy,screen) ;
	char *calloc() ;
	int *buf = (int *)calloc((unsigned)((size_x + 2) * 2),sizeof(int)) ;
	int *next = buf + 1 ;
	int *curr = next + size_x + 2 ;
	{
		if (img_depth == 8)
		{
			if (img->cmap_type == CMAP_RGB)
			{
				register int i ;
				register int k = 0 ;
				register struct rgb *map = img->cmap_data.rgb.map ;
				register int map_entries = visual->map_entries ;
				for(i = 0; i < size_y; i++)
				{
					register int j ;
					for(j = 0 ; j < size_x; j++,k += direction)
					{
						int x = k * img_width / size_x ;
						int y = i * img_height / size_y ;
						struct rgb *ps = map + pos[y * line_len + x] ;
						register int PictureIntensity =
							tone_scale_adjust((int)((299 * ps->r +
							587 * ps->g +
							114 * ps->b) / 1000 * MaxGrey / 256)) + curr[k] ;
						register int DeviceIntensity ;
						register int Error ;
						if (depth == 1)
						{
							DeviceIntensity = (PictureIntensity > Threshold) ? MaxGrey : 0 ;
							XPutPixel(image,k,i,DeviceIntensity ? white : black) ;
						}
						else
						{
							unsigned long pix = (PictureIntensity * map_entries) / MaxGrey ;
							DeviceIntensity = pix * MaxGrey / map_entries ;
							XPutPixel(image,k,i,
								pix < 0 ? 0 :
								pix >= map_entries ? map_entries - 1 :
								pix) ;
						}
						Error = PictureIntensity - DeviceIntensity ;
						curr[k + direction] += (Error * 3) / 8 ;
						next[k] += (Error * 3) / 8 ;
						next[k + direction] += Error / 4 ;
					}
					direction = -direction ;
					k += direction ;
					{
						int *t = next ;
						next = curr ;
						curr = t ;
						bzero((char *)(next - 1),(size_x + 2) * sizeof(int)) ;
					}
				}
			}
			else
			{
				register int i ;
				register int k = 0 ;
				register int map_entries = visual->map_entries ;
				for(i = 0; i < size_y; i++)
				{
					register int j ;
					for(j = 0 ; j < size_x; j++,k += direction)
					{
						int x = k * img_width / size_x ;
						int y = i * img_height / size_y ;
						register int PictureIntensity = tone_scale_adjust((int)(pos[y * line_len + x] * MaxGrey / 256)) +
							curr[k] ;
						register int DeviceIntensity ;
						register int Error ;
						if (depth == 1)
						{
							DeviceIntensity = (PictureIntensity > Threshold) ? MaxGrey : 0 ;
							XPutPixel(image,k,i,DeviceIntensity ? white : black) ;
						}
						else
						{
							unsigned long pix = (PictureIntensity * map_entries) / MaxGrey ;
							DeviceIntensity = pix * MaxGrey / map_entries ;
							XPutPixel(image,k,i,
								pix < 0 ? 0 :
								pix >= map_entries ? map_entries - 1 :
								pix) ;
						}
						Error = PictureIntensity - DeviceIntensity ;
						curr[k + direction] += (Error * 3) / 8 ;
						next[k] += (Error * 3) / 8 ;
						next[k + direction] += Error / 4 ;
					}
					direction = -direction ;
					k += direction ;
					{
						int *t = next ;
						next = curr ;
						curr = t ;
						bzero((char *)(next - 1),(size_x + 2) * sizeof(int)) ;
					}
				}
			}
		}
		else
		{
			if (img->cmap_type == CMAP_NONE)
			{
				register int i ;
				register int k = 0 ;
				register int map_entries = visual->map_entries ;
				for(i = 0; i < size_y; i++)
				{
					register int j ;
					for(j = 0 ; j < size_x; j++,k += direction)
					{
						int x = k * img_width / size_x ;
						int y = i * img_height / size_y ;
						Pix24 *xp = ((Pix24 *)(pos + y * line_len)) + x ;
						register int PictureIntensity =
							tone_scale_adjust((int)((299 * xp->r +
							587 * xp->g +
							114 * xp->b) / 1000 * MaxGrey / 256)) + curr[k] ;
						register int DeviceIntensity ;
						register int Error ;
						if (depth == 1)
						{
							DeviceIntensity = (PictureIntensity > Threshold) ? MaxGrey : 0 ;
							XPutPixel(image,k,i,DeviceIntensity ? white : black) ;
						}
						else
						{
							unsigned long pix = (PictureIntensity * map_entries) / MaxGrey ;
							DeviceIntensity = pix * MaxGrey / map_entries ;
							XPutPixel(image,k,i,
								pix < 0 ? 0 :
								pix >= map_entries ? map_entries - 1 :
								pix) ;
						}
						Error = PictureIntensity - DeviceIntensity ;
						curr[k + direction] += (Error * 3) / 8 ;
						next[k] += (Error * 3) / 8 ;
						next[k + direction] += Error / 4 ;
					}
					direction = -direction ;
					k += direction ;
					{
						int *t = next ;
						next = curr ;
						curr = t ;
						bzero((char *)(next - 1),(size_x + 2) * sizeof(int)) ;
					}
				}
			}
		}
	}
	free((char *)buf) ;
	*pix = image ;
}

/* Retrieves an img from and XImage structure */
PUBLIC
void
VtGetImgFromImage(dpy,image,img)
	Display *dpy;
	XImage *image;
	Rast_Img *img;
{
	int line_len;
	img->height = image->height;
	img->width = image->width;

	/* If there is a mask then assume full rgb */
	if (image->red_mask && image->green_mask && image->blue_mask)
	{
		img->depth = 24 ;
	}

	/* Otherwise do a color map */
	else
	{
		img->depth = (image->depth == 1) ? 1 : 8 ;
	}
	line_len = ((img->width * img->depth + 15) / 16) * 2 ;
	img->data = (unsigned char *)calloc((unsigned)line_len,(unsigned)img->height) ;

	/* If full rgb */
	if (img->depth == 24)
	{
		int i ;
		int base_r = find_base(image->red_mask) ;
		int base_g = find_base(image->green_mask) ;
		int base_b = find_base(image->blue_mask) ;
		int num_blue = (image->blue_mask >> base_b) + 1 ;
		int num_green = (image->green_mask >> base_g) + 1 ;
		int num_red = (image->red_mask >> base_r) + 1 ;
		unsigned char *pos = img->data ;
		img->cmap_type = CMAP_NONE ;
		for(i = 0; i < img->height; i++,pos += line_len)
		{
			int j ;
			Pix24 *p3 = (Pix24 *)pos ;
			for(j = 0; j < img->width; j++,p3++)
			{
				unsigned long pix = XGetPixel(image,j,i) ;
				p3->r = ((pix & image->red_mask) >> base_r) * 256 / num_red ;
				p3->g = ((pix & image->green_mask) >> base_g) * 256 / num_green ;
				p3->b = ((pix & image->blue_mask) >> base_b) * 256 / num_blue ;
			}
		}
	}

	/* If regular colormap */
	else if (img->depth == 8)
	{
		Colormap cmap = DefaultColormap(dpy,DefaultScreen(dpy));
		Visual *visual = DefaultVisual(dpy,DefaultScreen(dpy));
		int i ;
		unsigned char *pos = img->data ;
		img->cmap_type = CMAP_RGB ;
		img->cmap_data.rgb.map_size = visual->map_entries ;
		img->cmap_data.rgb.map = (struct rgb *)malloc((unsigned)(visual->map_entries * sizeof(struct rgb))) ;

		/* Query the colormap */
		for(i = 0; i < visual->map_entries; i++)
		{
			XColor col ;
			col.pixel = i ;
			col.flags = DoRed | DoGreen | DoBlue ;
			XQueryColor(dpy,cmap,&col) ;
			img->cmap_data.rgb.map[i].r = col.red * 256 / 65536 ;
			img->cmap_data.rgb.map[i].g = col.green * 256 / 65536 ;
			img->cmap_data.rgb.map[i].b = col.blue * 256 / 65536 ;
		}

		for(i = 0; i < img->height; i++,pos += line_len)
		{
			int j ;
			unsigned char *p3 = pos ;
			for(j = 0; j < img->width; j++)
			{
				unsigned long pix = XGetPixel(image,j,i) ;
				*p3++ = pix ;
			}
		}
	}

	/* If 1 bit, translate each pixel into 1 for black and 0 for white */
	else
	{
		int i ;
		unsigned long black = BlackPixel(dpy,DefaultScreen(dpy)) ;
		unsigned char *pos = img->data ;
		img->cmap_type = CMAP_NONE ;
		for(i = 0; i < img->height; i++,pos += line_len)
		{
			int j ;
			for(j = 0; j < img->width; j++)
			{
				unsigned long pix = XGetPixel(image,j,i) ;
				if (pix == black)
				{
					pos[j / 8] |= (1 << (7 - (j % 8))) ;
				}
			}
		}
	}
}
