[comp.soft-sys.andrew] PBM utilities

janssen@parc.xerox.com (Bill Janssen) (02/15/91)

With Fred Hansen's help, I finished up the ATK <-> PBM converters.  Here
they are, tested against the beta version of the new PBM release
(25Jan91).  Here's a shar file of the two C files and the Imakefile I
used to build them:

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Imakefile atktopbm.c pbmtoatk.c
# Wrapped by janssen@holmes on Thu Feb 14 18:29:51 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Imakefile'\"
else
echo shar: Extracting \"'Imakefile'\" \(211 characters\)
sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
XPBM_HOME = /import/pbmplus
X
XLOCALINCLUDES = -I${PBM_HOME}/pbm
X
XNormalObjectRule()
X
XProgramTarget (pbmtoatk, pbmtoatk.o, ${PBM_HOME}/pbm/libpbm.a,)
XProgramTarget (atktopbm, pbmtoatk.o, ${PBM_HOME}/pbm/libpbm.a,)
END_OF_FILE
if test 211 -ne `wc -c <'Imakefile'`; then
    echo shar: \"'Imakefile'\" unpacked with wrong size!
fi
# end of 'Imakefile'
fi
if test -f 'atktopbm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'atktopbm.c'\"
else
echo shar: Extracting \"'atktopbm.c'\" \(8870 characters\)
sed "s/^X//" >'atktopbm.c' <<'END_OF_FILE'
X/* atktopbm.c - read an Andrew raster object and produce a portable bitmap
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "pbm.h"
X
Xvoid ReadATKRaster();
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd;
X    register bit *bitrow, *bP;
X    int rows, cols, row, col, charcount;
X    unsigned char *data, mask;
X
X    pbm_init ( &argc, argv );
X
X    if ( argc > 2 )
X	pm_usage( "[raster obj]" );
X    
X    if ( argc == 2 )
X	ifd = pm_openr( argv[1] );
X    else
X	ifd = stdin;
X
X    ReadATKRaster( ifd, &cols, &rows, &data );
X
X    pm_close( ifd );
X
X    pbm_writepbminit( stdout, cols, rows );
X    bitrow = pbm_allocrow( cols );
X
X    for ( row = 0; row < rows; row++ )
X	{
X	charcount = 0;
X	mask = 1;
X	for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
X	    {
X	    if ( charcount >= 8 )
X		{
X		data++;
X		charcount = 0;
X		mask = 1;
X		}
X	    *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
X	    charcount++;
X	    mask = mask << 1;
X	    }
X	data++;
X	pbm_writepbmrow( stdout, bitrow, cols );
X	}
X
X    exit( 0 );
X    }
X
X/*  readatkraster.c
X
X	Routine for readingrasters in .raster form
X	(BE2 rasters version 2.)
X
X */
X
X/* codes for data stream */
X#define WHITEZERO	'f'
X#define WHITETWENTY	'z'
X#define BLACKZERO	'F'
X#define BLACKTWENTY	'Z'
X#define OTHERZERO	0x1F
X
X#define	WHITEBYTE	0x00
X#define	BLACKBYTE	0xFF
X
X/* error codes (copied from $ANDREW/atk/basics/common/dataobj.ch) */
X/* return values from Read */
X#define	dataobject_NOREADERROR	0
X#define	dataobject_PREMATUREEOF	1
X#define	dataobject_NOTBE2DATASTREAM 2 /* backward compatibility */
X#define	dataobject_NOTATKDATASTREAM 2 /* preferred version */
X#define dataobject_MISSINGENDDATAMARKER 3
X#define	dataobject_OBJECTCREATIONFAILED	4
X#define dataobject_BADFORMAT 5
X
X
X/* ReadRow(file, row, length) 
X	Reads from 'file' the encoding of bytes to fill in 'row'.  Row will be
X	truncated or padded (with WHITE) to exactly 'length' bytes.
X
X	Returns the code that terminated the row.  This may be
X			'|'  	correct end of line
X			'\0' 	if the length was satisfied (before a terminator)
X			EOF 	if the file ended
X			'\'  '{' 	other recognized ends. 
X	The '|' is the expected end and pads the row with WHITE.
X	The '\' and '{' are error conditions and may indicate the
X	beginning of some other portion of the data stream.
X	If the terminator is '\' or '{', it is left at the front of the input.
X	'|' is gobbled up.
X*/
X/* macros to generate case entries for switch statement */
X#define case1(v) case v
X#define case4(v) case v: case (v)+1: case (v)+2: case(v)+3
X#define case6(v) case4(v): case ((v)+4): case ((v)+5)
X#define case8(v) case4(v): case4((v)+4)
X
X	static long
XReadRow(file, row, length)
X	register FILE *file;		/* where to get them from */
X	register unsigned char *row;	/* where to put bytes */
X	register long length;	/* how many bytes in row must be filled */
X{
X	/* Each input character is processed by the central loop.  There are 
X		some input codes which require two or three characters for completion; 
X		these are handled by advancing the state machine.  Errors are not 
X		processed; instead the state machine is reset to the Ready state 
X		whenever a character unacceptable to the curent state is read.  */
X	enum stateCode {
X			Ready, 		/* any input code is allowed */
X			HexDigitPending,	/* have seen the first of a hex digit pair */
X			RepeatPending, 	/* repeat code has been seen:
X					must be followed by two hex digits */
X			RepeatAndDigit};	/* have seen repeat code and its first
X					following digit */
X	enum stateCode InputState;	/* current state */
X	register c;		/* the current input character */
X	register long repeatcount = 0;	/* current repeat value */
X	register long hexval;	/* current hex value */
X	long pendinghex = 0;		/* the first of a pair of hex characters */
X	
X	/* We cannot exit when length becomes zero because we need to check 
X	to see if a row ending character follows.  Thus length is checked only
Xwhen we get
X	a data generating byte.  If length then is zero, we ungetc the byte */
X
X	InputState = Ready;
X	while ((c=getc(file)) != EOF) 
X				switch (c) {
X
X	case8(0x0):
X	case8(0x8):
X	case8(0x10):
X	case8(0x18):
X	case1(' '):
X		/* control characters and space are legal and completely ignored */
X		break;
X	case1(0x40):	/* '@' */
X	case1(0x5B):	/* '[' */
X	case4(0x5D):	/*  ']'  '^'  '_'  '`' */
X	case4(0x7D):	/* '}'  '~'  DEL  0x80 */
X	default:		/* all above 0x80 */
X		/* error code:  Ignored at present.  Reset InputState. */
X		InputState = Ready;
X		break;
X
X	case1(0x7B):	/* '{' */
X	case1(0x5C):	/* '\\' */
X		/* illegal end of line:  exit anyway */
X		ungetc(c, file);		/* retain terminator in stream */
X		/* DROP THROUGH */
X	case1(0x7C):	/* '|' */
X		/* legal end of row: may have to pad  */
X		while (length-- > 0)
X			*row++ = WHITEBYTE;
X		return c;
X	
X	case1(0x21):
X	case6(0x22):
X	case8(0x28):
X		/* punctuation characters:  repeat byte given by two succeeding hex chars */
X		if (length <= 0) {
X			ungetc(c, file);
X			return('\0');
X		}
X		repeatcount = c - OTHERZERO;
X		InputState = RepeatPending;
X		break;
X
X	case8(0x30):
X	case8(0x38):
X		/* digit (or following punctuation)  -  hex digit */
X		hexval = c - 0x30;
X		goto hexdigit;
X	case6(0x41):
X		/* A ... F    -  hex digit */
X		hexval = c - (0x41 - 0xA);
X		goto hexdigit;
X	case6(0x61):
X		/* a ... f  - hex digit */
X		hexval = c - (0x61 - 0xA);
X		goto hexdigit;
X
X	case8(0x67):
X	case8(0x6F):
X	case4(0x77):
X		/* g ... z   -   multiple WHITE bytes */
X		if (length <= 0) {
X			ungetc(c, file);
X			return('\0');
X		}
X		repeatcount = c - WHITEZERO;
X		hexval = WHITEBYTE;
X		goto store;
X	case8(0x47):
X	case8(0x4F):
X	case4(0x57):
X		/* G ... Z   -   multiple BLACK bytes */
X		if (length <= 0) {
X			ungetc(c, file);
X			return('\0');
X		}
X		repeatcount = c - BLACKZERO;
X		hexval = BLACKBYTE;
X		goto store;
X
Xhexdigit:
X		/* process a hex digit.  Use InputState to determine
X			what to do with it. */
X		if (length <= 0) {
X			ungetc(c, file);
X			return('\0');
X		}
X		switch(InputState) {
X		case Ready:
X			InputState = HexDigitPending;
X			pendinghex = hexval << 4;
X			break;
X		case HexDigitPending:
X			hexval |= pendinghex;
X			repeatcount = 1;
X			goto store;
X		case RepeatPending:
X			InputState = RepeatAndDigit;
X			pendinghex = hexval << 4;
X			break;
X		case RepeatAndDigit:
X			hexval |= pendinghex;
X			goto store;
X		}
X		break;
X
Xstore:
X		/* generate byte(s) into the output row 
X			Use repeatcount, depending on state.  */
X		if (length < repeatcount) 
X			/* reduce repeat count if it would exceed
X				available space */
X			repeatcount = length;
X		length -= repeatcount;	/* do this before repeatcount-- */
X		while (repeatcount-- > 0)
X				*row++ = hexval;
X		InputState = Ready;
X		break;
X
X	} /* end of while( - )switch( - ) */
X	return EOF;
X}
X#undef case1
X#undef case4
X#undef case6
X#undef case8
X
Xvoid ReadATKRaster(filedes, rwidth, rheight, destaddr)
X     int filedes;
X     unsigned char **destaddr;
X     int *rwidth, *rheight;
X{
X	register unsigned char *byteaddr;	/* where to store next row */
X	register long row, rowlen;	/* count rows;  byte length of row */
X	long version, options, xscale, yscale, xoffset, yoffset, subwidth, subheight;
X	char keyword[6];
X	long discardid, objectid;		/* id read for the incoming pixel image */
X	long tc;				/* temp */
X	long width, height;			/* dimensions of image */
X	long result;
X	register FILE *file;
X
X	file = fdopen (filedes, "r");
X
X	if (fscanf(file, "\\begindata{raster,%ld", &discardid) != 1
X				|| getc(file) != '}' || getc(file) != '\n')
X	  pm_error ("input file not Andrew raster object", 0, 0, 0, 0, 0);
X
X	fscanf(file, " %d ", &version);
X	if (version < 2) 
X	  pm_error ("version too old to parse", 0, 0, 0, 0, 0 );
X
X	/* ignore all these features: */
X	fscanf(file, " %u %ld %ld %ld %ld %ld %ld",  
X		&options, &xscale, &yscale, &xoffset, 
X		&yoffset, &subwidth, &subheight);
X
X	/* scan to end of line in case this is actually something beyond V2 */
X	while (((tc=getc(file)) != '\n') && (tc != '\\') && (tc != EOF)) {}
X
X	/* read the keyword */
X	fscanf(file, " %5s", keyword);
X	if (strcmp(keyword, "bits") != 0)
X	  pm_error ("keyword is not bits!", 0, 0, 0, 0, 0);
X
X	fscanf(file, " %d %d %d ", &objectid, &width, &height);
X
X	if (width < 1 || height < 1 || width > 1000000 || height > 1000000) 
X	  pm_error ("bad width or height", 0, 0, 0, 0, 0 );
X
X	*rwidth = width;
X	*rheight = height;
X	rowlen = (width + 7) / 8;
X	*destaddr = (unsigned char *) malloc (sizeof(unsigned char) * height *
rowlen);
X	for (row = 0;   row < height;   row++)
X	  {
X	    long c;
X
X	    c = ReadRow(file, *destaddr + (row * rowlen), rowlen);
X	    if (c != '|')
X	      {
X		if (c == EOF)
X		  pm_error ("premature EOF", 0, 0, 0, 0, 0 );
X		else
X		  pm_error ("bad format", 0, 0, 0, 0, 0 );
X		break;
X	      }
X	  }
X	while (! feof(file) && getc(file) != '\\') {};	/* scan for \enddata */
X	if (fscanf(file, "enddata{raster,%d", &discardid) != 1
X	    || getc(file) != '}' || getc(file) != '\n')
X	  pm_error ("missing end-of-object marker", 0, 0, 0, 0, 0);
X
X	fclose (file);
X}
END_OF_FILE
if test 8870 -ne `wc -c <'atktopbm.c'`; then
    echo shar: \"'atktopbm.c'\" unpacked with wrong size!
fi
# end of 'atktopbm.c'
fi
if test -f 'pbmtoatk.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pbmtoatk.c'\"
else
echo shar: Extracting \"'pbmtoatk.c'\" \(4538 characters\)
sed "s/^X//" >'pbmtoatk.c' <<'END_OF_FILE'
X/* pbmtoatk.c - read a portable bitmap and produce an ATK raster object
X*/
X
X#include <stdio.h>
X#include "pbm.h"
X#ifdef SYSV
X#include <string.h>
X#define index strchr
X#else /*SYSV*/
X#include <strings.h>
X#endif /*SYSV*/
X
X#define DEFAULTSCALE (1<<16)
X#define RASTERVERSION 2
X#define TRUE		1
X#define FALSE		0
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd;
X    bit *bitrow;
X    register bit *bP;
X    int rows, cols, format, padright, row;
X    register int col;
X    char name[100], *cp;
X    static char hexchar[] = "0123456789abcdef";
X    unsigned char curbyte, newbyte;
X    int curcount, gather, line;
X
X    pbm_init ( &argc, argv );
X
X    if ( argc > 2 )
X	pm_usage( "[pbmfile]" );
X
X    if ( argc == 2 )
X	{
X	ifd = pm_openr( argv[1] );
X	strcpy( name, argv[1] );
X	if ( strcmp( name, "-" ) == 0 )
X	    strcpy( name, "noname" );
X
X	if ( ( cp = index( name, '.' ) ) != 0 )
X	    *cp = '\0';
X	}
X    else
X	{
X	ifd = stdin;
X	strcpy( name, "noname" );
X	}
X
X    pbm_readpbminit( ifd, &cols, &rows, &format );
X    bitrow = pbm_allocrow( cols );
X
X    /* Compute padding to round cols up to the nearest multiple of 16. */
X    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
X
X    printf ("\\begindata{raster,%d}\n", 1);
X    printf ("%ld %ld %ld %ld ", RASTERVERSION, 
X	     0, DEFAULTSCALE, DEFAULTSCALE);
X    printf ("%ld %ld %ld %ld\n",
X	     0, 0, cols, rows);	/* subraster */
X    printf ("bits %ld %ld %ld\n", 1, cols, rows);
X
X    fprintf (stderr, "cols is %d, rows is %d\n", cols, rows);
X    for ( row = 0; row < rows; row++ )
X	{
X	  pbm_readpbmrow( ifd, bitrow, cols, format );
X	  bP = bitrow;
X	  gather = 0;
X	  newbyte = 0;
X	  curbyte = 0;
X	  curcount = 0;
X	  col = 0;
X	  while (col < cols)
X	    {
X	      if (row == 3)
X		fprintf (stderr, "col %d (0x%x, %d, 0x%x, *bP is %d)\n", col,
X			 curbyte, curcount, newbyte, *bP);
X	      if (gather > 7)
X		{
X		  if (row == 3)
X		    fprintf (stderr, "process_atk_byte (%d, 0x%x, stdout, %0x,
FALSE)\n", curcount, curbyte, newbyte);
X		  process_atk_byte (&curcount, &curbyte, stdout, newbyte, FALSE);
X		  gather = 0;
X		  newbyte = 0;
X		}
X	      newbyte = (newbyte << 1) | (*bP++);
X	      gather += 1;
X	      col += 1;
X	    }
X
X	  if (gather > 0)
X	    {
X	      if (row == 3)
X		fprintf (stderr, "EOL:  newbyte is 0x%x, gather is %d\n", newbyte, gather);
X	      newbyte = (newbyte << (8 - gather));
X	      if (row == 3)
X		fprintf (stderr, "process_atk_byte (%d, 0x%x, stdout, 0x%x, TRUE)\n",
curcount, curbyte, newbyte);
X	      process_atk_byte (&curcount, &curbyte, stdout, newbyte, TRUE);
X	    }
X        }
X
X    pm_close( ifd );
X    
X    printf ("\\enddata{raster, %d}\n", 1);
X
X    exit( 0 );
X  }
X
Xwrite_atk_bytes (file, curbyte, curcount)
X     FILE *file;
X     unsigned char curbyte;
X     unsigned int curcount;
X{
X    /* codes for data stream */
X#define WHITEZERO	'f'
X#define WHITETWENTY	'z'
X#define BLACKZERO	'F'
X#define BLACKTWENTY	'Z'
X#define OTHERZERO	0x1F
X
X#define	WHITEBYTE	0x00
X#define	BLACKBYTE	0xFF
X
X    /* WriteRow table for conversion of a byte value to two character
hex representation */
X
X    static unsigned char hex[16] = {
X	'0', '1', '2', '3', '4', '5', '6', '7',
X	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
X    };
X
X  switch (curbyte) {
X  case WHITEBYTE:
X    while (curcount > 20) 
X      fputc(WHITETWENTY, file),
X      curcount -= 20;
X    fputc(WHITEZERO + curcount, file);
X    break;
X  case BLACKBYTE:
X    while (curcount > 20) 
X      fputc(BLACKTWENTY, file),
X      curcount -= 20;
X    fputc(BLACKZERO + curcount, file);
X    break;
X  default:
X    while (curcount > 16)
X      fputc(OTHERZERO+16, file),
X      fputc(hex[curbyte / 16], file),
X      fputc(hex[curbyte & 15], file),
X      curcount -= 16;
X    if (curcount > 1)
X      fputc(OTHERZERO+curcount, file);
X    else ;  /* the byte written will represent a single instance */
X    fputc(hex[curbyte / 16], file);
X    fputc(hex[curbyte & 15], file);
X  }
X}
X
Xprocess_atk_byte (pcurcount, pcurbyte, file, newbyte, eolflag)
Xint *pcurcount;
Xunsigned char *pcurbyte;
XFILE *file;
Xunsigned char newbyte;
Xint eolflag;
X{
X    int curcount = *pcurcount;
X    unsigned char curbyte = *pcurbyte;
X
X    if (curcount < 1)
X    {
X	*pcurbyte = curbyte = newbyte;
X	*pcurcount = curcount = 1;
X    }
X    else if (newbyte == curbyte)
X    {
X	*pcurcount = (curcount += 1);
X    }
X
X    if (curcount > 0 && newbyte != curbyte)
X      {
X	write_atk_bytes (file, curbyte, curcount);
X	*pcurcount = 1;
X	*pcurbyte = newbyte;
X      }
X
X    if (eolflag)
X      {
X	write_atk_bytes (file, *pcurbyte, *pcurcount);
X	fprintf (file, " |\n");
X	*pcurcount = 0;
X	*pcurbyte = 0;
X      }
X}
X
END_OF_FILE
if test 4538 -ne `wc -c <'pbmtoatk.c'`; then
    echo shar: \"'pbmtoatk.c'\" unpacked with wrong size!
fi
# end of 'pbmtoatk.c'
fi
echo shar: End of shell archive.
exit 0