[comp.sources.misc] v09i031: PBMPLUS part 15 of 19: ppm.shar5 of 5

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (11/27/89)

Posting-number: Volume 9, Issue 31
Submitted-by: jef@helios.ee.lbl.gov (Jef Poskanzer)
Archive-name: pbmplus/part15

#! /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:
#	ppm/ppmcscale.c
#	ppm/ppmcscale.1
#	ppm/ilbmtoppm.c
#	ppm/ilbm.h
#	ppm/ilbmtoppm.1
#	ppm/ppmquant.c
#	ppm/ppmquant.1
#	ppm/ppmarith.c
#	ppm/ppmarith.1
# This archive created: Wed Nov 22 21:14:05 1989
# By:	Jef Poskanzer (Paratheo-Anametamystikhood Of Eris Esoteric, Ada Lovelace Cabal)
export PATH; PATH=/bin:$PATH
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmcscale.c'" '(1631 characters)'
if test -f 'ppm/ppmcscale.c'
then
	echo shar: will not over-write existing file "'ppm/ppmcscale.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmcscale.c'
X/* ppmcscale.c - scale the colors in a portable pixmap
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 <stdio.h>
X#include "ppm.h"
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd;
X    register pixel *pixelrow, *pP;
X    int argn, rows, cols, format, row;
X    register int col;
X    pixval maxval, newmaxval;
X    int i;
X    char *usage = "newmaxval [ppmfile]";
X
X    pm_progname = argv[0];
X
X    argn = 1;
X
X    if ( argn == argc )
X	pm_usage( usage );
X    if ( sscanf( argv[argn], "%d", &i ) != 1 )
X	pm_usage( usage );
X    newmaxval = i;
X    argn++;
X    if ( newmaxval < 1 )
X	pm_error( "newmaxval must be > 1", 0,0,0,0,0 );
X
X    if ( argn != argc )
X	{
X	ifd = pm_openr( argv[argn] );
X	argn++;
X	}
X    else
X	ifd = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    ppm_readppminit( ifd, &cols, &rows, &maxval, &format );
X    pixelrow = ppm_allocrow( cols );
X
X    ppm_writeppminit( stdout, cols, rows, newmaxval );
X
X    for ( row = 0; row < rows; row++ )
X	{
X	ppm_readppmrow( ifd, pixelrow, cols, maxval, format );
X
X	for ( col = 0, pP = pixelrow; col < cols; col++, pP++ )
X	    PPM_CSCALE( *pP, *pP, maxval, newmaxval );
X
X	ppm_writeppmrow( stdout, pixelrow, cols, newmaxval );
X	}
X
X    pm_close( ifd );
X
X    exit( 0 );
X    }
SHAR_EOF
if test 1631 -ne "`wc -c < 'ppm/ppmcscale.c'`"
then
	echo shar: error transmitting "'ppm/ppmcscale.c'" '(should have been 1631 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmcscale.1'" '(803 characters)'
if test -f 'ppm/ppmcscale.1'
then
	echo shar: will not over-write existing file "'ppm/ppmcscale.1'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmcscale.1'
X.TH ppmcscale 1 "27 February 1989"
X.SH NAME
Xppmcscale - scale the colors in a portable pixmap
X.SH SYNOPSIS
Xppmcscale newmaxval [ppmfile]
X.SH DESCRIPTION
XReads a portable pixmap as input.
XScales all the pixel values, and writes out the image with the new maxval.
X.PP
XScaling the colors down to a smaller maxval will result in some loss
Xof information.
X.SH "SEE ALSO"
Xppmquant(1), ppm(5)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both that
Xcopyright notice and this permission notice appear in supporting
Xdocumentation.  This software is provided "as is" without express or
Ximplied warranty.
SHAR_EOF
if test 803 -ne "`wc -c < 'ppm/ppmcscale.1'`"
then
	echo shar: error transmitting "'ppm/ppmcscale.1'" '(should have been 803 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ilbmtoppm.c'" '(8919 characters)'
if test -f 'ppm/ilbmtoppm.c'
then
	echo shar: will not over-write existing file "'ppm/ilbmtoppm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ilbmtoppm.c'
X/* ilbmtoppm.c - read an Amiga IFF ILBM file and produce a portable pixmap
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 <stdio.h>
X#include "ppm.h"
X#include "ilbm.h"
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd;
X    pixel *pixelrow, *colormap = 0;
X    int argn, colors, i, j, r, g, b, byte, bytes;
X    short rows, cols = 0, row, col;
X    int maxval;
X    char iffid[5];
X    unsigned char *body = 0, *bp, *ubp, *rawrow, *runbuf;
X    long formsize, bytesread, chunksize, viewportmodes = 0;
X    int nPlanes, masking, compression, xAsp, yAsp, ham, hammask, allPlanes;
X    unsigned char get_byte();
X
X    pm_progname = argv[0];
X
X    argn = 1;
X
X    if ( argn < argc )
X	{
X	ifd = pm_openr( argv[argn] );
X	argn++;
X	}
X    else
X	ifd = stdin;
X
X    if ( argn != argc )
X	pm_usage( "[ilbmfile]" );
X
X    /* Read in the ILBM file. */
X    iffid[4] = '\0';
X    getfourchars( ifd, iffid );
X    if ( strcmp( iffid, "FORM" ) != 0 )
X	pm_error( "input is not a FORM type IFF file", 0,0,0,0,0 );
X    if ( pm_readbiglong( ifd, &formsize ) == -1 )
X	pm_perror( 0 );
X    getfourchars( ifd, iffid );
X    if ( strcmp( iffid, "ILBM" ) != 0 )
X	pm_error( "input is not an ILBM type FORM IFF file", 0,0,0,0,0 );
X    bytesread = 12;
X
X    /* Main loop, parsing the IFF FORM. */
X    while ( bytesread < formsize )
X	{
X	getfourchars( ifd, iffid );
X	if ( pm_readbiglong( ifd, &chunksize ) == -1 )
X	    pm_perror( 0 );
X	bytesread += 8;
X
X	if ( body != 0 )
X	    {
X	    fprintf(
X		stderr, "%s: \"%s\" chunk found after BODY chunk -- skipping\n",
X		argv[0], iffid );
X	    for ( i = 0; i < chunksize; i++ )
X		(void) get_byte( ifd );
X	    }
X	else if ( strcmp( iffid, "BMHD" ) == 0 )
X	    {
X	    short junk;
X	    if ( pm_readbigshort( ifd, &cols ) == -1 )
X		pm_perror( 0 );
X	    if ( pm_readbigshort( ifd, &rows ) == -1 )
X		pm_perror( 0 );
X	    if ( pm_readbigshort( ifd, &junk ) == -1 )
X		pm_perror( 0 );
X	    if ( pm_readbigshort( ifd, &junk ) == -1 )
X		pm_perror( 0 );
X	    nPlanes = get_byte( ifd );
X	    masking = get_byte( ifd );
X	    compression = get_byte( ifd );
X	    (void) get_byte( ifd );	/* pad1 */
X	    if ( pm_readbigshort( ifd, &junk ) == -1 )	/* transparentColor */
X		pm_perror( 0 );
X	    xAsp = get_byte( ifd );
X	    yAsp = get_byte( ifd );
X	    if ( pm_readbigshort( ifd, &junk ) == -1 )	/* pageWidth */
X		pm_perror( 0 );
X	    if ( pm_readbigshort( ifd, &junk ) == -1 )	/* pageHeight */
X		pm_perror( 0 );
X	    }
X	else if ( strcmp( iffid, "CMAP" ) == 0 )
X	    {
X	    colors = chunksize / 3;
X	    if ( colors > 0 )
X		{
X		colormap = ppm_allocrow( colors );
X		for ( i = 0; i < colors; i++ )
X		    {
X		    r = get_byte( ifd );
X		    g = get_byte( ifd );
X		    b = get_byte( ifd );
X		    PPM_ASSIGN( colormap[i], r, g, b );
X		    }
X		if ( colors * 3 != chunksize )
X		    (void) get_byte( ifd );
X		}
X	    }
X	else if ( strcmp( iffid, "CAMG" ) == 0 )
X	    {
X	    if ( pm_readbiglong( ifd, &viewportmodes ) == -1 )
X		pm_perror( 0 );
X	    }
X	else if ( strcmp( iffid, "BODY" ) == 0 )
X	    {
X	    body = (unsigned char *) malloc( chunksize );
X	    if ( body == 0 )
X		pm_error( "out of memory", 0,0,0,0,0 );
X	    if ( fread( body, 1, chunksize, ifd ) != chunksize )
X		pm_error( "premature EOF reading BODY chunk", 0,0,0,0,0 );
X	    }
X	else if ( strcmp( iffid, "GRAB" ) == 0 ||
X	          strcmp( iffid, "DEST" ) == 0 ||
X	          strcmp( iffid, "SPRT" ) == 0 ||
X	          strcmp( iffid, "CRNG" ) == 0 ||
X	          strcmp( iffid, "CCRT" ) == 0 ||
X	          strcmp( iffid, "DPPV" ) == 0 )
X	    {
X	    for ( i = 0; i < chunksize; i++ )
X		(void) get_byte( ifd );
X	    }
X	else
X	    {
X	    fprintf(
X		stderr, "%s: unknown chunk type \"%s\" -- skipping\n",
X		argv[0], iffid );
X	    for ( i = 0; i < chunksize; i++ )
X		(void) get_byte( ifd );
X	    }
X
X	bytesread += chunksize;
X	}
X
X    pm_close( ifd );
X
X    /* Done reading.  Now interpret what we got. */
X    if ( cols == 0 )
X	pm_error( "no BMHD chunk found", 0,0,0,0,0 );
X    if ( body == 0 )
X	pm_error( "no BODY chunk found", 0,0,0,0,0 );
X    if ( xAsp != yAsp )
X	fprintf(
X	    stderr,
X	    "(Warning: non-square pixels; to fix do a 'ppmscale -%cscale %g'.)\n",
X	    xAsp > yAsp ? 'x' : 'y',
X	    xAsp > yAsp ? (float) xAsp / yAsp : (float) yAsp / xAsp );
X    if ( viewportmodes & vmHAM )
X	{
X	ham = 1;
X	hammask = ( 1 << ( nPlanes - 2 ) ) - 1;
X	maxval = ( 1 << ( nPlanes - 2 ) ) - 1;
X	if ( maxval > PPM_MAXMAXVAL )
X	    pm_error(
X	     "nPlanes is too large - try recompiling with a larger pixval type",
X		0,0,0,0,0 );
X	if ( colormap != 0 )
X	    for ( i = 0; i < colors; i++ )
X		{
X		r = PPM_GETR( colormap[i] ) >> ( 10 - nPlanes );
X		g = PPM_GETG( colormap[i] ) >> ( 10 - nPlanes );
X		b = PPM_GETB( colormap[i] ) >> ( 10 - nPlanes );
X		PPM_ASSIGN( colormap[i], r, g, b );
X		}
X	}
X    else
X	{
X	ham = 0;
X	if ( colormap != 0 )
X	    maxval = 255;		/* colormap contains bytes */
X	else
X	    maxval = ( 1 << nPlanes ) - 1;
X	if ( maxval > PPM_MAXMAXVAL )
X	    pm_error( "nPlanes is too large - try recompiling with a larger pixval type" );
X	}
X    if ( viewportmodes & vmEXTRA_HALFBRITE )
X	{
X	pixel *tempcolormap;
X	
X	tempcolormap = ppm_allocrow( colors * 2 );
X	for ( i = 0; i < colors; i++ )
X	    {
X	    tempcolormap[i] = colormap[i];
X	    PPM_ASSIGN(
X		tempcolormap[colors + i], PPM_GETR(colormap[i]) / 2,
X		PPM_GETG(colormap[i]) / 2, PPM_GETB(colormap[i]) / 2 );
X	    }
X	ppm_freerow( colormap );
X	colormap = tempcolormap;
X	colors *= 2;
X	}
X    if ( colormap == 0 )
X	fprintf(
X	    stderr, "(No colormap -- interpreting values as grayscale.)\n" );
X    allPlanes = nPlanes + ( masking == mskHasMask ? 1 : 0 );
X
X    ppm_writeppminit( stdout, cols, rows, (pixval) maxval );
X    pixelrow = ppm_allocrow( cols );
X    rawrow = (unsigned char *) malloc( cols );
X    if ( rawrow == 0 )
X	pm_error( "out of memory", 0,0,0,0,0 );
X    runbuf = (unsigned char *) malloc( RowBytes( cols ) );
X    if ( runbuf == 0 )
X	pm_error( "out of memory", 0,0,0,0,0 );
X
X    bp = body;
X    for ( row = 0; row < rows; row++ )
X	{
X	/* Extract rawrow from the image. */
X	for ( col = 0; col < cols; col++ )
X	    rawrow[col] = 0;
X	for ( i = 0; i < allPlanes; i++ )
X	    {
X	    switch ( compression )
X		{
X		case cmpNone:
X		ubp = bp;
X		bp += RowBytes( cols );
X		break;
X
X		case cmpByteRun1:
X		ubp = runbuf;
X		bytes = RowBytes( cols );
X		do
X		    {
X		    byte = *bp++;
X		    if ( byte <= 127 )
X			for ( j = byte, bytes -= j + 1; j >= 0; j-- )
X			    *ubp++ = *bp++;
X		    else if ( byte != 128 )
X			for ( j = 256 - byte, bytes -= j + 1, byte = *bp++;
X			      j >= 0; j-- )
X			    *ubp++ = byte;
X		    }
X		while ( bytes > 0 );
X		if ( bytes < 0 )
X		    pm_error( "error doing ByteRun decompression", 0,0,0,0,0 );
X		ubp = runbuf;
X		break;
X
X		default:
X		pm_error( "unknown compression type", 0,0,0,0,0 );
X		}
X
X	    if ( i >= nPlanes )
X		continue;	/* ignore mask plane */
X
X	    for ( col = 0; col < cols; col++ )
X		if ( ubp[col / 8] & ( 128 >> ( col % 8 ) ) )
X		    rawrow[col] |= 1 << i;
X	    }
X
X	/* Interpret rawrow into pixels. */
X	r = g = b = 0;
X	for ( col = 0; col < cols; col++ )
X	    if ( ham )
X		{ /* HAM mode. */
X		switch ( ( rawrow[col] >> nPlanes - 2 ) & 0x3 )
X		    {
X		    case 0:
X		    if ( colormap != 0 && colors >= maxval )
X			pixelrow[col] = colormap[rawrow[col] & hammask];
X		    else
X			PPM_ASSIGN(
X			    pixelrow[col], rawrow[col] & hammask,
X			    rawrow[col] & hammask, rawrow[col] & hammask );
X		    r = PPM_GETR( pixelrow[col] );
X		    g = PPM_GETG( pixelrow[col] );
X		    b = PPM_GETB( pixelrow[col] );
X		    break;
X
X		    case 1:
X		    b = rawrow[col] & hammask;
X		    PPM_ASSIGN( pixelrow[col], r, g, b );
X		    break;
X
X		    case 2:
X		    r = rawrow[col] & hammask;
X		    PPM_ASSIGN( pixelrow[col], r, g, b );
X		    break;
X
X		    case 3:
X		    g = rawrow[col] & hammask;
X		    PPM_ASSIGN( pixelrow[col], r, g, b );
X		    break;
X
X		    default:
X		    pm_error( "impossible HAM code", 0,0,0,0,0 );
X		    }
X		}
X	    else if ( colormap != 0 )
X		/* Non-HAM colormapped. */
X		pixelrow[col] = colormap[rawrow[col]];
X	    else
X		/* Non-HAM direct -- weird. */
X		PPM_ASSIGN(
X		    pixelrow[col], rawrow[col], rawrow[col], rawrow[col] );
X
X	/* And write out the row. */
X	ppm_writeppmrow( stdout, pixelrow, cols, (pixval) maxval );
X	}
X
X    exit( 0 );
X    }
X
Xunsigned char
Xget_byte( f )
XFILE *f;
X    {
X    int i;
X
X    i = getc( f );
X    if ( i == EOF )
X	pm_error( "premature EOF", 0,0,0,0,0 );
X
X    return (unsigned char) i;
X    }
X
Xgetfourchars( f, fourchars )
XFILE *f;
Xchar fourchars[4];
X    {
X    fourchars[0] = get_byte( f );
X    fourchars[1] = get_byte( f );
X    fourchars[2] = get_byte( f );
X    fourchars[3] = get_byte( f );
X    }
SHAR_EOF
if test 8919 -ne "`wc -c < 'ppm/ilbmtoppm.c'`"
then
	echo shar: error transmitting "'ppm/ilbmtoppm.c'" '(should have been 8919 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ilbm.h'" '(702 characters)'
if test -f 'ppm/ilbm.h'
then
	echo shar: will not over-write existing file "'ppm/ilbm.h'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ilbm.h'
X/* ilbm.h - header file for Amiga IFF ILBM files
X*/
X
X#define RowBytes(cols)		( ( ( (cols) + 15 ) / 16 ) * 2 )
X
X/* definitions for BMHD */
X
Xtypedef struct
X    {
X    unsigned short w, h;
X    short x, y;
X    unsigned char nPlanes, masking, compression, pad1;
X    unsigned short transparentColor;
X    unsigned char xAspect, yAspect;
X    short pageWidth, pageHeight;
X    } BitMapHeader;
X
X#define mskNone			0
X#define mskHasMask		1
X#define mskHasTransparentColor	2
X#define mskLasso		3
X
X#define cmpNone			0
X#define cmpByteRun1		1
X
X/* definitions for CMAP */
X
Xtypedef struct
X    {
X    unsigned char r, g, b;
X    } ColorRegister;
X
X/* definitions for CAMG */
X
X#define	vmEXTRA_HALFBRITE	0x80
X#define	vmHAM			0x800
SHAR_EOF
if test 702 -ne "`wc -c < 'ppm/ilbm.h'`"
then
	echo shar: error transmitting "'ppm/ilbm.h'" '(should have been 702 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ilbmtoppm.1'" '(716 characters)'
if test -f 'ppm/ilbmtoppm.1'
then
	echo shar: will not over-write existing file "'ppm/ilbmtoppm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ilbmtoppm.1'
X.TH ilbmtoppm 1 "30 March 1989"
X.SH NAME
Xilbmtoppm - convert Amiga IFF ILBM file into a portable pixmap
X.SH SYNOPSIS
Xilbmtoppm [ilbmfile]
X.SH DESCRIPTION
XReads an Amiga IFF ILBM file as input.
XProduces a portable pixmap as output.
XHandles HAM and EXTRA_HALFBRIGHT, no problem.
X.SH "SEE ALSO"
Xppm(5)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both that
Xcopyright notice and this permission notice appear in supporting
Xdocumentation.  This software is provided "as is" without express or
Ximplied warranty.
SHAR_EOF
if test 716 -ne "`wc -c < 'ppm/ilbmtoppm.1'`"
then
	echo shar: error transmitting "'ppm/ilbmtoppm.1'" '(should have been 716 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmquant.c'" '(15723 characters)'
if test -f 'ppm/ppmquant.c'
then
	echo shar: will not over-write existing file "'ppm/ppmquant.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmquant.c'
X/* ppmquant.c - quantize the colors in a pixmap down to a specified number
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 <stdio.h>
X#include "ppm.h"
X#include "ppmcmap.h"
X#ifdef SYSV
X#include <string.h>
X#define srandom srand
X#define random rand
X#else /*SYSV*/
X#include <strings.h>
X#endif /*SYSV*/
X
X#define min(a,b) ((a) < (b) ? (a) : (b))
X#define max(a,b) ((a) > (b) ? (a) : (b))
X
X#define MAXCOLORS 32768
X#define CLUSTER_MAXVAL 63
X
X/* #define LARGE_NORM /**/
X#define LARGE_LUM /**/
X
X/* #define REP_CENTER_BOX /**/
X/* #define REP_AVERAGE_COLORS /**/
X#define REP_AVERAGE_PIXELS /**/
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd;
X    register pixel **pixels, *pP;
X    int argn, rows, cols, row;
X    register int col, limitcol;
X    pixval maxval;
X    int newcolors, colors;
X    register int index;
X    colorhist_vector chv, colormap, mediancut();
X    colorhash_table cht;
X    int floyd;
X    long *thisrerr, *nextrerr, *thisgerr, *nextgerr, *thisberr, *nextberr,
X	*temperr;
X    register long sr, sg, sb;
X#define FS_SCALE 1024
X    int fs_direction;
X    pixval err;
X    char *usage = "[-floyd|-fs] <newcolors> [ppmfile]";
X
X    pm_progname = argv[0];
X
X    argn = 1;
X    floyd = 0;
X
X    if ( argn < argc && argv[argn][0] == '-' )
X	{
X	if ( strncmp(argv[argn],"-fs",max(strlen(argv[argn]),2)) == 0 ||
X	     strncmp(argv[argn],"-floyd",max(strlen(argv[argn]),2)) == 0 )
X	    floyd = 1;
X	else
X	    pm_usage( usage );
X	argn++;
X	}
X
X    if ( argn == argc )
X	pm_usage( usage );
X    if ( sscanf( argv[argn], "%d", &newcolors ) != 1 )
X	pm_usage( usage );
X    argn++;
X    if ( newcolors <= 1 )
X	pm_error( "number of colors must be > 1", 0,0,0,0,0 );
X
X    if ( argn != argc )
X	{
X	ifd = pm_openr( argv[argn] );
X	argn++;
X	}
X    else
X	ifd = stdin;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    /*
X    ** Step 0: read in the image.
X    */
X    pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
X    pm_close( ifd );
X
X
X    /*
X    ** Step 1: attempt to make a histogram of the colors, unclustered.
X    */
X    fprintf( stderr, "(Making histogram...  " );
X    fflush( stderr );
X    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
X    if ( chv == (colorhist_vector) 0 )
X	{
X	fprintf( stderr, "Too many colors.)\n" );
X	/*
X	** Step 2: try lowering maxval, to increase color coherence.
X	*/
X	if ( maxval <= CLUSTER_MAXVAL )
X	    { /* (This is not likely to happen.) */
X	    fprintf(
X		stderr, "(Try recompiling with a smaller CLUSTER_MAXVAL.)\n" );
X	    exit( 1 );
X	    }
X	fprintf(
X	    stderr,
X	    "(Scaling colors from maxval=%d to maxval=%d to improve clustering...  ",
X	    maxval, CLUSTER_MAXVAL );
X	fflush( stderr );
X	for ( row = 0; row < rows; row++ )
X	    for ( col = 0, pP = pixels[row]; col < cols; col++, pP++ )
X		PPM_CSCALE( *pP, *pP, maxval, CLUSTER_MAXVAL );
X	maxval = CLUSTER_MAXVAL;
X	fprintf( stderr, "Done.)\n" );
X
X	fprintf( stderr, "(Trying histogram again...  " );
X	fflush( stderr );
X	chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
X	if ( chv == (colorhist_vector) 0 )
X	    {
X	    fprintf(
X		stderr, "Still too many colors - try recompiling with a smaller CLUSTER_MAXVAL.)\n" );
X	    exit( 1 );
X	    }
X	}
X    fprintf( stderr, "Done.  %d colors found.)\n", colors );
X
X    /*
X    ** Step 3: apply median-cut to histogram, making the new colormap.
X    */
X    fprintf( stderr, "(Choosing %d colors...  ", newcolors );
X    fflush( stderr );
X    colormap = mediancut( chv, colors, rows * cols, maxval, newcolors );
X    ppm_freecolorhist( chv );
X    fprintf( stderr, "Done.)\n" );
X
X    /*
X    ** Step 4: map the colors in the image to their closest match in the
X    ** new colormap, and write 'em out.
X    */
X    fprintf( stderr, "(Mapping image to new colors...  " );
X    fflush( stderr );
X    cht = ppm_alloccolorhash( );
X    ppm_writeppminit( stdout, cols, rows, maxval );
X    if ( floyd )
X	{
X	/* Initialize Floyd-Steinberg error vectors. */
X	thisrerr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	nextrerr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	thisgerr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	nextgerr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	thisberr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	nextberr = (long *) pm_allocrow( cols + 2, sizeof(long) );
X	srandom( (int) time( 0 ) );
X	for ( col = 0; col < cols + 2; col++ )
X	    {
X	    thisrerr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
X	    thisgerr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
X	    thisberr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
X	    /* (random errors in [-1 .. 1]) */
X	    }
X	fs_direction = 1;
X	}
X    for ( row = 0; row < rows; row++ )
X	{
X	if ( floyd )
X	    for ( col = 0; col < cols + 2; col++ )
X		nextrerr[col] = nextgerr[col] = nextberr[col] = 0;
X	if ( ( ! floyd ) || fs_direction )
X	    {
X	    col = 0;
X	    limitcol = cols;
X	    pP = pixels[row];
X	    }
X	else
X	    {
X	    col = cols - 1;
X	    limitcol = -1;
X	    pP = &(pixels[row][col]);
X	    }
X	do
X	    {
X	    if ( floyd )
X		{
X		/* Use Floyd-Steinberg errors to adjust actual color. */
X		sr = PPM_GETR(*pP) * FS_SCALE + thisrerr[col + 1];
X		sg = PPM_GETG(*pP) * FS_SCALE + thisgerr[col + 1];
X		sb = PPM_GETB(*pP) * FS_SCALE + thisberr[col + 1];
X		PPM_ASSIGN( *pP, sr / FS_SCALE, sg / FS_SCALE, sb / FS_SCALE );
X		}
X
X	    /* Check hash table to see if we have already matched this color. */
X	    index = ppm_lookupcolor( cht, *pP );
X	    if ( index == -1 )
X		{ /* No; search colormap for closest match. */
X		register int i, r1, g1, b1, r2, g2, b2;
X		register long dist, newdist;
X		r1 = PPM_GETR( *pP );
X		g1 = PPM_GETG( *pP );
X		b1 = PPM_GETB( *pP );
X		dist = 2000000000;
X		for ( i = 0; i < newcolors; i++ )
X		    {
X		    r2 = PPM_GETR( colormap[i].color );
X		    g2 = PPM_GETG( colormap[i].color );
X		    b2 = PPM_GETB( colormap[i].color );
X		    newdist = ( r1 - r2 ) * ( r1 - r2 ) +
X			      ( g1 - g2 ) * ( g1 - g2 ) +
X			      ( b1 - b2 ) * ( b1 - b2 );
X		    if ( newdist < dist )
X			{
X			index = i;
X			dist = newdist;
X			}
X		    }
X		ppm_addtocolorhash( cht, *pP, index );
X		}
X
X	    if ( floyd )
X		{
X		/* Propagate Floyd-Steinberg error terms. */
X		if ( fs_direction )
X		    {
X		    err = sr - PPM_GETR( colormap[index].color ) * FS_SCALE;
X		    thisrerr[col + 2] += ( err * 7 ) / 16;
X		    nextrerr[col    ] += ( err * 3 ) / 16;
X		    nextrerr[col + 1] += ( err * 5 ) / 16;
X		    nextrerr[col + 2] += ( err     ) / 16;
X		    err = sg - PPM_GETG( colormap[index].color ) * FS_SCALE;
X		    thisgerr[col + 2] += ( err * 7 ) / 16;
X		    nextgerr[col    ] += ( err * 3 ) / 16;
X		    nextgerr[col + 1] += ( err * 5 ) / 16;
X		    nextgerr[col + 2] += ( err     ) / 16;
X		    err = sb - PPM_GETB( colormap[index].color ) * FS_SCALE;
X		    thisberr[col + 2] += ( err * 7 ) / 16;
X		    nextberr[col    ] += ( err * 3 ) / 16;
X		    nextberr[col + 1] += ( err * 5 ) / 16;
X		    nextberr[col + 2] += ( err     ) / 16;
X		    }
X		else
X		    {
X		    err = sr - PPM_GETR( colormap[index].color ) * FS_SCALE;
X		    thisrerr[col    ] += ( err * 7 ) / 16;
X		    nextrerr[col + 2] += ( err * 3 ) / 16;
X		    nextrerr[col + 1] += ( err * 5 ) / 16;
X		    nextrerr[col    ] += ( err     ) / 16;
X		    err = sg - PPM_GETG( colormap[index].color ) * FS_SCALE;
X		    thisgerr[col    ] += ( err * 7 ) / 16;
X		    nextgerr[col + 2] += ( err * 3 ) / 16;
X		    nextgerr[col + 1] += ( err * 5 ) / 16;
X		    nextgerr[col    ] += ( err     ) / 16;
X		    err = sb - PPM_GETB( colormap[index].color ) * FS_SCALE;
X		    thisberr[col    ] += ( err * 7 ) / 16;
X		    nextberr[col + 2] += ( err * 3 ) / 16;
X		    nextberr[col + 1] += ( err * 5 ) / 16;
X		    nextberr[col    ] += ( err     ) / 16;
X		    }
X		}
X
X	    *pP = colormap[index].color;
X
X	    if ( ( ! floyd ) || fs_direction )
X		{
X		col++;
X		pP++;
X		}
X	    else
X		{
X		col--;
X		pP--;
X		}
X	    }
X	while ( col != limitcol );
X
X	if ( floyd )
X	    {
X	    temperr = thisrerr;
X	    thisrerr = nextrerr;
X	    nextrerr = temperr;
X	    temperr = thisgerr;
X	    thisgerr = nextgerr;
X	    nextgerr = temperr;
X	    temperr = thisberr;
X	    thisberr = nextberr;
X	    nextberr = temperr;
X	    fs_direction = ! fs_direction;
X	    }
X
X	ppm_writeppmrow( stdout, pixels[row], cols, maxval );
X	}
X    fprintf( stderr, "Done.)\n" );
X
X    exit( 0 );
X    }
X
X/*
X** Here is the fun part, the median-cut colormap generator.  This is based
X** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
X** Display", SIGGRAPH '82 Proceedings, page 297.
X*/
X
Xtypedef struct box *box_vector;
Xstruct box
X    {
X    int index;
X    int colors;
X    int sum;
X    };
X
Xcolorhist_vector
Xmediancut( chv, colors, sum, maxval, newcolors )
Xcolorhist_vector chv;
Xint colors, sum, newcolors;
Xpixval maxval;
X    {
X    colorhist_vector colormap;
X    box_vector bv;
X    register int bi, i;
X    int boxes;
X    int redcompare(), greencompare(), bluecompare(), sumcompare();
X
X    bv = (box_vector) malloc( sizeof(struct box) * newcolors );
X    colormap =
X	(colorhist_vector) malloc( sizeof(struct colorhist_item) * newcolors );
X    if ( bv == (box_vector) 0 || colormap == (colorhist_vector) 0 )
X	pm_error( "out of memory", 0,0,0,0,0 );
X    for ( i = 0; i < newcolors; i++ )
X	PPM_ASSIGN( colormap[i].color, 0, 0, 0 );
X
X    /*
X    ** Set up the initial box.
X    */
X    bv[0].index = 0;
X    bv[0].colors = colors;
X    bv[0].sum = sum;
X    boxes = 1;
X
X    /*
X    ** Main loop: split boxes until we have enough.
X    */
X    while ( boxes < newcolors )
X	{
X	register int indx, clrs;
X	int sm;
X	register int minr, maxr, ming, maxg, minb, maxb, v;
X	int halfsum, lowersum;
X
X	/*
X	** Find the first splittable box.
X	*/
X	for ( bi = 0; bv[bi].colors < 2 && bi < boxes; bi++ )
X	    ;
X	if ( bi == boxes )
X	    break;	/* ran out of colors! */
X	indx = bv[bi].index;
X	clrs = bv[bi].colors;
X	sm = bv[bi].sum;
X
X	/*
X	** Go through the box finding the minimum and maximum of each
X	** component -- the boundaries of the box.
X	*/
X	minr = maxr = PPM_GETR( chv[indx].color );
X	ming = maxg = PPM_GETG( chv[indx].color );
X	minb = maxb = PPM_GETB( chv[indx].color );
X	for ( i = 1; i < clrs; i++ )
X	    {
X	    v = PPM_GETR( chv[indx + i].color );
X	    if ( v < minr ) minr = v;
X	    if ( v > maxr ) maxr = v;
X	    v = PPM_GETG( chv[indx + i].color );
X	    if ( v < ming ) ming = v;
X	    if ( v > maxg ) maxg = v;
X	    v = PPM_GETB( chv[indx + i].color );
X	    if ( v < minb ) minb = v;
X	    if ( v > maxb ) maxb = v;
X	    }
X
X	/*
X	** Find the largest dimension, and sort by that component.  I have
X	** included two methods for determining the "largest" dimension;
X	** first by simply comparing the range in RGB space, and second
X	** by transforming into luminosities before the comparison.  You
X	** can switch which method is used by switching the commenting on
X	** the LARGE_ defines at the beginning of this source file.
X	*/
X#ifdef LARGE_NORM
X	if ( maxr - minr >= maxg - ming && maxr - minr >= maxb - minb )
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		redcompare );
X	else if ( maxg - ming >= maxb - minb )
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		greencompare );
X	else
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		bluecompare );
X#endif /*LARGE_NORM*/
X#ifdef LARGE_LUM
X	{
X	pixel	p;
X	float	rl, gl, bl;
X
X	PPM_ASSIGN(p, maxr - minr, 0, 0);
X	rl = PPM_LUMIN(p);
X	PPM_ASSIGN(p, 0, maxg - ming, 0);
X	gl = PPM_LUMIN(p);
X	PPM_ASSIGN(p, 0, 0, maxb - minb);
X	bl = PPM_LUMIN(p);
X
X	if ( rl >= gl && rl >= bl )
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		redcompare );
X	else if ( gl >= bl )
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		greencompare );
X	else
X	    qsort(
X		(char *) &(chv[indx]), clrs, sizeof(struct colorhist_item),
X		bluecompare );
X	}
X#endif /*LARGE_LUM*/
X	
X	/*
X	** Now find the median based on the counts, so that about half the
X	** pixels (not colors, pixels) are in each subdivision.
X	*/
X	lowersum = chv[indx].value;
X	halfsum = sm / 2;
X	for ( i = 1; i < clrs - 1; i++ )
X	    {
X	    if ( lowersum >= halfsum )
X		break;
X	    lowersum += chv[indx + i].value;
X	    }
X
X	/*
X	** Split the box, and sort to bring the biggest box to the top.
X	*/
X	bv[bi].colors = i;
X	bv[bi].sum = lowersum;
X	bv[boxes].index = indx + i;
X	bv[boxes].colors = clrs - i;
X	bv[boxes].sum = sm - lowersum;
X	boxes++;
X	qsort( (char *) bv, boxes, sizeof(struct box), sumcompare );
X	}
X
X    /*
X    ** Ok, we've got enough boxes.  Now choose a representative color for
X    ** each box.  There are a number of possible ways to make this choice.
X    ** One would be to choose the center of the box; this ignores any structure
X    ** within the boxes.  Another method would be to average all the colors in
X    ** the box -- this is the method specified in Heckbert's paper.  A third
X    ** method is to average all the pixels in the box.  You can switch which
X    ** method is used by switching the commenting on the REP_ defines at
X    ** the beginning of this source file.
X    */
X    for ( bi = 0; bi < boxes; bi++ )
X	{
X#ifdef REP_CENTER_BOX
X	register int indx = bv[bi].index;
X	register int clrs = bv[bi].colors;
X	register int minr, maxr, ming, maxg, minb, maxb, v;
X
X	minr = maxr = PPM_GETR( chv[indx].color );
X	ming = maxg = PPM_GETG( chv[indx].color );
X	minb = maxb = PPM_GETB( chv[indx].color );
X	for ( i = 1; i < clrs; i++ )
X	    {
X	    v = PPM_GETR( chv[indx + i].color );
X	    minr = min( minr, v );
X	    maxr = max( maxr, v );
X	    v = PPM_GETG( chv[indx + i].color );
X	    ming = min( ming, v );
X	    maxg = max( maxg, v );
X	    v = PPM_GETB( chv[indx + i].color );
X	    minb = min( minb, v );
X	    maxb = max( maxb, v );
X	    }
X	PPM_ASSIGN(
X	    colormap[bi].color, ( minr + maxr ) / 2, ( ming + maxg ) / 2,
X	    ( minb + maxb ) / 2 );
X#endif /*REP_CENTER_BOX*/
X#ifdef REP_AVERAGE_COLORS
X	register int indx = bv[bi].index;
X	register int clrs = bv[bi].colors;
X	register long r = 0, g = 0, b = 0;
X
X	for ( i = 0; i < clrs; i++ )
X	    {
X	    r += PPM_GETR( chv[indx + i].color );
X	    g += PPM_GETG( chv[indx + i].color );
X	    b += PPM_GETB( chv[indx + i].color );
X	    }
X	r = r / clrs;
X	g = g / clrs;
X	b = b / clrs;
X	PPM_ASSIGN( colormap[bi].color, r, g, b );
X#endif /*REP_AVERAGE_COLORS*/
X#ifdef REP_AVERAGE_PIXELS
X	register int indx = bv[bi].index;
X	register int clrs = bv[bi].colors;
X	register long r = 0, g = 0, b = 0, sum = 0;
X
X	for ( i = 0; i < clrs; i++ )
X	    {
X	    r += PPM_GETR( chv[indx + i].color ) * chv[indx + i].value;
X	    g += PPM_GETG( chv[indx + i].color ) * chv[indx + i].value;
X	    b += PPM_GETB( chv[indx + i].color ) * chv[indx + i].value;
X	    sum += chv[indx + i].value;
X	    }
X	r = r / sum;
X	if ( r > maxval ) r = maxval;	/* avoid math errors */
X	g = g / sum;
X	if ( g > maxval ) g = maxval;
X	b = b / sum;
X	if ( b > maxval ) b = maxval;
X	PPM_ASSIGN( colormap[bi].color, r, g, b );
X#endif /*REP_AVERAGE_PIXELS*/
X	}
X
X    /*
X    ** All done.
X    */
X    return colormap;
X    }
X
Xint
Xredcompare( ch1, ch2 )
Xcolorhist_vector ch1, ch2;
X    {
X    return (int) PPM_GETR( ch1->color ) - (int) PPM_GETR( ch2->color );
X    }
X
Xint
Xgreencompare( ch1, ch2 )
Xcolorhist_vector ch1, ch2;
X    {
X    return (int) PPM_GETG( ch1->color ) - (int) PPM_GETG( ch2->color );
X    }
X
Xint
Xbluecompare( ch1, ch2 )
Xcolorhist_vector ch1, ch2;
X    {
X    return (int) PPM_GETB( ch1->color ) - (int) PPM_GETB( ch2->color );
X    }
X
Xint
Xsumcompare( b1, b2 )
Xbox_vector b1, b2;
X    {
X    return b2->sum - b1->sum;
X    }
SHAR_EOF
if test 15723 -ne "`wc -c < 'ppm/ppmquant.c'`"
then
	echo shar: error transmitting "'ppm/ppmquant.c'" '(should have been 15723 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmquant.1'" '(1258 characters)'
if test -f 'ppm/ppmquant.1'
then
	echo shar: will not over-write existing file "'ppm/ppmquant.1'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmquant.1'
X.TH ppmquant 1 "18 May 1989"
X.SH NAME
Xppmquant - quantize the colors in a portable pixmap down to a specified number
X.SH SYNOPSIS
Xppmquant [-floyd|-fs] <ncolors> [ppmfile]
X.SH DESCRIPTION
XReads a portable pixmap as input.
XChooses <ncolors> colors to best represent the image, maps the existing colors
Xto the new ones, and writes a portable pixmap as output.
X.PP
XThe quantization method is Heckbert's "median cut".
X.PP
XThe -floyd flag adds a Floyd-Steinberg error diffusion step.
XThis may give better results on images where the unmodified quantization
Xhas banding or other artifacts.
XIt takes maybe 20% more CPU time.
X.PP
XAll flags can be abbreviated to their shortest unique prefix.
X.SH REFERENCES
X"Color Image Quantization for Frame Buffer Display" by Paul Heckbert,
XSIGGRAPH '82 Proceedings, page 297.
X.SH "SEE ALSO"
Xppmcscale(1), ppm(5)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both that
Xcopyright notice and this permission notice appear in supporting
Xdocumentation.  This software is provided "as is" without express or
Ximplied warranty.
SHAR_EOF
if test 1258 -ne "`wc -c < 'ppm/ppmquant.1'`"
then
	echo shar: error transmitting "'ppm/ppmquant.1'" '(should have been 1258 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmarith.c'" '(3432 characters)'
if test -f 'ppm/ppmarith.c'
then
	echo shar: will not over-write existing file "'ppm/ppmarith.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmarith.c'
X/* ppmarith.c - perform arithmetic on two portable pixmaps
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 <stdio.h>
X#include "ppm.h"
X#ifdef SYSV
X#include <string.h>
X#else /*SYSV*/
X#include <strings.h>
X#endif /*SYSV*/
X
X#define max(a,b) ((a) > (b) ? (a) : (b))
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X    {
X    FILE *ifd1, *ifd2;
X    register pixel *pixrow1, *pixrow2, *p1P, *p2P;
X    pixval maxval1, maxval2, maxval3;
X    int argn, rows1, cols1, format1, rows2, cols2, format2, row, col;
X    char function;
X    char *usage = "-add|-subtract|-multiply ppmfile1 ppmfile2";
X
X    pm_progname = argv[0];
X
X    argn = 1;
X    function = ' ';
X
X    /* Check for flags. */
X    if ( argn < argc && argv[argn][0] == '-' )
X	{
X	if ( strncmp(argv[argn],"-add",max(strlen(argv[argn]),2)) == 0 )
X	    function = '+';
X	else if ( strncmp(argv[argn],"-subtract",max(strlen(argv[argn]),2)) == 0 )
X	    function = '-';
X	else if ( strncmp(argv[argn],"-multiply",max(strlen(argv[argn]),2)) == 0 )
X	    function = '*';
X	else
X	    pm_usage( usage );
X	argn++;
X	}
X
X    if ( function == ' ' )
X	pm_usage( usage );
X
X    if ( argn == argc )
X	pm_usage( usage );
X    ifd1 = pm_openr( argv[argn] );
X    argn++;
X
X    if ( argn == argc )
X	pm_usage( usage );
X    ifd2 = pm_openr( argv[argn] );
X    argn++;
X
X    if ( argn != argc )
X	pm_usage( usage );
X
X    ppm_readppminit( ifd1, &cols1, &rows1, &maxval1, &format1 );
X    pixrow1 = ppm_allocrow( cols1 );
X    ppm_readppminit( ifd2, &cols2, &rows2, &maxval2, &format2 );
X    if ( cols2 != cols1 || rows2 != rows1 )
X	pm_error(
X	    "the two pixmaps must be the same width and height", 0,0,0,0,0 );
X    pixrow2 = ppm_allocrow( cols1 );
X
X    maxval3 = max( maxval1, maxval2 );
X    ppm_writeppminit( stdout, cols1, rows1, maxval3 );
X    for ( row = 0; row < rows1; row++ )
X	{
X	ppm_readppmrow( ifd1, pixrow1, cols1, maxval1, format1 );
X	ppm_readppmrow( ifd2, pixrow2, cols1, maxval2, format2 );
X        for ( col = 0, p1P = pixrow1, p2P = pixrow2; col < cols1; col++, p1P++, p2P++ )
X	    {
X	    int r1, g1, b1, r2, g2, b2;
X
X	    if ( maxval1 != maxval3 )
X		PPM_CSCALE( *p1P, *p1P, maxval1, maxval3 );
X	    r1 = PPM_GETR( *p1P );
X	    g1 = PPM_GETG( *p1P );
X	    b1 = PPM_GETB( *p1P );
X	    if ( maxval2 != maxval3 )
X		PPM_CSCALE( *p2P, *p2P, maxval2, maxval3 );
X	    r2 = PPM_GETR( *p2P );
X	    g2 = PPM_GETG( *p2P );
X	    b2 = PPM_GETB( *p2P );
X	    switch ( function )
X		{
X		case '+':
X		r1 += r2;
X		g1 += g2;
X		b1 += b2;
X		break;
X
X		case '-':
X		r1 -= r2;
X		g1 -= g2;
X		b1 -= b2;
X		break;
X
X		case '*':
X		r1 = r1 * r2 / maxval3;
X		g1 = g1 * g2 / maxval3;
X		b1 = b1 * b2 / maxval3;
X		break;
X
X		default:
X		pm_error( "can't happen", 0,0,0,0,0 );
X		}
X	    if ( r1 < 0 ) r1 = 0;
X	    else if ( r1 > maxval3 ) r1 = maxval3;
X	    if ( g1 < 0 ) g1 = 0;
X	    else if ( g1 > maxval3 ) g1 = maxval3;
X	    if ( b1 < 0 ) b1 = 0;
X	    else if ( b1 > maxval3 ) b1 = maxval3;
X	    PPM_ASSIGN( *p1P, r1, g1, b1 );
X	    }
X	ppm_writeppmrow( stdout, pixrow1, cols1, maxval3 );
X	}
X
X    pm_close( ifd1 );
X    pm_close( ifd2 );
X
X    exit( 0 );
X    }
SHAR_EOF
if test 3432 -ne "`wc -c < 'ppm/ppmarith.c'`"
then
	echo shar: error transmitting "'ppm/ppmarith.c'" '(should have been 3432 characters)'
fi
fi # end of overwriting check
if test ! -d 'ppm'
then
	echo shar: creating directory "'ppm'"
	mkdir 'ppm'
fi
echo shar: extracting "'ppm/ppmarith.1'" '(1098 characters)'
if test -f 'ppm/ppmarith.1'
then
	echo shar: will not over-write existing file "'ppm/ppmarith.1'"
else
sed 's/^X//' << \SHAR_EOF > 'ppm/ppmarith.1'
X.TH ppmarith 1 "08 August 1989"
X.SH NAME
Xppmarith - perform arithmetic on two portable pixmaps
X.SH SYNOPSIS
Xppmarith -add|-subtract|-multiply ppmfile1 ppmfile2
X.SH DESCRIPTION
XReads two portable bitmaps as input.
XPerforms the specified arithmetic operation,
Xand produces a portable bitmap as output.
XThe two input pixmaps must be the same width and height.
X.PP
XThe arithmetic is performed between corresponding pixels in the two
Xbitmaps, with maxval as 1.0, black as 0.0, and a linear scale in between.
XResults that fall outside of [0..1) are truncated.
X.PP
XAll flags can be abbreviated to their shortest unique prefix.
X.SH "SEE ALSO"
Xpbmmask(1), pbmpaste(1), pnminvert(1), ppm(5)
X.SH AUTHOR
XCopyright (C) 1989 by Jef Poskanzer.
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both that
Xcopyright notice and this permission notice appear in supporting
Xdocumentation.  This software is provided "as is" without express or
Ximplied warranty.
SHAR_EOF
if test 1098 -ne "`wc -c < 'ppm/ppmarith.1'`"
then
	echo shar: error transmitting "'ppm/ppmarith.1'" '(should have been 1098 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0