[alt.graphics.pixutils] ppmtoicr

svpillay@narnia.Princeton.EDU (Kanthan Pillay) (08/01/90)

ppmtoicr reads a ppm file and outputs a number of commands using NCSA's
Interactive Color Raster (ICR) protocol. This means that if you run
ppmtoicr on a terminal that understands ICR (for example, a Macintosh
with Color Quickdraw running NCSA Telnet 2.3), ppmtoicr will create a
window, download a colormap, and display the picture. (See the man page
for more details.) You need the PBMPLUS libraries to compile ppmtoicr.
ppmtoicr is copyrighted but may be freely distributed (a la PBMPLUS).

Kanthan.

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	ppmtoicr
# This archive created: Mon Jul 30 20:04:49 1990
export PATH; PATH=/bin:$PATH
if test ! -d 'ppmtoicr'
then
	mkdir 'ppmtoicr'
fi
cd 'ppmtoicr'
if test -f 'ppmtoicr.c'
then
	echo shar: will not over-write existing file "'ppmtoicr.c'"
else
cat << \SHAR_EOF > 'ppmtoicr.c'
/* ppmtoicr.c - convert a portable pixmap to NCSA ICR protocol
**
** Copyright (C) 1990 by Kanthan Pillay (svpillay@Princeton.EDU)
** Portions Copyright (C) 1989 by Jef Poskanzer.
**
** 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 <stdio.h>
#include "ppm.h"
#include "ppmcmap.h"

#define MAXCOLORS 256
#define CLUTCOLORS 768

pixel **pixels;
colorhash_table cht;
char *malloc(), *testimage;

main(argc, argv)
int argc;
char *argv[];
{
	FILE *ifd;
	int argn, rows, cols, colors, i, j, BitsPerPixel, newxsize;
	pixval maxval;
	colorhist_vector chv;
	int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS];
	char rgb[CLUTCOLORS];
	int GetPixel();
	char *usage = "[-r] [-w windowname] [-d display] [-e expand] [ppmfile]";
	char *windowname, *thischar, *thisline, *space;
	register unsigned char c;
	register char *p;
	int ci, icolors;
	int display = 0, expand = 1;

	int rflag = 0, wflag = 0, errflg = 0;
	int q, z;
	extern int optind, opterr;
	extern char *optarg;

/*	I use getopt(3) for parsing options. If you don't have it, there's a
	public domain version available from comp.sources.misc  */

	pm_progname = argv[0];
	windowname = "untitled";
	opterr = 0;
	while ((q = getopt(argc, argv, "rw:d:e:")) != EOF)
	switch (q) {
	case 'r':
		rflag++;
	break;
	case 'w':
		windowname=optarg;
		wflag++;
	break;
	case 'd':
		z = (sscanf(optarg,"%d",&display));
		if ((z == EOF) || (z == 0))
			errflg++;
	break;
	case 'e':
		z = (sscanf(optarg,"%d",&expand));
		if ((z == EOF) || (z == 0))
			errflg++;
	break;
	case '?':
		errflg++;
	}

	if (errflg) {
		pm_usage(usage);
		exit(2);
	}

	if (optind < argc)
	{
	ifd = pm_openr(argv[optind]);
	if (!wflag)
		windowname = argv[optind];
	optind++;
	}
	else
	{
	ifd = stdin;
	}

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

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

	pm_close(ifd);

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

	/* Figure out the colormap. This is Jeff's code from ppmtogif.c */
	fprintf(stderr, "(Computing colormap..." );
	fflush(stderr);
	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
	if (chv == (colorhist_vector) 0)
	pm_error(
		"too many colors - try running the pixmap through 'ppmquant 256'",
		0,0,0,0,0 );
	fprintf(stderr, "Done. %d colors found.)\n", colors);

	/* Turn the ppm colormap into an ICR colormap. */
	if (maxval > 255)
	fprintf(
		stderr, "(Maxval is not 255 -- automatically rescaling colors.)\n");
	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. Jeff's code again. */
	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"  */

	fprintf(stderr, "(Creating window %s ...",windowname);
	(void)printf("\033^W;%d;%d;%d;%d;%d;%s^",0,0,cols*expand,rows*expand,display,windowname);
	fflush(stdout);
	fprintf(stderr, "Done ...)\n");


	/****************** Download the colormap.  ********************/
	fprintf(stderr, "(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);
	fprintf(stderr, "Done ...)\n");

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

	if (rflag) {	
		fprintf(stderr, "(Sending run-length encoded picture data ...");
		testimage = malloc(rows*cols);
		p = testimage;
		for (i=0; i<rows; i++)
			for (j=0; j<cols; j++) 
			*p++ = GetPixel(j,i);
		space = 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);
		fprintf(stderr, "Done ...)\n");
		exit(0);
		}

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

		else {
		fprintf(stderr, "(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);
		fprintf(stderr, "Done)\n");
		exit(0);
		}
	}

/* Jeff's code */
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", 0,0,0,0,0);
	return bpp;
	}

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 */

rleit(buf,bufto,len)
	int len;
	char *buf,*bufto;
	{
	register char *p,*q,*cfoll,*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 = 128 | (p-begp);
	else
		clead--;

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


/* End */
SHAR_EOF
fi # end of overwriting check
if test -f 'ppmtoicr.1'
then
	echo shar: will not over-write existing file "'ppmtoicr.1'"
else
cat << \SHAR_EOF > 'ppmtoicr.1'
.TH PPMTOICR 1 "30 July 1990"
.SH NAME
ppmtoicr \- convert a portable pixmap into NCSA ICR format 
.SH SYNOPSIS
.B ppmtoicr 
[
.B \-w
.I windowname
] 
[
.B \-e
.I expand
] 
[
.B \-d
.I display
] 
[
.B \-r
] 
.I ppmfile 
.SH DESCRIPTION
.LP
Reads a portable pixmap file as input.
Produces an NCSA Telnet Interactive Color Raster graphic file as output.
If
.I ppmfile
is not supplied, 
.B ppmtoicr
will read from standard input.
.PP
Interactive Color Raster (ICR) is a protocol for displaying raster
graphics on workstation screens. The protocol is implemented in NCSA
Telnet for the Macintosh version 2.3. The ICR protocol shares
characteristics of the Tektronix graphics terminal emulation protocol.
For example, escape sequences are used to control the display.
.PP
.B ppmtoicr
will output the appropriate sequences to create a window of the
dimensions of the input
.B ppm(5)
file, create a colormap of up to 256
colors on the display, then load the picture data into the window.
.SH OPTIONS
.TP 14
.BI "\-w" " windowname"
Output will be displayed in
.I windowname
(Default is to use
.I ppmfile
or "untitled" if standard input is read.)
.TP
.BI "\-e" " expand"
Output will be expanded on display by factor 
.I expand
(For example, a value of 2 will cause four pixels to be displayed for
every input pixel.)
.TP
.BI "\-d" " display"
Output will be displayed on screen numbered 
.I display
.TP
.B "\-r"
Use run-length encoded format for display. (This will nearly always
result in a quicker display, but may skew the colormap.)
.SH EXAMPLES
To display a
.B ppm(5)
file using the protocol:
.LP
.RS
.nf
.BI ppmtoicr " ppmfile "
.RE
.fi
.LP
This will create a window named 
.I ppmfile
on the display with the
correct dimensions for
.IR "ppmfile" ,
create and download a colormap of up
to 256 colors, and download the picture into the window. The same effect
may be achieved by the following sequence:
.LP
.RS
.nf
.BI ppmtoicr " ppmfile " > " filename"
.BI cat " filename "
.RE
.fi
.LP
To display a
.B GIF 
file using the protocol in a window titled after the input file, zoom
the displayed image by a factor of 2,  and
run-length encode the data:
.LP
.RS
.nf
.BI "giftoppm " "giffile " "| ppmtoicr -w " "giffile " "-r -e " "2"
.RE
.fi
.LP
.B ppmtoicr
will also read
.B pbm(5)
and
.B pgm(5)
files. The full range of pbm manipulation routines may be applied to
graphics for display using
.BR "ppmtoicr" "."
Thus, a monochrome Sun raster file may be viewed in 5 color
grayscale by the following sequence:
.LP
.RS
.nf
.BI "pbmtorast " "rasterfile " "| ppmscale .5 | ppmtoicr"
.RE
.fi
.LP
.SH BUGS
Outputting run-length encoded data to the ICR display 
using the 
.B \-r
option sometimes results
in blurring of the picture on the right edge.
.PP
The protocol uses frequent 
.BR fflush(3)
calls to speed up display. If the
output is saved to a file for later display via
.BR "cat" "(1),"
drawing will be
much slower. In either case, increasing the Blocksize limit on the
display will speed up transmission substantially.
.PP
On displays not equipped with the protocol, garbage will be printed on
screen.
.PP
Note that there is currently no
.B icrtoppm
tool. This is because files generated by
.B ppmtoicr
are simply strings of escape sequences that generate appropriate
responses on a suitably equipped display. These files are inefficient
for long-term storage of images.
.SH SEE ALSO
.BR giftoppm(1),
.BR ilbmtoppm(1),
.BR imgtoppm(1),
.BR mtvtoppm(1),
.BR qrttoppm(1),
.BR rasttoppm(1),
.BR tgatoppm(1),
.BR xwdtoppm(1), 
.BR ppmarith(1),
.BR ppmconvol(1),
.BR ppmcscale(1),
.BR ppmhist(1), 
.BR ppmquant(1),
.BR ppmrotate(1),
.BR ppmscale(1),
.BR ppmshear(1),
.BR ppm(5),
.BR pnm(5),
.BR pgm(5),
.BR pbm(5),
.BR cat(1),
.LP
.IR "NCSA Telnet for the Macintosh", 
University of Illinois at Urbana-Champaign (1989)
.SH AUTHOR
Kanthan Pillay, Princeton University Computing and Information
Technology.
.SH COPYRIGHT
Copyright (C) 1990 by Kanthan Pillay
.LP
Portions Copyright (C) 1989 by Jef Poskanzer.
.PP
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.
SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
ppmtoicr is meant to be installed as part of Jeff Poskanzer's PBMPLUS
package and requires its libraries. Please do NOT ask me to send you
copies of the package. The package may be obtained from the 
comp.sources.misc archive at uunet.uu.net and is recommended for
anyone who doesn't want to reinvent the wheel for image processing,
and the price is right!

If you find any more bugs than what I've documented in the man page, I'd
like to hear about it.

Kanthan Pillay

---
Work: (609) 258-6028    Internet: svpillay@Princeton.EDU
Home: (609) 683-8212    Bitnet:   SVPILLAY@PUCC
Fax:  (609) 258-3943    uucp:     ...princeton!svpillay

P.S. If you're happy with this program and would like to say thanks,
stand up for protecting the free flow of information, including the
right to use this program to display whatever images you may want to.

SHAR_EOF
fi # end of overwriting check
if test -f 'INSTALLATION'
then
	echo shar: will not over-write existing file "'INSTALLATION'"
else
cat << \SHAR_EOF > 'INSTALLATION'
1) If you haven't already done so, build and install the PBMPLUS package.

2) Copy ppmtoicr.c to the ppm subdirectory of the pbm files.

3) Edit ppm/Makefile and add ppmtoicr to the list of files under
PORTBINARIES.

4) Change to the ppm subdirectory.

5) Type "make".

Alternatively, you can simply build it as follows (assuming you have the
libraries somewhere).
	cc -s -o ppmtoicr ppmtoicr.c libppm.a libpgm.a libpbm.a

Neither gcc or /bin/cc -O compile the PBM package correctly on Sun 4
(SPARC) systems. The package does compile correctly on a Sun 3 running
SunOS 4.0.3 using gcc.


SHAR_EOF
fi # end of overwriting check
cd ..
#	End of shell archive
exit 0
---
Work: (609) 258-6028    Internet: svpillay@Princeton.EDU
Home: (609) 683-8212    Bitnet:   SVPILLAY@PUCC
Fax:  (609) 258-3943    uucp:     ...rutgers!princeton!svpillay