/*
 * 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.
 */
/*
 *		Routines to read an write raster files to and from images.
 *		At present, images must be Z-Pixmap and only rasterfiles of depth
 *		less than that of the screen can be read in.
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <fcntl.h>
#include <malloc.h>
#include <rast_img.h>

#define RAS_MAGIC 0x59a66a95
#define RT_OLD          0
#define RT_STANDARD     1
#define RT_BYTE_ENCODED 2
#define RMT_NONE        0
#define RMT_EQUAL_RGB   1

struct rasterfile
{
	int ras_magic ;
	int ras_width ;
	int ras_height ;
	int ras_depth ;
	int ras_length ;
	int ras_type ;
	int ras_maptype ;
	int ras_maplength ;
} ;

/*
 *	Reads in rasterfile data from file.
 */
static
unsigned char *
read_raster_data(fd,length,type,size)
{
	/* Read in image data */
	unsigned char *file_data = (unsigned char *)malloc((unsigned)length) ;
	read(fd,(char *)file_data,length) ;

	/* If run length encoded then decode image data */
	if (type == RT_BYTE_ENCODED)
	{
		unsigned char *data = (unsigned char *)malloc((unsigned)size) ;
		register unsigned char *p1 = data ;
		register unsigned char *p2 = file_data ;
		register unsigned char *limit = p2 + length ;
		while(p2 < limit)
		{
			{
				unsigned char c = *p2++ ;
				if (c != 128)
				{
					*p1++ = c ;
					continue ;
				}
			}
			{
				register unsigned char count = *p2++ ;
				if (!count)
				{
					*p1++ = 128 ;
				}
				else
				{
					register unsigned char run_char = *p2++ ;
					do
					{
						*p1++ = run_char ;
					}
					while(count--) ;
				}
			}
		}

		/* Don't need original any longer */
		free((char *)file_data) ;

		/* If we did not use up all of the data then something is wrong */
		if (p1 - data != size)
		{
			free((char *)data) ;
			return NULL ;
		}

		/* Give back the data */
		return data ;
	}

	/* Give back the original */
	return file_data ;
}

static
get_token(fp,str,l)
	FILE *fp ;
	char *str ;
{
	for(;;)
	{
		int c ;
		while((c = getc(fp)) == ' ' || c == '\t' || c == '\n') ;
		if (c == '#')
		{
			while(c != '\n' && c >= 0)
			{
				c = getc(fp) ;
			}
		}
		else
		{
			while(c != ' ' && c != '\n' && c != '\t' && c >= 0)
			{
				if (l > 1)
				{
					*str++ = c ;
					l-- ;
				}
				c = getc(fp) ;
			}
			*str = 0 ;
			return ;
		}
	}
}

static
get_int_token(fp)
	FILE *fp ;
{
	char str[256] ;
	get_token(fp,str,sizeof str) ;
	return atoi(str) ;
}

static
read_ppm_file(fd,img)
	Rast_Img *img ;
{
	FILE *fp = fdopen(fd,"r") ;
	int width = get_int_token(fp) ;
	int height = get_int_token(fp) ;
	int max_color = get_int_token(fp) ;
	int i ;
	int line_len = (width * 24 + 15) / 16 * 2 ;
	int size = line_len * height ; 
	img->width = width ;
	img->height = height ;
	img->depth = 24 ;
	img->cmap_type = CMAP_NONE ;
	img->data = (unsigned char *)malloc(size) ;
	for(i = 0; i < height; i++)
	{
		int j ;
		for(j = 0; j < width; j++)
		{
			unsigned char *pos = img->data + i * line_len + j * 3 ;
			pos[0] = get_int_token(fp)  * 256 / (max_color + 1) ;
			pos[1] = get_int_token(fp)  * 256 / (max_color + 1) ;
			pos[2] = get_int_token(fp)  * 256 / (max_color + 1) ;
		}
	}
	fclose(fp) ;
}

static
read_bin_ppm_file(fd,img)
	Rast_Img *img ;
{
	FILE *fp = fdopen(fd,"r") ;
	int width = get_int_token(fp) ;
	int height = get_int_token(fp) ;
	int max_color = get_int_token(fp) ;
	int i ;
	int line_len = (width * 24 + 15) / 16 * 2 ;
	int size = line_len * height ; 
	img->width = width ;
	img->height = height ;
	img->depth = 24 ;
	img->cmap_type = CMAP_NONE ;
	img->data = (unsigned char *)malloc(size) ;
	for(i = 0; i < height; i++)
	{
		int j ;
		for(j = 0; j < width; j++)
		{
			unsigned char *pos = img->data + i * line_len + j * 3 ;
			pos[0] = getc(fp)  * 256 / (max_color + 1) ;
			pos[1] = getc(fp)  * 256 / (max_color + 1) ;
			pos[2] = getc(fp)  * 256 / (max_color + 1) ;
		}
	}
	fclose(fp) ;
}

read_rast_img_fd(fd,img)
	Rast_Img *img ;
{
	int size ;
	int line_len ;
	int depth ;
	int bits_per_pixel ;
	struct rasterfile rasterfile ;
	char ppm_header[2] ;
	read(fd,ppm_header,sizeof(ppm_header)) ;
	if (ppm_header[0] == 'P' && ppm_header[1] == '3')
	{
		read_ppm_file(fd,img) ;
		return 0 ;
	}
	else if (ppm_header[0] == 'P' && ppm_header[1] == '6')
	{
		read_bin_ppm_file(fd,img) ;
		return 0 ;
	}

	/* Read in the header */
	lseek(fd,0L,L_SET) ;
	(void)read(fd,(char *)&rasterfile,sizeof rasterfile) ;

	/* If not rasterfile then forget it */
	if (rasterfile.ras_magic != RAS_MAGIC)
	{
		return -1 ;
	}

	/* If some stupid encoding then forget it */
	if (rasterfile.ras_type != RT_STANDARD &&
		rasterfile.ras_type != RT_BYTE_ENCODED)
	{
		return -2 ;
	}

	/* Get depth */
	depth = rasterfile.ras_depth ;

	/* Even though depth may not be 1, 8 or 24, bits per pixel always is */
	bits_per_pixel =
		(depth == 1) ?
			1 :
		(depth <= 8) ?
			8 :
		(depth <= 24) ?
			24 :
			32 ;

	/* Get # bits per line */
	line_len = ((rasterfile.ras_width * bits_per_pixel + 15) / 16) * 2 ;
	size = rasterfile.ras_height * line_len ;

	if (rasterfile.ras_type == RT_STANDARD && size != rasterfile.ras_length)
	{
		fprintf(stderr,"Warning: Rasterfile ras_length field invalid, substituting %d\n",size) ;
		rasterfile.ras_length = size ;
	}

	/* If a color map is present */
	if (rasterfile.ras_maplength > 0)
	{
		int map_length = rasterfile.ras_maplength ;
		unsigned char *cmap = (unsigned char *)malloc((unsigned)map_length) ;

		/* Read it in */
		read(fd,(char *)cmap,map_length) ;

		/* We can only interpret equal_rgb at this time */
		if (rasterfile.ras_maptype == RMT_EQUAL_RGB)
		{
			int i ;
			img->cmap_type = CMAP_RGB ;
			img->cmap_data.rgb.map_size = map_length / 3 ;
			img->cmap_data.rgb.map = (struct rgb *)malloc((unsigned)(img->cmap_data.rgb.map_size * sizeof(struct rgb))) ;
			for(i = 0; i < img->cmap_data.rgb.map_size; i++)
			{
				img->cmap_data.rgb.map[i].r = cmap[i] ;
				img->cmap_data.rgb.map[i].g = cmap[i + img->cmap_data.rgb.map_size] ;
				img->cmap_data.rgb.map[i].b = cmap[i + 2 * img->cmap_data.rgb.map_size] ;
			}
		}
		else
		{
			free((char *)cmap) ;
			return -3 ;
		}
	}

	/* If no colormap data is there */
	else
	{
		if (rasterfile.ras_maptype != RMT_NONE)
		{
			fprintf(stderr,"Warning: Unstandard RMT, assuming RMT_NONE\n") ;
		}
		img->cmap_type = CMAP_NONE ;
	}

	/* Read in image data */
	img->data = read_raster_data(fd,rasterfile.ras_length,rasterfile.ras_type,size) ;
	if (img->data == NULL)
	{
		return -6 ;
	}
	img->width = rasterfile.ras_width ;
	img->height = rasterfile.ras_height ;
	img->depth = depth ;
	return 0 ;
}

read_rast_img(filename,img)
	char *filename ;
	Rast_Img *img ;
{
	int fd = open(filename,O_RDONLY,0L) ;
	if (fd < 0)
	{
		return -5 ;
	}
	else
	{
		int rc = read_rast_img_fd(fd,img) ;
		close(fd) ;
		return rc ;
	}
}

unsigned long
get_rast_pixel_rgb(img,x,y,rgb)
	Rast_Img *img ;
	struct rgb *rgb ;
{
	unsigned char *data = img->data + (img->width * img->depth + 15) / 16 * y ;
	if (img->depth == 1)
	{
		if (data[x / 8] & (1 << (7 - x % 8)))
		{
			if (img->cmap_type == CMAP_NONE)
			{
				rgb->r = rgb->g = rgb->b = 0 ;
			}
			else
			{
				*rgb = img->cmap_data.rgb.map[1] ;
			}
		}
		else
		{
			if (img->cmap_type == CMAP_NONE)
			{
				rgb->r = rgb->g = rgb->b = 255 ;
			}
			else
			{
				*rgb = img->cmap_data.rgb.map[0] ;
			}
		}
	}
	else if (img->depth == 8)
	{
		int val = data[x] ;
		if (img->cmap_type == CMAP_NONE)
		{
			rgb->r = rgb->g = rgb->b = val ;
		}
		else
		{
			*rgb = img->cmap_data.rgb.map[val] ;
		}
	}
	else if (img->depth == 24)
	{
		*rgb = ((struct rgb *)data)[x] ;
	}
	else
	{
		rgb->r = ((struct rgba *)data)[x].r ;
		rgb->g = ((struct rgba *)data)[x].g ;
		rgb->b = ((struct rgba *)data)[x].b ;
	}
}

put_rast_pixel_1(img,x,y,val)
	Rast_Img *img ;
{
	unsigned char *data = img->data + (img->width * img->depth + 15) / 16 * y ;
	if (val)
	{
		data[x / 8] |= 1L << (7 - x % 8) ;
	}
	else
	{
		data[x / 8] &= ~(1L << (7 - x % 8)) ;

	}
}

put_rast_pixel_8(img,x,y,val)
	Rast_Img *img ;
{
	unsigned char *data = img->data + (img->width * img->depth + 15) / 16 * y ;
	data[x] |= val ;
}

put_rast_pixel_24(img,x,y,val)
	Rast_Img *img ;
	struct rgb *val ;
{
	unsigned char *data = img->data + (img->width * img->depth + 15) / 16 * y ;
	((struct rgb *)data)[x] = *val ;
}
