/* ppmtoicr.c - convert a portable pixmap to NCSA ICR protocol
**
** Copyright (C) 1990 by Kanthan Pillay (svpillay@Princeton.EDU)
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include "ppm.h"
#include "ppmcmap.h"

#define MAXCOLORS 256
#define CLUTCOLORS 768

static int colorstobpp ARGS(( int colors ));
static int GetPixel ARGS(( int x, int y ));
static int rleit ARGS(( char* buf, char* bufto, int len ));

static pixel** pixels;
static colorhash_table cht;
static char* testimage;

int
main(argc, argv)
int argc;
char* argv[];
{
	FILE* ifp;
	int argn, rows, cols, colors, i, j, BitsPerPixel, newxsize;
	pixval maxval;
	colorhist_vector chv;
	char rgb[CLUTCOLORS];
	const char* windowname;
	char* thischar;
	char* thisline;
	char* space;
	register unsigned char c;
	register char* p;
	int display, expand;
	int rleflag, winflag;
	const char* const usage = "[-windowname windowname] [-expand expand] [-display display] [-rle] [ppmfile]";


	ppm_init( &argc, argv );

	argn = 1;
	windowname = "untitled";
	winflag = 0;
	expand = 1;
	display = 0;
	rleflag = 0;

	while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
	    {
	    if ( pm_keymatch(argv[argn],"-windowname",2) && argn + 1 < argc )
		{
		++argn;
		windowname = argv[argn];
		winflag = 1;
		}
	    else if ( pm_keymatch(argv[argn],"-expand",2) && argn + 1 < argc )
		{
		++argn;
		if ( sscanf( argv[argn], "%d",&expand ) != 1 )
		    pm_usage( usage );
		}
	    else if ( pm_keymatch(argv[argn],"-display",2) && argn + 1 < argc )
		{
		++argn;
		if ( sscanf( argv[argn], "%d",&display ) != 1 )
		    pm_usage( usage );
		}
	    else if ( pm_keymatch(argv[argn],"-rle",2) )
		rleflag = 1;
	    else if ( pm_keymatch(argv[argn],"-norle",2) )
		rleflag = 0;
	    else
		pm_usage( usage );
	    }

	if ( argn < argc )
	    {
	    ifp = pm_openr( argv[argn] );
	    if ( ! winflag )
		windowname = argv[argn];
	    ++argn;
	    }
	else
	    ifp = stdin;

	if ( argn != argc )
	    pm_usage( usage );

	pixels = ppm_readppm( ifp, &cols, &rows, &maxval );

	pm_close( ifp );

	for (i = 0; i < CLUTCOLORS; i++)
	    rgb[i] = 0;

	/* Figure out the colormap. */
	pm_message("computing colormap..." );
	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
	if (chv == (colorhist_vector) 0)
	pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
	pm_message("%d colors found", colors );

	/* Turn the ppm colormap into an ICR colormap. */
	if (maxval > 255)
	pm_message(
		"maxval is not 255 - automatically rescaling colors" );
	for (i = 0; i < colors; i++)
	{
	j = (3 * i);
	if (maxval == 255)
		{
		rgb[j] = PPM_GETR(chv[i].color) ;
		j++;
		rgb[j] = PPM_GETG(chv[i].color) ;
		j++;
		rgb[j] = PPM_GETB(chv[i].color) ;
		}
	else
		{
		rgb[j] = (int) PPM_GETR(chv[i].color) * 255 / maxval;
		j++;
		rgb[j] = (int) PPM_GETG(chv[i].color) * 255 / maxval;
		j++;
		rgb[j] = (int) PPM_GETB(chv[i].color) * 255 / maxval;
		}
	}
	BitsPerPixel = colorstobpp(colors);

	/* And make a hash table for fast lookup. */
	cht = ppm_colorhisttocolorhash(chv, colors);
	ppm_freecolorhist(chv);


	/************** Create a new window using ICR protocol *********/
	/* Format is "ESC^W;left;top;width;height;display;windowname"  */

	pm_message("creating window %s ...", windowname );
	(void)printf("\033^W;%d;%d;%d;%d;%d;%s^",0,0,cols*expand,rows*expand,display,windowname);
	fflush(stdout);


	/****************** Download the colormap.  ********************/
	pm_message("downloading colormap for %s ...", windowname );

	(void)printf("\033^M;%d;%d;%d;%s^",0,MAXCOLORS,CLUTCOLORS,windowname);
	thischar = rgb;
	for (j=0; j<CLUTCOLORS; j++) {
	c = *thischar++;
		if (c > 31 && c < 123 ) {	 /* printable ASCII */
		putchar(c);
		}
		else {
		putchar((c>>6)+123);	 /* non-printable, so encode it */
		putchar((c & 0x3f) + 32);
		}
	}
	fflush(stdout);

	/**************** send out picture *************************/
	/* Protocol's RLE scheme is quicker but buggy              */

	if (rleflag) {	
		pm_message("sending run-length encoded picture data ..." );
		testimage = (char*) malloc(rows*cols);
		p = testimage;
		for (i=0; i<rows; i++)
			for (j=0; j<cols; j++) 
			*p++ = GetPixel(j,i);
		space = (char*) malloc(rows*3);
		thisline = testimage;
		for (i = 0; i < rows; i++) {
			newxsize = rleit(thisline,space,cols);
			thisline += cols;	/* increment to next line */
		(void)printf("\033^R;%d;%d;%d;%d;%s^",0,i*expand,expand,newxsize,windowname);
		thischar = space;
		for (j=0; j< newxsize; j++) {
			c= *thischar++;  /*get byte to send */
			if (c>31 && c <123) {
				putchar(c);
				}
			else {
				putchar((c>>6) + 123);
				putchar((c & 0x3f) + 32);
				}
			}
			fflush(stdout);
		}
		free(space);
		exit(0);
		}

	/* Otherwise, send out uncompressed pixel data via the slow method */

		else {
		pm_message("sending picture data ..." );
		for (i = 0; i < rows; i++) {
			(void)printf("\033^P;%d;%d;%d;%d;%s^",0,i*expand,expand,cols,windowname);
			for (j = 0; j < cols; j++) {
				c  = GetPixel(j,i);
				if (c > 31 && c < 123) {
						putchar(c);
						}
				else		{
						putchar((c>>6)+123);
						putchar((c & 0x3f) + 32);
						}
				}
			}
		fflush(stdout);
		exit(0);
		}
	}

static int
colorstobpp(colors)
int colors;
	{
	int bpp;

	if (colors <= 2)
	bpp = 1;
	else if (colors <= 4)
	bpp = 2;
	else if (colors <= 8)
	bpp = 3;
	else if (colors <= 16)
	bpp = 4;
	else if (colors <= 32)
	bpp = 5;
	else if (colors <= 64)
	bpp = 6;
	else if (colors <= 128)
	bpp = 7;
	else if (colors <= 256)
	bpp = 8;
	else
	pm_error("can't happen" );
	return bpp;
	}

static int
GetPixel(x, y)
int x, y;
	{
	int color;

	color = ppm_lookupcolor(cht, &pixels[y][x]);
	return color;
	}


/* rleit   compress with run length encoding as per NCSA's documentation */

static int
rleit(buf,bufto,len)
	char* buf;
	char* bufto;
	int len;
	{
	register char* p;
	register char* q;
	register char* cfoll;
	register char* clead;
	char* begp;
	int i;

	p = buf;
	cfoll = bufto;
	clead = cfoll + 1;

	begp = p;
	while (len > 0 ) {		/* encode until gone */
		
		q = p + 1;
		i = len-1;
	while (*p == *q && i+120 > len && i) {
		q++;
		i--;
	}

	if (q > p +2) {			/* three in a row */
		if (p > begp) {
			*cfoll = p - begp;
			cfoll = clead;
		}
		*cfoll++ = 128 | (q-p);		/*len of seq*/
		*cfoll++ = *p;			/* char of seq */
		len -= q-p;		/* subtract len of seq */
		p = q;
		clead = cfoll+1;
		begp = p;
	}
	else {
		*clead++ = *p++;	/* copy one char */
		len--;
		if (p>begp + 120) {
			*cfoll = p - begp;
			cfoll = clead++;
			begp = p;
		}
	}
	}

/* fillin last bytecount */

	if (p>begp)
		*cfoll = (p - begp);
	else
		clead--;

	return((int) (clead-bufto));	/*how many stored as encoded */
}
