PBMPLUS, part 14 of 18
Jef Poskanzer
pokey at well.UUCP
Thu Sep 14 21:25:57 AEST 1989
#! /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: Thu Sep 14 03:43:46 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'" '(8685 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, rows, cols = 0, row, col, colors, i, j, r, g, b, byte, bytes;
X pixval 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 short get_big_short();
X long get_big_long();
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 formsize = get_big_long( ifd );
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 chunksize = get_big_long( ifd );
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 cols = get_big_short( ifd );
X rows = get_big_short( ifd );
X (void) get_big_short( ifd ); /* x */
X (void) get_big_short( ifd ); /* y */
X nPlanes = get_byte( ifd );
X masking = get_byte( ifd );
X compression = get_byte( ifd );
X (void) get_byte( ifd ); /* pad1 */
X (void) get_big_short( ifd ); /* transparentColor */
X xAsp = get_byte( ifd );
X yAsp = get_byte( ifd );
X (void) get_big_short( ifd ); /* pageWidth */
X (void) get_big_short( ifd ); /* pageHeight */
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 viewportmodes = get_big_long( ifd );
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 ( 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 }
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, 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, 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 }
X
Xshort
Xget_big_short( f )
XFILE *f;
X {
X short s;
X
X s = get_byte( f ) << 8;
X s |= get_byte( f );
X
X return s;
X }
X
Xlong
Xget_big_long( f )
XFILE *f;
X {
X long l;
X
X l = get_byte( f ) << 24;
X l |= get_byte( f ) << 16;
X l |= get_byte( f ) << 8;
X l |= get_byte( f );
X
X return l;
X }
SHAR_EOF
if test 8685 -ne "`wc -c < 'ppm/ilbmtoppm.c'`"
then
echo shar: error transmitting "'ppm/ilbmtoppm.c'" '(should have been 8685 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'" '(14526 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#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#include "ppm.h"
X#include "ppmcmap.h"
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
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.
X */
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
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.
X */
X/* #define CENTER_BOX */
X/* #define AVERAGE_COLORS */
X#define AVERAGE_PIXELS
X for ( bi = 0; bi < boxes; bi++ )
X {
X#ifdef 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 CENTER_BOX
X#ifdef 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 AVERAGE_COLORS
X#ifdef 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 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 14526 -ne "`wc -c < 'ppm/ppmquant.c'`"
then
echo shar: error transmitting "'ppm/ppmquant.c'" '(should have been 14526 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'" '(3424 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#ifdef SYSV
X#include <string.h>
X#else SYSV
X#include <strings.h>
X#endif SYSV
X#include "ppm.h"
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 3424 -ne "`wc -c < 'ppm/ppmarith.c'`"
then
echo shar: error transmitting "'ppm/ppmarith.c'" '(should have been 3424 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
More information about the Alt.sources
mailing list