Newsgroups: comp.sources.misc
From: jef@well.sf.ca.us (Jef Poskanzer)
Subject:  REPOST: v23i044:  pbmplus - Extended Portable Bitmap Toolkit, Part09/24
Message-ID: <1991Oct16.022711.5550@sparky.imd.sterling.com>
X-Md4-Signature: 8dee7d121bdd3f3d9943ec405518418c
Date: Wed, 16 Oct 1991 02:27:11 GMT
Approved: kent@sparky.imd.sterling.com

Submitted-by: jef@well.sf.ca.us (Jef Poskanzer)
Posting-number: Volume 23, Issue 44
Archive-name: pbmplus/part09
Environment: UNIX

#! /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:
#	pgm/pgmtopbm.c
#	pgm/pgmtopbm.1
#	pgm/dithers.h
#	pgm/psidtopgm.c
#	pgm/psidtopgm.1
#	pgm/fitstopgm.c
#	pgm/fitstopgm.1
#	pgm/lispmtopgm.c
#	pgm/lispmtopgm.1
#	pgm/pgmtolispm.c
#	pgm/pgmtolispm.1
#	pgm/pgmhist.c
#	pgm/pgmhist.1
#	pgm/pgmnorm.c
# This archive created: Fri Sep 27 17:50:18 1991
# By:	Jef Poskanzer (Acme Software)
export PATH; PATH=/bin:$PATH
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmtopbm.c'" '(6676 characters)'
if test -f 'pgm/pgmtopbm.c'
then
	echo shar: will not over-write existing file "'pgm/pgmtopbm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmtopbm.c'
X/* pgmtopbm.c - read a portable graymap and write a portable bitmap
X**
X** Copyright (C) 1989 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include "pgm.h"
X#include "pbm.h"
X#include "dithers.h"
X
Xvoid
Xmain( argc, argv )
X    int argc;
X    char* argv[];
X    {
X    FILE* ifp;
X    gray* grayrow;
X    register gray* gP;
X    bit* bitrow;
X    register bit* bP;
X    int argn, rows, cols, format, row, col, limitcol;
X    float fthreshval;
X    gray maxval;
X    char* usage = "[-floyd|-fs | -threshold | -dither8|-d8 |\n     -cluster3|-c3|-cluster4|-c4|-cluster8|-c8] [-value <val>] [pgmfile]";
X    int halftone;
X#define QT_FS 1
X#define QT_THRESH 2
X#define QT_DITHER8 3
X#define QT_CLUSTER3 4
X#define QT_CLUSTER4 5
X#define QT_CLUSTER8 6
X    long threshval, sum;
X    long* thiserr;
X    long* nexterr;
X    long* temperr;
X#define FS_SCALE 1024
X#define HALF_FS_SCALE 512
X    int fs_direction;
X
X    pgm_init( &argc, argv );
X
X    argn = 1;
X    halftone = QT_FS;	/* default quantization is Floyd-Steinberg */
X    fthreshval = 0.5;
X
X    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
X	{
X	if ( pm_keymatch( argv[argn], "-fs", 2 ) ||
X	     pm_keymatch( argv[argn], "-floyd", 2 ) )
X	    halftone = QT_FS;
X	else if ( pm_keymatch( argv[argn], "-threshold", 2 ) )
X	    halftone = QT_THRESH;
X	else if ( pm_keymatch( argv[argn], "-dither8", 2 ) ||
X	          pm_keymatch( argv[argn], "-d8", 3 ) )
X	    halftone = QT_DITHER8;
X	else if ( pm_keymatch( argv[argn], "-cluster3", 9 ) ||
X	          pm_keymatch( argv[argn], "-c3", 3 ) )
X	    halftone = QT_CLUSTER3;
X	else if ( pm_keymatch( argv[argn], "-cluster4", 9 ) ||
X	          pm_keymatch( argv[argn], "-c4", 3 ) )
X	    halftone = QT_CLUSTER4;
X	else if ( pm_keymatch( argv[argn], "-cluster8", 9 ) ||
X	          pm_keymatch( argv[argn], "-c8", 3 ) )
X	    halftone = QT_CLUSTER8;
X	else if ( pm_keymatch( argv[argn], "-value", 2 ) )
X	    {
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%f", &fthreshval ) != 1 ||
X		 fthreshval < 0.0 || fthreshval > 1.0 )
X		pm_usage( usage );
X	    }
X	else
X	    pm_usage( usage );
X	++argn;
X	}
X
X    if ( argn != argc )
X	{
X	ifp = pm_openr( argv[argn] );
X	++argn;
X	}
X    else
X	ifp = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
X    grayrow = pgm_allocrow( cols );
X
X    pbm_writepbminit( stdout, cols, rows, 0 );
X    bitrow = pbm_allocrow( cols );
X
X    /* Initialize. */
X    switch ( halftone )
X	{
X	case QT_FS:
X	/* Initialize Floyd-Steinberg error vectors. */
X	thiserr = (long*) pm_allocrow( cols + 2, sizeof(long) );
X	nexterr = (long*) pm_allocrow( cols + 2, sizeof(long) );
X	srandom( (int) ( time( 0 ) ^ getpid( ) ) );
X	for ( col = 0; col < cols + 2; ++col )
X	    thiserr[col] = ( random( ) % FS_SCALE - HALF_FS_SCALE ) / 4;
X	    /* (random errors in [-FS_SCALE/8 .. FS_SCALE/8]) */
X	fs_direction = 1;
X	threshval = fthreshval * FS_SCALE;
X	break;
X
X	case QT_THRESH:
X	threshval = fthreshval * maxval + 0.999999;
X	break;
X
X	case QT_DITHER8:
X	/* Scale dither matrix. */
X	for ( row = 0; row < 16; ++row )
X	    for ( col = 0; col < 16; ++col )
X		dither8[row][col] = dither8[row][col] * ( maxval + 1 ) / 256;
X	break;
X
X	case QT_CLUSTER3:
X	/* Scale order-3 clustered dither matrix. */
X	for ( row = 0; row < 6; ++row )
X	    for ( col = 0; col < 6; ++col )
X		cluster3[row][col] = cluster3[row][col] * ( maxval + 1 ) / 18;
X	break;
X
X	case QT_CLUSTER4:
X	/* Scale order-4 clustered dither matrix. */
X	for ( row = 0; row < 8; ++row )
X	    for ( col = 0; col < 8; ++col )
X		cluster4[row][col] = cluster4[row][col] * ( maxval + 1 ) / 32;
X	break;
X
X	case QT_CLUSTER8:
X	/* Scale order-8 clustered dither matrix. */
X	for ( row = 0; row < 16; ++row )
X	    for ( col = 0; col < 16; ++col )
X		cluster8[row][col] = cluster8[row][col] * ( maxval + 1 ) / 128;
X	break;
X
X	default:
X	pm_error( "can't happen" );
X	exit( 1 );
X	}
X
X    for ( row = 0; row < rows; ++row )
X	{
X	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
X
X	switch ( halftone )
X	    {
X	    case QT_FS:
X	    for ( col = 0; col < cols + 2; ++col )
X		nexterr[col] = 0;
X	    if ( fs_direction )
X		{
X		col = 0;
X		limitcol = cols;
X		gP = grayrow;
X		bP = bitrow;
X		}
X	    else
X		{
X		col = cols - 1;
X		limitcol = -1;
X		gP = &(grayrow[col]);
X		bP = &(bitrow[col]);
X		}
X	    do
X		{
X		sum = ( (long) *gP * FS_SCALE ) / maxval + thiserr[col + 1];
X		if ( sum >= threshval )
X		    {
X		    *bP = PBM_WHITE;
X		    sum = sum - threshval - HALF_FS_SCALE;
X		    }
X		else
X		    *bP = PBM_BLACK;
X
X		if ( fs_direction )
X		    {
X		    thiserr[col + 2] += ( sum * 7 ) / 16;
X		    nexterr[col    ] += ( sum * 3 ) / 16;
X		    nexterr[col + 1] += ( sum * 5 ) / 16;
X		    nexterr[col + 2] += ( sum     ) / 16;
X
X		    ++col;
X		    ++gP;
X		    ++bP;
X		    }
X		else
X		    {
X		    thiserr[col    ] += ( sum * 7 ) / 16;
X		    nexterr[col + 2] += ( sum * 3 ) / 16;
X		    nexterr[col + 1] += ( sum * 5 ) / 16;
X		    nexterr[col    ] += ( sum     ) / 16;
X
X		    --col;
X		    --gP;
X		    --bP;
X		    }
X		}
X	    while ( col != limitcol );
X	    temperr = thiserr;
X	    thiserr = nexterr;
X	    nexterr = temperr;
X	    fs_direction = ! fs_direction;
X	    break;
X
X	    case QT_THRESH:
X	    for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
X		if ( *gP >= threshval )
X		    *bP = PBM_WHITE;
X		else
X		    *bP = PBM_BLACK;
X	    break;
X
X	    case QT_DITHER8:
X	    for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
X		if ( *gP >= dither8[row % 16][col % 16] )
X		    *bP = PBM_WHITE;
X		else
X		    *bP = PBM_BLACK;
X	    break;
X
X	    case QT_CLUSTER3:
X	    for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
X		if ( *gP >= cluster3[row % 6][col % 6] )
X		    *bP = PBM_WHITE;
X		else
X		    *bP = PBM_BLACK;
X	    break;
X
X	    case QT_CLUSTER4:
X	    for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
X		if ( *gP >= cluster4[row % 8][col % 8] )
X		    *bP = PBM_WHITE;
X		else
X		    *bP = PBM_BLACK;
X	    break;
X
X	    case QT_CLUSTER8:
X	    for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
X		if ( *gP >= cluster8[row % 16][col % 16] )
X		    *bP = PBM_WHITE;
X		else
X		    *bP = PBM_BLACK;
X	    break;
X
X	    default:
X	    pm_error( "can't happen" );
X	    exit( 1 );
X	    }
X
X	pbm_writepbmrow( stdout, bitrow, cols, 0 );
X	}
X
X    pm_close( ifp );
X
X    exit( 0 );
X    }
SHAR_EOF
if test 6676 -ne "`wc -c < 'pgm/pgmtopbm.c'`"
then
	echo shar: error transmitting "'pgm/pgmtopbm.c'" '(should have been 6676 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmtopbm.1'" '(2165 characters)'
if test -f 'pgm/pgmtopbm.1'
then
	echo shar: will not over-write existing file "'pgm/pgmtopbm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmtopbm.1'
X.TH pgmtopbm 1 "26 July 1988"
X.IX pgmtopbm
X.SH NAME
Xpgmtopbm - convert a portable graymap into a portable bitmap
X.SH SYNOPSIS
X.B pgmtopbm
X.RB [ -floyd | -fs | -threshold
X.RB | -dither8 | -d8 | -cluster3
X.RB | -c3 | -cluster4 | -c4
X.RB | -cluster8 | -c8 ]
X.RB [ -value
X.IR val ]
X.RI [ pgmfile ]
X.SH DESCRIPTION
XReads a portable graymap as input.
XProduces a portable bitmap as output.
X.IX halftoning
X.PP
XNote that there is no pbmtopgm converter, because any pgm program can
Xread pbm files automagically.
X.SH OPTIONS
X.PP
XThe default quantization method is boustrophedonic Floyd-Steinberg error
Xdiffusion
X.RB ( -floyd
Xor
X.BR -fs ).
X.IX Floyd-Steinberg
X.IX "error diffusion"
XAlso available are simple thresholding
X.RB ( -threshold );
X.IX thresholding
XBayer's ordered dither
X.RB ( -dither8 )
Xwith a 16x16 matrix; and three different sizes of 45-degree clustered-dot dither
X.RB  ( -cluster3 ,
X.BR -cluster4 ,
X.BR -cluster8 ).
X.IX dithering
X.PP
XFloyd-Steinberg will almost always give the best looking results; however,
Xlooking good is not always what you want.
XFor instance, thresholding can be used in a pipeline with the
X.I pnmconvol
X.IX pnmconvol
Xtool, for tasks like edge and peak detection.
XAnd clustered-dot dithering gives a newspaper-ish look, a useful special effect.
X.PP
XThe
X.B -value
Xflag alters the thresholding value for Floyd-Steinberg and
Xsimple thresholding.
XIt should be a real number between 0 and 1.
XAbove 0.5 means darker images; below 0.5 means lighter.
X.PP
XAll flags can be abbreviated to their shortest unique prefix.
X.SH REFERENCES
XThe only reference you need for this stuff is "Digital Halftoning" by
XRobert Ulichney, MIT Press, ISBN 0-262-21009-6.
X.SH "SEE ALSO"
Xpbmreduce(1), pgm(5), pbm(5), pnmconvol(1)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 2165 -ne "`wc -c < 'pgm/pgmtopbm.1'`"
then
	echo shar: error transmitting "'pgm/pgmtopbm.1'" '(should have been 2165 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/dithers.h'" '(3499 characters)'
if test -f 'pgm/dithers.h'
then
	echo shar: will not over-write existing file "'pgm/dithers.h'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/dithers.h'
X/*
X** dithers.h
X**
X** Here are some dithering matrices.  They are all taken from "Digital
X** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6.
X*/
X
X/*
X** Order-6 ordered dithering matrix.  Note that smaller ordered dithers
X** have no advantage over larger ones, so use dither8 instead.
X*/
Xstatic int dither6[8][8] = {
X     1, 59, 15, 55,  2, 56, 12, 52,
X    33, 17, 47, 31, 34, 18, 44, 28,
X     9, 49,  5, 63, 10, 50,  6, 60,
X    41, 25, 37, 21, 42, 26, 38, 22,
X     3, 57, 13, 53,  0, 58, 14, 54,
X    35, 19, 45, 29, 32, 16, 46, 30,
X    11, 51,  7, 61,  8, 48,  4, 62,
X    43, 27, 39, 23, 40, 24, 36, 20 };
X
X/* Order-8 ordered dithering matrix. */
Xstatic int dither8[16][16] = {
X      1,235, 59,219, 15,231, 55,215,  2,232, 56,216, 12,228, 52,212,
X    129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116,
X     33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244,
X    161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92,
X      9,225, 49,209,  5,239, 63,223, 10,226, 50,210,  6,236, 60,220,
X    137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124,
X     41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252,
X    169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86,
X      3,233, 57,217, 13,229, 53,213,  0,234, 58,218, 14,230, 54,214,
X    131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118,
X     35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246,
X    163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94,
X     11,227, 51,211,  7,237, 61,221,  8,224, 48,208,  4,238, 62,222,
X    139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126,
X     43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254,
X    171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84 };
X
X/* Order-3 clustered dithering matrix. */
Xstatic int cluster3[6][6] = {
X     9,11,10, 8, 6, 7,
X    12,17,16, 5, 0, 1,
X    13,14,15, 4, 3, 2,
X     8, 6, 7, 9,11,10,
X     5, 0, 1,12,17,16,
X     4, 3, 2,13,14,15 };
X
X/* Order-4 clustered dithering matrix. */
Xstatic int cluster4[8][8] = {
X    18,20,19,16,13,11,12,15,
X    27,28,29,22, 4, 3, 2, 9,
X    26,31,30,21, 5, 0, 1,10,
X    23,25,24,17, 8, 6, 7,14,
X    13,11,12,15,18,20,19,16,
X     4, 3, 2, 9,27,28,29,22,
X     5, 0, 1,10,26,31,30,21,
X     8, 6, 7,14,23,25,24,17 };
X
X/* Order-8 clustered dithering matrix. */
Xstatic int cluster8[16][16] = {
X     64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60,
X     70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52,
X     78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44,
X     88,110,123,124,125,118,107, 85, 39, 17,  4,  3,  2,  9, 20, 42,
X     89,111,122,127,126,117,106, 84, 38, 16,  5,  0,  1, 10, 21, 43,
X     79,102,119,121,120,113, 97, 82, 48, 25,  8,  6,  7, 14, 30, 45,
X     71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53,
X     65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61,
X     63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67,
X     57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75,
X     49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83,
X     39, 17,  4,  3,  2,  9, 20, 42, 88,110,123,124,125,118,107, 85,
X     38, 16,  5,  0,  1, 10, 21, 43, 89,111,122,127,126,117,106, 84,
X     48, 25,  8,  6,  7, 14, 30, 45, 79,102,119,121,120,113, 97, 82,
X     56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74,
X     62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66 };
SHAR_EOF
if test 3499 -ne "`wc -c < 'pgm/dithers.h'`"
then
	echo shar: error transmitting "'pgm/dithers.h'" '(should have been 3499 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/psidtopgm.c'" '(2829 characters)'
if test -f 'pgm/psidtopgm.c'
then
	echo shar: will not over-write existing file "'pgm/psidtopgm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/psidtopgm.c'
X/* psidtopgm.c - convert PostScript "image" data into a portable graymap
X**
X** Copyright (C) 1989 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include "pgm.h"
X
Xstatic int gethexit ARGS(( FILE* ifp ));
X
Xvoid
Xmain( argc, argv )
Xint argc;
Xchar* argv[];
X    {
X    FILE* ifp;
X    gray* grayrow;
X    register gray* gP;
X    int argn, row;
X    register int col, val;
X    int maxval;
X    int rows, cols, bitspersample;
X    char* usage = "<width> <height> <bits/sample> [imagedata]";
X
X    pgm_init( &argc, argv );
X
X    argn = 1;
X
X    if ( argn + 3 > argc )
X	pm_usage( usage );
X
X    cols = atoi( argv[argn++] );
X    rows = atoi( argv[argn++] );
X    bitspersample = atoi( argv[argn++] );
X    if ( cols <= 0 || rows <= 0 || bitspersample <= 0 )
X	pm_usage( usage );
X
X    if ( argn < argc )
X	{
X	ifp = pm_openr( argv[argn] );
X	++argn;
X	}
X    else
X	ifp = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    maxval = pm_bitstomaxval( bitspersample );
X    if ( maxval > PGM_MAXMAXVAL )
X	pm_error(
X	    "bits/sample is too large - try reconfiguring with PGM_BIGGRAYS" );
X
X    pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
X    grayrow = pgm_allocrow( ( cols + 7 ) / 8 * 8 );
X    for ( row = 0; row < rows; ++row)
X	{
X	for ( col = 0, gP = grayrow; col < cols; )
X	    {
X	    val = gethexit( ifp ) << 4;
X	    val += gethexit( ifp );
X	    switch ( bitspersample )
X		{
X		case 1:
X		*gP++ = val >> 7;
X		*gP++ = ( val >> 6 ) & 0x1;
X		*gP++ = ( val >> 5 ) & 0x1;
X		*gP++ = ( val >> 4 ) & 0x1;
X		*gP++ = ( val >> 3 ) & 0x1;
X		*gP++ = ( val >> 2 ) & 0x1;
X		*gP++ = ( val >> 1 ) & 0x1;
X		*gP++ = val & 0x1;
X		col += 8;
X		break;
X
X		case 2:
X		*gP++ = val >> 6;
X		*gP++ = ( val >> 4 ) & 0x3;
X		*gP++ = ( val >> 2 ) & 0x3;
X		*gP++ = val & 0x3;
X		col += 4;
X		break;
X
X		case 4:
X		*gP++ = val >> 4;
X		*gP++ = val & 0xf;
X		col += 2;
X		break;
X
X		case 8:
X		*gP++ = val;
X		++col;
X		break;
X
X		default:
X		pm_error( "bitspersample of %d not supported", bitspersample );
X		}
X	    }
X	pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
X	}
X    pm_close( ifp );
X
X    exit( 0 );
X    }
X
Xstatic int
Xgethexit( ifp )
XFILE* ifp;
X    {
X    register int i;
X    register char c;
X
X    for ( ; ; )
X	{
X	i = getc( ifp );
X	if ( i == EOF )
X	    pm_error( "EOF / read error" );
X	c = (char) i;
X	if ( c >= '0' && c <= '9' )
X	    return c - '0';
X	else if ( c >= 'A' && c <= 'F' )
X	    return c - 'A' + 10;
X	else if ( c >= 'a' && c <= 'f' )
X	    return c - 'a' + 10;
X	/* Else ignore - whitespace. */
X	}
X    }
SHAR_EOF
if test 2829 -ne "`wc -c < 'pgm/psidtopgm.c'`"
then
	echo shar: error transmitting "'pgm/psidtopgm.c'" '(should have been 2829 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/psidtopgm.1'" '(1477 characters)'
if test -f 'pgm/psidtopgm.1'
then
	echo shar: will not over-write existing file "'pgm/psidtopgm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/psidtopgm.1'
X.TH psidtopgm 1 "02 August 89"
X.IX psidtopgm
X.SH NAME
Xpsidtopgm - convert PostScript "image" data into a portable graymap
X.SH SYNOPSIS
X.B psidtopgm
X.I width height bits/sample
X.RI [ imagedata ]
X.SH DESCRIPTION
XReads the "image" data from a PostScript file as input.
X.IX PostScript
XProduces a portable graymap as output.
X.PP
XThis is a very simple and limited program, and is here only because
Xso many people have asked for it.
XTo use it you have to
X.B manually
Xextract the readhexstring data portion from your PostScript file, and then
Xgive the width, height, and bits/sample on the command line.
XBefore you attempt this, you should
X.B at least
Xread the description of the "image" operator in the PostScript Language
XReference Manual.
X.PP
XIt would probably not be too hard to write a script that uses this filter
Xto read a specific variety of PostScript image, but the variation is too
Xgreat to make a general-purpose reader.
XUnless, of course, you want to write a full-fledged PostScript interpreter...
X.SH "SEE ALSO"
Xpnmtops(1), pgm(5)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 1477 -ne "`wc -c < 'pgm/psidtopgm.1'`"
then
	echo shar: error transmitting "'pgm/psidtopgm.1'" '(should have been 1477 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/fitstopgm.c'" '(6061 characters)'
if test -f 'pgm/fitstopgm.c'
then
	echo shar: will not over-write existing file "'pgm/fitstopgm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/fitstopgm.c'
X/* fitstopgm.c - read a FITS file and produce a portable graymap
X**
X** Copyright (C) 1989 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include "pgm.h"
X
Xstruct FITS_Header {
X    int simple;		/* basic format or not */
X    int bitpix;		/* number of bits per pixel */
X    int naxis;		/* number of axes */
X    int naxis1;		/* number of points on axis 1 */
X    int naxis2;		/* number of points on axis 2 */
X    int naxis3;		/* number of points on axis 3 */
X    double datamin;	/* min # */
X    double datamax;	/* max # */
X    double bzer;	/* ??? */
X    double bscale;	/* ??? */
X    };
X
Xstatic void read_fits_header ARGS(( FILE* fp, struct FITS_Header* hP ));
Xstatic void read_card ARGS(( FILE* fp, char* buf ));
X
Xvoid
Xmain( argc, argv )
Xint argc;
Xchar* argv[];
X    {
X    FILE* ifp;
X    gray* grayrow;
X    register gray* gP;
X    int argn, imagenum, image, row;
X    register int col;
X    gray maxval;
X    double fmaxval, scale;
X    int rows, cols, images;
X    struct FITS_Header h;
X    char* usage = "[-image N] [FITSfile]";
X
X    pgm_init( &argc, argv );
X
X    argn = 1;
X    imagenum = 1;
X
X    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
X	{
X	if ( pm_keymatch( argv[argn], "-image", 2 ) )
X	    {
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%d", &imagenum ) != 1 )
X		pm_usage( usage );
X	    }
X	else
X	    pm_usage( usage );
X	++argn;
X	}
X
X    if ( argn < argc )
X	{
X	ifp = pm_openr( argv[argn] );
X	++argn;
X	}
X    else
X	ifp = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    read_fits_header( ifp, &h );
X
X    if ( ! h.simple )
X	pm_error( "FITS file is not in simple format, can't read" );
X    switch ( h.bitpix )
X	{
X	case 8:
X	fmaxval = 255.0;
X	break;
X
X	case 16:
X	fmaxval = 65535.0;
X	break;
X
X	case 32:
X	fmaxval = 4294967295.0;
X	break;
X
X	default:
X	pm_error( "unusual bits per pixel (%d), can't read", h.bitpix );
X	}
X    if ( h.naxis != 2 && h.naxis != 3 )
X	pm_error( "FITS file has %d axes, can't read", h.naxis );
X    cols = h.naxis1;
X    rows = h.naxis2;
X    if ( h.naxis == 2 )
X	images = 1;
X    else
X	images = h.naxis3;
X    if ( imagenum > images )
X	pm_error( "only %d image%s in this file",
X		  images, images > 1 ? "s" : "" );
X    maxval = min( fmaxval, PGM_MAXMAXVAL );
X    if ( h.datamin != 0.0 || h.datamax != 1.0 )
X	scale = maxval / ( h.datamax - h.datamin );
X    else
X	{
X	/* FITS images are not required to contain DATAMIN and DATAMAX cards
X	** In this case, it would be necessary to read through the image
X	** twice to properly scale.  Take this shortcut instead, remembering
X	** that all FITS integers are signed values.  This scheme works
X	** on most images because most astronomical data reduction packages
X	** scale the images when writing so as to make maximal use of the
X	** dynamic range of the output format. */
X	h.bscale = 1.;
X	h.bzer = ( fmaxval + 1.0 ) * 0.5;
X	scale = maxval / fmaxval;
X	}
X
X    pgm_writepgminit( stdout, cols, rows, maxval, 0 );
X    grayrow = pgm_allocrow( cols );
X    for ( image = 1; image <= imagenum; ++image )
X	{
X	if ( image != imagenum )
X	    pm_message( "skipping image %d of %d", image, images );
X	else if ( images > 1 )
X	    pm_message( "reading image %d of %d", image, images );
X	for ( row = 0; row < rows; ++row)
X	    {
X	    for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
X		{
X		int ich;
X		double val;
X
X		switch ( h.bitpix )
X		    {
X		    case 8:
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val = ich;
X		    break;
X
X		    case 16:
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val = ich << 8;
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val += ich;
X		    break;
X
X		    case 32:
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val = ich << 24;
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val += ich << 16;
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val += ich << 8;
X		    ich = getc( ifp );
X		    if ( ich == EOF )
X			pm_error( "EOF / read error" );
X		    val += ich;
X		    break;
X
X		    default:
X		    pm_error( "can't happen" );
X		    }
X		*gP = (gray) ( scale * ( val * h.bscale + h.bzer - h.datamin) );
X		}
X	    if ( image == imagenum )
X		pgm_writepgmrow( stdout, grayrow, cols, maxval, 0 );
X	    }
X	}
X    pm_close( ifp );
X
X    exit( 0 );
X    }
X
Xstatic void
Xread_fits_header( fp, hP )
XFILE* fp;
Xstruct FITS_Header* hP;
X    {
X    char buf[80];
X    int seen_end;
X    int i;
X    char c;
X
X    seen_end = 0;
X    hP->simple = 0;
X    hP->bzer = 0.0;
X    hP->bscale = 1.0;
X    hP->datamin = 0.0;
X    hP->datamax = 1.0;
X
X    while ( ! seen_end )
X	for ( i = 0; i < 36; ++i )
X	    {
X	    read_card( fp, buf );
X
X	    if ( sscanf( buf, "SIMPLE = %c", &c ) == 1 )
X		{
X		if ( c == 'T' || c == 't' )
X		    hP->simple = 1;
X		}
X	    else if ( sscanf( buf, "BITPIX = %d", &(hP->bitpix) ) == 1 );
X	    else if ( sscanf( buf, "NAXIS = %d", &(hP->naxis) ) == 1 );
X	    else if ( sscanf( buf, "NAXIS1 = %d", &(hP->naxis1) ) == 1 );
X	    else if ( sscanf( buf, "NAXIS2 = %d", &(hP->naxis2) ) == 1 );
X	    else if ( sscanf( buf, "NAXIS3 = %d", &(hP->naxis3) ) == 1 );
X	    else if ( sscanf( buf, "DATAMIN = %lf", &(hP->datamin) ) == 1 );
X	    else if ( sscanf( buf, "DATAMAX = %lf", &(hP->datamax) ) == 1 );
X	    else if ( sscanf( buf, "BZERO = %lf", &(hP->bzer) ) == 1 );
X	    else if ( sscanf( buf, "BSCALE = %lf", &(hP->bscale) ) == 1 );
X	    else if ( strncmp( buf, "END ", 4 ) == 0 ) seen_end = 1;
X	    }
X    }
X
Xstatic void
Xread_card( fp, buf )
XFILE* fp;
Xchar* buf;
X    {
X    if ( fread( buf, 1, 80, fp ) == 0 )
X	pm_error( "error reading header" );
X    }
SHAR_EOF
if test 6061 -ne "`wc -c < 'pgm/fitstopgm.c'`"
then
	echo shar: error transmitting "'pgm/fitstopgm.c'" '(should have been 6061 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/fitstopgm.1'" '(1288 characters)'
if test -f 'pgm/fitstopgm.1'
then
	echo shar: will not over-write existing file "'pgm/fitstopgm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/fitstopgm.1'
X.TH fitstopgm 1 "20 September 89"
X.IX fitstopgm
X.SH NAME
Xfitstopgm - convert a FITS file into a portable graymap
X.SH SYNOPSIS
X.B fitstopgm
X.RB [ -image
X.IR N ]
X.RI [ FITSfile ]
X.SH DESCRIPTION
XReads a FITS file as input.
X.IX FITS
XProduces a portable graymap as output.
XThe results may need to be flipped top for bottom; if so, just
Xpipe the output through
X.B pnmflip -tb.
X.IX pnmflip
X.SH OPTIONS
X.PP
XThe
X.B -image
Xoption is for FITS files with three axes.
XThe assumption is that the third axis is for multiple images,
Xand this option lets you select which one you want.
X.PP
XAll flags can be abbreviated to their shortest unique prefix.
X.SH REFERENCES
XFITS stands for Flexible Image Transport System.  A full description
Xcan be found in Astronomy & Astrophysics Supplement Series 44 (1981),
Xpage 363.
X.SH "SEE ALSO"
Xpgmtofits(1), pgm(5), pnmflip(1)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 1288 -ne "`wc -c < 'pgm/fitstopgm.1'`"
then
	echo shar: error transmitting "'pgm/fitstopgm.1'" '(should have been 1288 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/lispmtopgm.c'" '(5202 characters)'
if test -f 'pgm/lispmtopgm.c'
then
	echo shar: will not over-write existing file "'pgm/lispmtopgm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/lispmtopgm.c'
X/* lispmtopgm.c - read a file written by the tv:write-bit-array-file function
X** of TI Explorer and Symbolics Lisp Machines, and write a PGM.
X**
X** Written by Jamie Zawinski based on code (C) 1988 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X**
X**   When one writes a multi-plane bitmap with tv:write-bit-array-file, it is
X**   usually a color image; but a color map is not written in the file, so we
X**   treat this as a graymap instead.  Since the pgm reader can also read pbms,
X**   this doesn't matter if you're using only single plane images.
X*/
X
X#include <stdio.h>
X#include "pgm.h"
X
X#define LISPM_MAGIC  "This is a BitMap file"
X
Xstatic void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
Xstatic int depth_to_word_size ARGS(( int depth ));
Xstatic unsigned int getval ARGS(( FILE* file ));
X
Xvoid
Xmain( argc, argv )
X    int argc;
X    char* argv[];
X    {
X    FILE* ifp;
X    gray* grayrow;
X    register gray* gP;
X    short rows, cols, padright, row, col;
X    short depth;
X    int maxval;
X
X    pgm_init( &argc, argv );
X
X    if ( argc > 2 )
X	pm_usage( "[lispmfile]" );
X
X    if ( argc == 2 )
X        ifp = pm_openr( argv[1] );
X    else
X	ifp = stdin;
X
X    getinit( ifp, &cols, &rows, &depth, &padright );
X    maxval = 1 << depth;
X
X    if ( maxval > PGM_MAXMAXVAL )
X	pm_error( "depth is too large - try reconfiguring with PGM_BIGGRAYS" );
X
X    pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
X    grayrow = pgm_allocrow( ( cols + 7 ) / 8 * 8 );
X
X    for ( row = 0; row < rows; ++row )
X	{
X        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
X	    *gP = getval( ifp );
X	pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
X	}
X    pm_close( ifp );
X    exit( 0 );
X    }
X
Xstatic long item, bitmask;
Xstatic unsigned int bitsperitem, maxbitsperitem, bitshift;
X
Xstatic void
Xgetinit( file, colsP, rowsP, depthP, padrightP )
X    FILE* file;
X    short* colsP;
X    short* rowsP;
X    short* padrightP;
X    short* depthP;
X    {
X    short cols_32;
X    char magic[sizeof(LISPM_MAGIC)];
X    int i;
X
X    for ( i = 0; i < sizeof(magic)-1; ++i )
X        magic[i] = getc( file );
X    magic[i]='\0';
X    if (0 != strcmp(LISPM_MAGIC, magic))
X        pm_error( "bad id string in Lispm file" );
X    
X    if ( pm_readlittleshort( file, colsP ) == -1 )
X        pm_error( "EOF / read error" );
X    if ( pm_readlittleshort( file, rowsP ) == -1 )
X        pm_error( "EOF / read error" );
X    if ( pm_readlittleshort( file, &cols_32 ) == -1 )
X        pm_error( "EOF / read error" );
X    *depthP = getc( file );
X    
X    if ( *depthP == 0 )
X	*depthP = 1;	/* very old file */
X    
X    *padrightP = ( ( *colsP + 31 ) / 32 ) * 32 - *colsP;
X    
X    if ( *colsP != (cols_32 - *padrightP) ) {
X/*    pm_message( "inconsistent input: Width and Width(mod32) fields don't agree" );  */
X/*    *padrightP = cols_32 - *colsP;   */ /*    hmmmm....   */
X      /* This is a dilemma.  Usually the output is rounded up to mod32, but not always.
X       * For the Lispm code to not round up, the array size must be the same size as the
X       * portion being written - that is, the array itself must be an odd size, not just
X       * the selected portion.  Since arrays that are odd sizes can't be handed to bitblt,
X       * such arrays are probably not image data - so punt on it for now.
X       *
X       * Also, the lispm code for saving bitmaps has a bug, in that if you are writing a
X       * bitmap which is not mod32 across, the file may be up to 7 bits too short!  They
X       * round down instead of up.
X       *
X       * The code in 'pgmtolispm.c' always rounds up to mod32, which is totally reasonable.
X       */
X      }
X    bitsperitem = 0;
X    maxbitsperitem = depth_to_word_size( *depthP );
X    bitmask = ( 1 << maxbitsperitem ) - 1;		/* for depth=3, mask=00000111 */
X
X    for ( i = 0; i < 9; ++i )
X	getc( file );	/* discard bytes reserved for future use */
X    }
X
Xstatic int
Xdepth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is written    */
X  int depth;			/* out with a depth of 5, it really has a depth of 8, and  */
X{				/* is stored that way in the file.			   */
X    if (depth==0 || depth==1)	return ( 1);
X    else if (depth ==  2)	return ( 2);
X    else if (depth <=  4)	return ( 4);
X    else if (depth <=  8)	return ( 8);
X    else if (depth <= 16)	return (16);
X    else if (depth <= 32)	return (32);
X    else
X      pm_error( "depth was %d, which is not in the range 1-32.", depth );
X}
X
Xstatic unsigned int
Xgetval( file )
X    FILE* file;
X    {
X    unsigned int b;
X
X    if ( bitsperitem == 0 )
X	{
X	if ( pm_readlittlelong( file, &item ) == -1 )
X	    pm_error( "EOF / read error" );
X	bitsperitem = 32;
X	bitshift = 0;
X	item = ~item;
X	}
X    b = ( ( item >> bitshift ) & bitmask );
X    bitsperitem = bitsperitem - maxbitsperitem;
X    bitshift = bitshift + maxbitsperitem;
X    return b;
X    }
SHAR_EOF
if test 5202 -ne "`wc -c < 'pgm/lispmtopgm.c'`"
then
	echo shar: error transmitting "'pgm/lispmtopgm.c'" '(should have been 5202 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/lispmtopgm.1'" '(1643 characters)'
if test -f 'pgm/lispmtopgm.1'
then
	echo shar: will not over-write existing file "'pgm/lispmtopgm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/lispmtopgm.1'
X.TH lispmtopgm 1 "06 March 1990"
X.IX lispmtopgm
X.SH NAME
Xlispmtopgm - convert a Lisp Machine bitmap file into pgm format
X.SH SYNOPSIS
X.B lispmtopgm
X.RI [ lispmfile ]
X.SH DESCRIPTION
XReads a Lisp Machine bitmap as input.
X.IX "Lisp Machine bitmap"
XProduces a portable graymap as output.
X.PP
XThis is the file format written by the tv:write-bit-array-file function on
XTI Explorer and Symbolics lisp machines.
X.PP
XMulti-plane bitmaps on lisp machines are color; but the lispm image file
Xformat does not include a color map, so we must treat it as a graymap 
Xinstead.  This is unfortunate.
X.SH "SEE ALSO"
Xpgmtolispm(1), pgm(5)
X.SH BUGS
XThe Lispm bitmap file format is a bit quirky;  Usually the image in the file
Xhas its width rounded up to the next higher multiple of 32, but not always.
XIf the width is not a multiple of 32, we don't deal with it properly, but 
Xbecause of the Lispm microcode, such arrays are probably not image data 
Xanyway.
X.PP
XAlso, the lispm code for saving bitmaps has a bug, in that if you are writing a
Xbitmap which is not mod32 across, the file may be up to 7 bits too short!  They
Xround down instead of up, and we don't handle this bug gracefully.
X.PP
XNo color.
X.SH AUTHOR
XCopyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 1643 -ne "`wc -c < 'pgm/lispmtopgm.1'`"
then
	echo shar: error transmitting "'pgm/lispmtopgm.1'" '(should have been 1643 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmtolispm.c'" '(4012 characters)'
if test -f 'pgm/pgmtolispm.c'
then
	echo shar: will not over-write existing file "'pgm/pgmtolispm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmtolispm.c'
X/* pgmtolispm.c - read a pgm and write a file acceptable to the 
X** tv:read-bit-array-file function of TI Explorer and Symbolics Lisp Machines.
X**
X** Written by Jamie Zawinski based on code (C) 1988 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X**
X**   When one writes a multi-plane bitmap with tv:write-bit-array-file, it is
X**   usually a color image; but a color map is not written in the file, so we
X**   treat this as a graymap instead.  To convert a color image to Lispm 
X**   format, you must convert it to a pgm, and hand-edit a color map...  Ick.
X*/
X
X#include <stdio.h>
X#include "pgm.h"
X
X#define LISPM_MAGIC  "This is a BitMap file"
X
Xstatic void putinit ARGS(( int cols, int rows, int depth ));
Xstatic int depth_to_word_size ARGS(( int depth ));
Xstatic void putval ARGS(( gray b ));
Xstatic void putrest ARGS(( void ));
Xstatic void putitem ARGS(( void ));
X
Xvoid
Xmain( argc, argv )
X    int argc;
X    char* argv[];
X    {
X    FILE* ifp;
X    gray *grayrow;
X    register gray* gP;
X    int rows, cols, depth, format, padright, row, col;
X    gray maxval;
X
X    pgm_init( &argc, argv );
X
X    if ( argc > 2 )
X	pm_usage( "[pgmfile]" );
X    if ( argc == 2 )
X	ifp = pm_openr( argv[1] );
X    else
X	ifp = stdin;
X
X    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
X    grayrow = pgm_allocrow( cols );
X    depth = pm_maxvaltobits( maxval );
X
X    /* Compute padding to round cols up to the nearest multiple of 32. */
X    padright = ( ( cols + 31 ) / 32 ) * 32 - cols;
X
X    putinit( cols, rows, depth );
X    for ( row = 0; row < rows; ++row )
X	{
X	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
X        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
X	    putval( *gP );
X	for ( col = 0; col < padright; ++col )
X	    putval( 0 );
X        }
X
X    pm_close( ifp );
X
X    putrest( );
X
X    exit( 0 );
X    }
X
Xstatic unsigned int item;
Xstatic unsigned int bitsperitem, maxbitsperitem, bitshift;
X
Xstatic void
Xputinit( cols, rows, depth )
X    int cols, rows, depth;
X    {
X    int i;
X    int cols32 = ( ( cols + 31 ) / 32 ) * 32;	/* Lispms are able to write bit files that are not mod32 wide, but we   */
X						/* don't.  This should be ok, since bit arrays which are not mod32 wide */
X    printf(LISPM_MAGIC);			/* are pretty useless on a lispm (can't hand them to bitblt).		*/
X    pm_writelittleshort( stdout, cols );
X    pm_writelittleshort( stdout, rows );
X    pm_writelittleshort( stdout, cols32 );
X    putchar(depth & 0xFF);
X
X    for ( i = 0; i < 9; ++i )
X	putchar( 0 );	/* pad bytes */
X
X    item = 0;
X    bitsperitem = 0;
X    maxbitsperitem = depth_to_word_size( depth );
X    bitshift = 0;
X    }
X
Xstatic int
Xdepth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is written    */
X  int depth;			/* out with a depth of 5, it really has a depth of 8, and  */
X{				/* is stored that way in the file.			   */
X    if (depth==0 || depth==1)	return ( 1);
X    else if (depth ==  2)	return ( 2);
X    else if (depth <=  4)	return ( 4);
X    else if (depth <=  8)	return ( 8);
X    else if (depth <= 16)	return (16);
X    else if (depth <= 32)	return (32);
X    else
X      pm_error( "depth was %d, which is not in the range 1-32", depth );
X}
X
X#if __STDC__
Xstatic void
Xputval( gray b )
X#else /*__STDC__*/
Xstatic void
Xputval( b )
Xgray b;
X#endif /*__STDC__*/
X    {
X    if ( bitsperitem == 32 )
X	putitem( );
X    item = item | ( b << bitshift );
X    bitsperitem = bitsperitem + maxbitsperitem;
X    bitshift = bitshift + maxbitsperitem;
X    }
X
Xstatic void
Xputrest( )
X    {
X    if ( bitsperitem > 0 )
X	putitem( );
X    }
X
Xstatic void
Xputitem( )
X    {
X    pm_writelittlelong( stdout, ~item );
X    item = 0;
X    bitsperitem = 0;
X    bitshift = 0;
X    }
SHAR_EOF
if test 4012 -ne "`wc -c < 'pgm/pgmtolispm.c'`"
then
	echo shar: error transmitting "'pgm/pgmtolispm.c'" '(should have been 4012 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmtolispm.1'" '(1493 characters)'
if test -f 'pgm/pgmtolispm.1'
then
	echo shar: will not over-write existing file "'pgm/pgmtolispm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmtolispm.1'
X.TH pgmtolispm 1 "06 March 1990"
X.IX pgmtolispm
X.SH NAME
Xpgmtolispm - convert a portable graymap into Lisp Machine format
X.SH SYNOPSIS
X.B pgmtolispm
X.RI [ pgmfile ]
X.SH DESCRIPTION
XReads a portable graymap as input.
XProduces a Lisp Machine bitmap as output.
X.IX "Lisp Machine bitmap"
X.PP
XThis is the file format read by the tv:read-bit-array-file function on
XTI Explorer and Symbolics lisp machines.
X.PP
XGiven a pgm (instead of a pbm) a multi-plane image will be output.
XThis is probably not useful unless you have a color lisp machine.
X.PP
XMulti-plane bitmaps on lisp machines are color; but the lispm image file
Xformat does not include a color map, so we must treat it as a graymap 
Xinstead.  This is unfortunate.
X.SH "SEE ALSO"
Xlispmtopgm(1), pgm(5)
X.SH BUGS
XOutput width is always rounded up to the nearest multiple of 32; this might 
Xnot always be what you want, but it probably is (arrays which are not 
Xmodulo 32 cannot be passed to the Lispm BITBLT function, and thus cannot 
Xeasily be displayed on the screen).
X.PP
XNo color.
X.SH AUTHOR
XCopyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 1493 -ne "`wc -c < 'pgm/pgmtolispm.1'`"
then
	echo shar: error transmitting "'pgm/pgmtolispm.1'" '(should have been 1493 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmhist.c'" '(2055 characters)'
if test -f 'pgm/pgmhist.c'
then
	echo shar: will not over-write existing file "'pgm/pgmhist.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmhist.c'
X/* pgmhist.c - print a histogram of the values in a portable graymap
X**
X** Copyright (C) 1989 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include "pgm.h"
X
Xvoid
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifp;
X    gray maxval, *grayrow;
X    register gray *gP;
X    int argn, rows, cols, format, row;
X    int i, *hist, *rcount, count, size;
X    register int col;
X    char *usage = "[pgmfile]";
X
X    pgm_init( &argc, argv );
X
X    argn = 1;
X
X    if ( argn < argc )
X	{
X	ifp = pm_openr( argv[argn] );
X	argn++;
X	}
X    else
X	ifp = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
X    grayrow = pgm_allocrow( cols );
X
X    /* Build histogram. */
X    hist = (int *) malloc( ( maxval + 1 ) * sizeof(int) );
X    rcount = (int *) malloc( ( maxval + 1 ) * sizeof(int) );
X    if ( hist == (int *) 0 || rcount == (int *) 0 )
X	pm_error( "out of memory" );
X    for ( i = 0; i <= maxval; i++ )
X	hist[i] = 0;
X    for ( row = 0; row < rows; row++ )
X	{
X	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
X        for ( col = 0, gP = grayrow; col < cols; col++, gP++ )
X	    hist[(int) *gP]++;
X	}
X
X    pm_close( ifp );
X
X    /* Compute count-down */
X    count = 0;
X    for ( i = maxval; i >= 0; i-- )
X	{
X	count += hist[i];
X	rcount[i] = count;
X	}
X
X    /* And print it. */
X    printf( "value\tcount\tb%%\tw%%\n" );
X    printf( "-----\t-----\t--\t--\n" );
X    count = 0;
X    size = rows * cols;
X    for ( i = 0; i <= maxval; i++ )
X	if ( hist[i] > 0 )
X	    {
X	    count += hist[i];
X	    printf(
X		"%d\t%d\t%5.3g%%\t%5.3g%%\n", i, hist[i],
X		(float) count * 100.0 / size, (float) rcount[i] * 100.0 / size );
X	    }
X
X    exit( 0 );
X    }
SHAR_EOF
if test 2055 -ne "`wc -c < 'pgm/pgmhist.c'`"
then
	echo shar: error transmitting "'pgm/pgmhist.c'" '(should have been 2055 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmhist.1'" '(734 characters)'
if test -f 'pgm/pgmhist.1'
then
	echo shar: will not over-write existing file "'pgm/pgmhist.1'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmhist.1'
X.TH pgmhist 1 "28 February 1989"
X.IX pgmhist
X.SH NAME
Xpgmhist - print a histogram of the values in a portable graymap
X.SH SYNOPSIS
X.B pgmhist
X.RI [ pgmfile ]
X.SH DESCRIPTION
XReads a portable graymap as input.
XPrints a histogram of the gray values.
X.SH "SEE ALSO"
Xpgmnorm(1), pgm(5), ppmhist(1)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 734 -ne "`wc -c < 'pgm/pgmhist.1'`"
then
	echo shar: error transmitting "'pgm/pgmhist.1'" '(should have been 734 characters)'
fi
fi # end of overwriting check
if test ! -d 'pgm'
then
	echo shar: creating directory "'pgm'"
	mkdir 'pgm'
fi
echo shar: extracting "'pgm/pgmnorm.c'" '(5084 characters)'
if test -f 'pgm/pgmnorm.c'
then
	echo shar: will not over-write existing file "'pgm/pgmnorm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pgm/pgmnorm.c'
X/* pgmnorm.c - read a portable graymap and normalize the contrast
X**
X** Copyright (C) 1989, 1991 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include "pgm.h"
X
Xstatic int hist[PGM_MAXMAXVAL+1];
X
Xvoid
Xmain( argc, argv )
Xint argc;
Xchar* argv[];
X    {
X    FILE* ifp;
X    gray maxval;
X    gray** grays;
X    gray* grayrow;
X    register gray* gP;
X    int argn, rows, cols, format, row;
X    register int col;
X    int i, size, cutoff, count, val;
X    float bpercent, wpercent;
X    int bvalue, wvalue, range;
X    int specbpercent, specbvalue, specwpercent, specwvalue;
X    char* usage = "[-bpercent N | -bvalue N] [-wpercent N | -wvalue N] [pgmfile]";
X
X    pgm_init( &argc, argv );
X
X    argn = 1;
X    bpercent = 2.0;
X    wpercent = 1.0;
X    specbpercent = specbvalue = specwpercent = specwvalue = 0;
X
X    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
X	{
X	if ( pm_keymatch( argv[argn], "-bpercent", 3 ) )
X	    {
X	    if ( specbvalue )
X		pm_error( "only one of -bpercent and -bvalue may be specified" );
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%f", &bpercent ) != 1 )
X		pm_usage( usage );
X	    if ( bpercent < 0.0  || bpercent > 100.0 )
X		pm_error( "black percentage must between 0 and 100" );
X	    specbpercent = 1;
X	    }
X	else if ( pm_keymatch( argv[argn], "-bvalue", 3 ) )
X	    {
X	    if ( specbpercent )
X		pm_error( "only one of -bpercent and -bvalue may be specified" );
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%d", &bvalue ) != 1 )
X		pm_usage( usage );
X	    if ( bvalue < 0 )
X		pm_error( "black value must be >= 0" );
X	    specbvalue = 1;
X	    }
X	else if ( pm_keymatch( argv[argn], "-wpercent", 3 ) )
X	    {
X	    if ( specwvalue )
X		pm_error( "only one of -wpercent and -wvalue may be specified" );
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%f", &wpercent ) != 1 )
X		pm_usage( usage );
X	    if ( wpercent < 0.0 || wpercent > 100.0 )
X		pm_error( "white percentage must be between 0 and 100" );
X	    specwpercent = 1;
X	    }
X	else if ( pm_keymatch( argv[argn], "-wvalue", 3 ) )
X	    {
X	    if ( specwpercent )
X		pm_error( "only one of -wpercent and -wvalue may be specified" );
X	    ++argn;
X	    if ( argn == argc || sscanf( argv[argn], "%d", &wvalue ) != 1 )
X		pm_usage( usage );
X	    if ( wvalue < 0 )
X		pm_error( "white value must be >= 0" );
X	    specwvalue = 1;
X	    }
X	else
X	    pm_usage( usage );
X	++argn;
X	}
X
X    if ( argn < argc )
X	{
X	ifp = pm_openr( argv[argn] );
X	++argn;
X	}
X    else
X	ifp = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    if ( specbvalue && specwvalue )
X	{
X	/* Rescale so that bvalue maps to 0, wvalue maps to maxval. */
X	pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
X	grayrow = pgm_allocrow( cols );
X	pgm_writepgminit( stdout, cols, rows, maxval, 0 );
X	for ( i = 0; i <= bvalue; ++i )
X	    hist[i] = 0;
X	for ( i = wvalue; i <= maxval; ++i )
X	    hist[i] = maxval;
X	range = wvalue - bvalue;
X	for ( i = bvalue+1, val = maxval; i < wvalue; ++i, val += maxval )
X	    hist[i] = val / range;
X	for ( row = 0; row < rows; ++row )
X	    {
X	    pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
X	    for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
X		*gP = hist[*gP];
X	    pgm_writepgmrow( stdout, grayrow, cols, maxval, 0 );
X	    }
X	pm_close( ifp );
X	}
X    else
X	{
X	grays = pgm_readpgm( ifp, &cols, &rows, &maxval );
X	pm_close( ifp );
X
X	/* Build histogram. */
X	for ( i = 0; i <= maxval; ++i )
X	    hist[i] = 0;
X	for ( row = 0; row < rows; ++row )
X	    for ( col = 0, gP = grays[row]; col < cols; ++col, ++gP )
X		++hist[*gP];
X	size = rows * cols;
X	if ( ! specbvalue )
X	    { /* Compute bvalue from bpercent. */
X	    cutoff = size * bpercent / 100.0;
X	    count = 0;
X	    for ( bvalue = 0; bvalue <= maxval; ++bvalue )
X		{
X		count += hist[bvalue];
X		if ( count > cutoff )
X		break;
X		}
X	    }
X	if ( ! specwvalue )
X	    { /* Compute wvalue from wpercent. */
X	    cutoff = size * wpercent / 100.0;
X	    count = 0;
X	    for ( wvalue = maxval; wvalue >= 0; wvalue-- )
X		{
X		count += hist[wvalue];
X		if ( count > cutoff )
X		    break;
X		}
X	    }
X
X	/* Now rescale so that bvalue maps to 0, wvalue maps to maxval. */
X	pm_message(
X	    "remapping %d..%d to %d..%d", bvalue, wvalue, 0, maxval, 0 );
X	pgm_writepgminit( stdout, cols, rows, maxval, 0 );
X	for ( i = 0; i <= bvalue; ++i )
X	    hist[i] = 0;
X	for ( i = wvalue; i <= maxval; ++i )
X	    hist[i] = maxval;
X	range = wvalue - bvalue;
X	for ( i = bvalue + 1, val = maxval; i < wvalue; ++i, val += maxval )
X	    hist[i] = val / range;
X	for ( row = 0; row < rows; ++row )
X	    {
X	    for ( col = 0, gP = grays[row]; col < cols; ++col, ++gP )
X		*gP = hist[*gP];
X	    pgm_writepgmrow( stdout, grays[row], cols, maxval, 0 );
X	    }
X	}
X
X    exit( 0 );
X    }
SHAR_EOF
if test 5084 -ne "`wc -c < 'pgm/pgmnorm.c'`"
then
	echo shar: error transmitting "'pgm/pgmnorm.c'" '(should have been 5084 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.
