[comp.sources.misc] v14i006: ataritoppm, convert between Atari and pbmplus image formats

seb3@gte.com (Steve Belczyk) (07/16/90)

Posting-number: Volume 14, Issue 6
Submitted-by: seb3@gte.com (Steve Belczyk)
Archive-name: ataritoppm/part01

Hello Brandon.  Please accept this contribution to comp.sources.misc.
Included are five programs which convert between Jef Poskanzer's pbmplus
image file format and two popular Atari ST image formats:

      pi1toppm.c -- Reads low-resolution Degas and writes ppm
      ppmtopi1.c -- Reads ppm and writes Low-resolution Degas
      spctoppm.c -- Reads compressed Spectrum 512 and writes ppm
      sputoppm.c -- Reads uncompressed Spectrum 512 and writes ppm
      ppmtospu.c -- Reads ppm and writes uncompressed Spectrum 512

Thank you for considering this submission.

Steve Belczyk   CIS: 75126,515    BBS: +1 508 664-0149 (2400 bps)
seb3@gte.com    GEnie: sbelczyk   UUCP: {harvard,vaxine}!bunny!seb3

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README pi1toppm.c ppmtopi1.c ppmtospu.c spctoppm.c
#   sputoppm.c
# Wrapped by seb3@bunny.gte.com on Fri Jul 13 22:25:51 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2223 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
Here are three programs which convert between the Spectrum 512 graphics
format for the Atari ST and the Portable Pixel Map (ppm) format used by
Jef Poskanzer's PBMPLUS package.  Also included are two programs to
convert between PBMPLUS and Degas PI1 format for the Atari.
X
X"sputoppm.c" reads an uncompressed Spectrum file on its standard input
and writes a ppm file to its standard output.  The program does *not*
check for a file name on the command line and will complain if it finds
one.  Be sure to send the input file to standard input.
X
X"spctoppm.c" reads a compressed Spectrum file on standard input and
writes a ppm file to standard output.  Again, placing a file name on the
command line won't work.
X
X"ppmtospu.c" reads a ppm file on standard input and writes an
uncompressed Spectrum file on standard output.  As with the preceding
programs, don't use a file name on the command line.  There are three
command line options:
X
X        -d0  turns off dithering.
X        -d2  uses a 2x2 ordered dither.  This is the default.
X        -d4  uses a 4x4 ordered dither.
X
Using a different dithering option may increase the image quality on
difficult images.
X
X"pi1toppm.c" reads a Degas PI1 (low-resolution) image on standard input
and writes a ppm file on standard output.
X
X"ppmtopi1.c" reads a ppm image on standard input and writes a Degas PI1
file on standard output.  It will complain if the input file has more
than 16 colors.
X
X
These programs were originally developed on an Amiga.  I've since
compiled them on VAX, Sun, Silicon Graphics, and Multiflow machines
with no problems.
X
I wish to convey my appreciation to Scott Yelich (scott@xanth.cs.odu.edu)
for beta-testing these programs.
X
These programs are Copyright (C) 1990, Steve Belczyk.
X
Permission to use, copy, modify and distribute this software for any
purpose and without fee is hereby granted, provided that the above
copyright notice appear in all copies and supporting documentation.
This software is provided "as is" without express or implied warranty.
X
Please send bug reports to one of the following addresses:
X
Steve Belczyk   CIS: 75126,515    BBS: +1 508 664-0149 (2400 bps)
seb3@gte.com    GEnie: sbelczyk   UUCP: {harvard,vaxine}!bunny!seb3
X
END_OF_FILE
if test 2223 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'pi1toppm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pi1toppm.c'\"
else
echo shar: Extracting \"'pi1toppm.c'\" \(2006 characters\)
sed "s/^X//" >'pi1toppm.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X *  pi1toppm.c - Reads a Degas PI1 file on stdin and
X *  writes a portable pixmap (ppm) file on stdout.
X *
X *  Copyright (C) 1990, Steve Belczyk
X */
X
X/* Remove the following definition if you don't want raw ppm files */
X#define RAWBITS
X
char *progname;
X
int pal[16][3];			/* Degas palettes */
short screen[16000];		/* Simulates the Atari's video RAM */
X
main (argc, argv)
int argc;
char *argv[];
X{
X	int x, y;
X
X	progname = argv[0];
X
X	/* Check for bogus arguments */
X	if (argc > 1)
X	{
X		fprintf (stderr, "usage: %s <pi1file >ppmfile\n",
X			 progname);
X		exit (-1);
X	}
X
X	/* Read the PI1 file */
X	ReadPI1();
X
X	/* Write PPM header */
X#ifdef RAWBITS
X	printf ("P6\n320 200\n255\n"); /* Magic, resolution, max pixel */
X#else
X	printf ("P3\n320 200\n255\n");
X#endif
X
X	/* Convert and write */
X	for (y=0; y<200; y++)
X	{
X		for (x=0; x<320; x++)
X		{
X			DoPixel (x, y);
X		}
X	}
X}
X
ReadPI1 ()
X{
X	int i, j;
X
X	/* Check resolution word */
X	i = GetWord();
X	if (i != 0)
X	{
X		fprintf (stderr, "%s: Not a PI1 file\n", progname);
X		exit (-1);
X	}
X
X	/* Read the palette */
X	for (i=0; i<16; i++)
X	{
X		j = GetWord();
X		pal[i][0] = (0x700 & j) >> 3;
X		pal[i][1] = (0x070 & j) << 1;
X		pal[i][2] = (0x007 & j) << 5;
X	}
X
X	/* Read the screen data */
X	for (i=0; i<16000; i++)
X	{
X		screen[i] = GetWord();
X	}
X}
X
int GetWord()
X{
X	int w;
X
X	w  = (0xff & getchar()) << 8;
X	w |=  0xff & getchar();
X
X	return (w);
X}
X
DoPixel (x, y)
int x, y;
X{
X	int c;
X
X	c = GetPixel (x, y);
X
X#ifdef RAWBITS
X	putchar (0xff & pal[c][0]);
X	putchar (0xff & pal[c][1]);
X	putchar (0xff & pal[c][2]);
X#else
X	printf ("%d %d %d\n", (0xff & pal[c][0]),
X			      (0xff & pal[c][1]),
X			      (0xff & pal[c][2]));
X#endif
X}
X
int GetPixel (x, y)
int x, y;
X{
X	int c, index, bit, plane;
X
X	c = 0;
X
X/*	index = (80*y) + 4*(x/16);	*/
X	index = (y << 6) + (y << 4) + ((x >> 4) << 2);
X
X/*	bit = 0x8000 >> (x % 16);	*/
X	bit = 0x8000 >> (x & 0x0f);
X
X	for (plane=0; plane<4; plane++)
X	{
X		if (bit & screen[index+plane])
X		{
X			c |= (1 << plane);
X		}
X	}
X	return (c);
X}
X
END_OF_FILE
if test 2006 -ne `wc -c <'pi1toppm.c'`; then
    echo shar: \"'pi1toppm.c'\" unpacked with wrong size!
fi
# end of 'pi1toppm.c'
fi
if test -f 'ppmtopi1.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ppmtopi1.c'\"
else
echo shar: Extracting \"'ppmtopi1.c'\" \(4040 characters\)
sed "s/^X//" >'ppmtopi1.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X *  ppmtppi1.c - Reads a portable pixmap (ppm) file on stdin and
X *  writes a Degas PI1 file on stdout.
X *
X *  Copyright (C) 1990, Steve Belczyk
X */
X
char *progname;
X
int pal[16][3];		/* Degas palette */
X
X/* This is the ST's video RAM */
short screen[16000];
X
X/* RAWBITS flag */
int rawbits;
X
X/* Picture resolution */
int xres, yres;
X
main (argc, argv)
int argc;
char *argv[];
X{
X	char magic[10];
X	int i, maxpix;
X	
X	progname = argv[0];
X	
X	/* Check for bogus arguments */
X	if (argc != 1)
X	{
X		fprintf (stderr, "usage: %s <ppmfile >pi1file\n",
X			 progname);
X		exit (-1);
X	}
X
X	/* Read ppm header, validate fields */
X	GetPPMHeader (magic, &xres, &yres, &maxpix);
X
X	if ( (magic[0] != 'P') && (magic[0] != 'p') )
X	{
X		fprintf (stderr, "Not a ppm file.\n");
X		exit (-1);
X	}
X	if (magic[1] == '3')
X	{
X		rawbits = 0;
X	}
X	else if (magic[1] == '6')
X	{
X		rawbits = 1;
X	}
X	else
X	{
X		fprintf (stderr, "%s: Not a ppm file.\n", progname);
X		exit (-1);
X	}
X
X	if ( (xres > 320) || (yres > 200) )
X	{
X		fprintf (stderr,
X			"%s: Resolution greater than 320x200. Sorry.\n",
X			progname);
X		exit (-1);
X	}
X
X	/* Clear the bitmap */
X	for (i=0; i<16000; screen[i++]=0);
X
X	/* Read the PPM data */
X	ReadPPM();
X
X	/* Write the PI1 file */
X	WritePI1();
X}
X
WritePI1 ()
X{
X	int i, w;
X
X	PutWord (0);	/* Low resolution */
X
X	/* Write palette */
X	for (i=0; i<16; i++)
X	{
X		w  = (0xe0 & pal[i][0]) << 3;
X		w |= (0xe0 & pal[i][1]) >> 1;
X		w |= (0xe0 & pal[i][2]) >> 5;
X		PutWord (w);
X	}
X
X	/* Write video RAM */
X	WriteScreen();
X}
X
SetPixel (x, y, c)
int x, y, c;
X{
X	int index, bit, plane;
X
X	/* In the next few statements, the bit operations are a little
X	   quicker, but the arithmetic versions are easier to read and
X	   maybe more portable.  Please try swapping them if you have
X	   trouble on your machine. */
X
X/*	index = (80 * y) + 4 * (x / 16);	*/
X	index = (y << 6) + (y << 4) + ((x >> 4) << 2);
X
X/*	bit = 0x8000 >> (x % 16);	*/
X	bit = 0x8000 >> (x & 0x0f);
X
X	for (plane=0; plane<4; plane++)
X	{
X		if (c & (1 << plane))
X		{
X			screen[index+plane] |= bit;
X		}
X	}
X}
X
WriteScreen ()
X{
X	int i;
X
X	for (i=0; i<16000; i++)
X	{
X		PutWord (screen[i]);
X	}
X}
X
GetString (s)
char *s;
X{
X	int i;
X	char c;
X
X	/* Skip leading white space */
X	do
X	{
X		c = GetChar();
X	} while ( (c == ' ') || (c == '\t') || (c == '\n') );
X
X	/* Build string */
X	i = 0;
X	do
X	{
X		s[i++] = c;
X		if (i > 8) break;
X		c = GetChar();
X	} while ( (c != ' ') && (c != '\t') && (c != '\n') );
X
X	s[i] = 0;
X	return;
X}
X
int GetChar()
X{
X	int c;
X
X	c = getchar();
X
X	if (c == EOF)
X	{
X		fprintf (stderr, "%s: Premature EOF.\n", progname);
X		exit (-1);
X	}
X
X	do
X	{
X		if (c == '#')	/* Comment character */
X		{
X			do	/* Skip to end-of-line */
X			{
X				c = getchar();
X				if (c == EOF)
X				{
X					fprintf (stderr,
X					  "%s: Premature EOF.\n",
X					  progname);
X					exit (-1);
X				}
X			} while (c != '\n');
X
X			c = getchar();
X		}
X	} while (c == '#');	/* In case there's another comment */
X
X	return (c);
X}
X
GetPPMHeader (magic, xres, yres, maxpix)
char *magic;
int *xres, *yres, *maxpix;
X{
X	char s[10];
X
X	GetString (magic);
X
X	GetString (s);
X	*xres = atoi(s);
X
X	GetString (s);
X	*yres = atoi(s);
X
X	GetString (s);
X	*maxpix = atoi(s);
X}
X
ReadPPM ()
X{
X	int x, y, n, r, g, b, i;
X	
X	n = 0;	/* counts colors */
X	
X	for (y=0; y<yres; y++)
X	{
X		for (x=0; x<xres; x++)
X		{
X			if (rawbits)
X			{
X				r = 0xff & getchar();
X				g = 0xff & getchar();
X				b = 0xff & getchar();
X			}
X			else
X			{
X				scanf ("%d %d %d", &r, &g, &b);
X			}
X			i = FindColor (&n, r, g, b);
X			SetPixel (x, y, i);
X		}
X	}
X}
X
int FindColor (n, r, g, b)
int *n, r, g, b;
X{
X	int i;
X	
X	r = 0xe0 & r;
X	g = 0xe0 & g;
X	b = 0xe0 & b;
X	
X	for (i=0; i<(*n); i++)
X	{
X		if ( (r == pal[i][0]) &&
X		     (g == pal[i][1]) &&
X		     (b == pal[i][2]) )
X		{
X			return (i);
X		}
X	}
X	
X	if ((*n) > 15)
X	{
X		fprintf (stderr, "%s: More than 16 colors.\n", progname);
X		exit (-1);
X	}
X
X	pal[*n][0] = r;
X	pal[*n][1] = g;
X	pal[*n][2] = b;
X	
X	i = (*n);
X	(*n)++;
X
X	return (i);
X}
X
PutWord (w)
int w;
X{
X	char c0, c1;
X	
X	c0 = 0xff & (w >> 8);
X	c1 = 0xff & w;
X	
X	putchar (c0);
X	putchar (c1);
X}
END_OF_FILE
if test 4040 -ne `wc -c <'ppmtopi1.c'`; then
    echo shar: \"'ppmtopi1.c'\" unpacked with wrong size!
fi
# end of 'ppmtopi1.c'
fi
if test -f 'ppmtospu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ppmtospu.c'\"
else
echo shar: Extracting \"'ppmtospu.c'\" \(8712 characters\)
sed "s/^X//" >'ppmtospu.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X *  ppmtpspu.c - Reads a raw portable pixmap (ppm) file on stdin and
X *  writes an uncompressed Spectrum file on stdout.
X *
X *  Copyright (C) 1990, Steve Belczyk
X */
X
X/* These are the palettes, 3 16-color palettes per each of 200
X   scan lines */
int pal[200][48];
X
X/* This is the ST's video RAM */
short screen[16000];
X
X/* This is the stuff to remember about each pixel */
struct pixel_type
X{
X	int index4;	 /* 4-bit color, used in bitmap */
X	int x;		 /* Pixel's original x-position */
X	int pop;	 /* Popularity of this pixel's color */
X	int color9;	 /* 9-bit color this pixel actually got */
X} pixel[320];
X
int index48[320][16];	/* Indices into the 48 color entries */
X
X/* Histogram for counting color occurences */
int hist[512];		/* Count for each color */
X
X/* RAWBITS flag */
int rawbits;
X
X/* Dithering flag */
int dithflag = 2;
X
main (argc, argv)
int argc;
char *argv[];
X{
X	char magic[10];
X	int xres, yres, maxpix, y, i;
X
X	/* Parse arguments */
X	for (i=1; i<argc; i++)
X	{
X		if (!strcmp (argv[i], "-d0"))
X		{
X			dithflag = 0;
X		}
X		else if (!strcmp (argv[i], "-d2"))
X		{
X			dithflag = 2;
X		}
X		else if (!strcmp (argv[i], "-d4"))
X		{
X			dithflag = 4;
X		}
X		else
X		{
X			fprintf (stderr,
X			  "usage: %s <ppmfile >spufile [-d0|-d2|-d4]\n",
X			  argv[0]);
X			exit (-1);
X		}
X	}
X
X	/* Read ppm header, validate fields */
X	GetPPMHeader (magic, &xres, &yres, &maxpix);
X
X	if ( (magic[0] != 'P') && (magic[0] != 'p') )
X	{
X		fprintf (stderr, "Not a ppm file.\n");
X		exit (-1);
X	}
X	if (magic[1] == '3')
X	{
X		rawbits = 0;
X	}
X	else if (magic[1] == '6')
X	{
X		rawbits = 1;
X	}
X	else
X	{
X		fprintf (stderr, "Not a ppm file.\n");
X		exit (-1);
X	}
X
X	if ( (xres != 320) || (yres != 200) )
X	{
X		fprintf (stderr,
X			"Resolution must be 320x200. Sorry.\n");
X		exit (-1);
X	}
X
X	/* Clear the bitmap */
X	for (i=0; i<16000; screen[i++]=0);
X
X	/* Set up the index48 variables */
X	Setup48();
X
X	/* Process each row */
X	for (y=0; y<200; y++)
X	{
X		DoRow (y);
X	}
X
X	/* Write the SPU file */
X	WriteSPU();
X}
X
DoRow (r)
int r;
X{
X	int i, j;
X	char row[320][3];
X
X	/* First row is special */
X	if (r == 0)
X	{
X		for (i=0; i<320; i++)
X		{
X			/* Skip image data */
X			if (rawbits)
X			{
X				getchar(); getchar(); getchar();
X			}
X			else
X			{
X				scanf ("%d %d %d", &j, &j, &j);
X			}
X		}
X
X		/* Set palettes to zero */
X		for (i=0; i<200; i++)
X		{
X			for (j=0; j<48; pal[i][j++]=0);
X		}
X
X		/* Set first row of screen data to black */
X		for (i=0; i<80; screen[i++]=0);
X
X		return;
X	}
X
X	/* Else read the screen data */
X	for (i=0; i<320; i++)
X	{
X		for (j=0; j<3; j++)
X		{
X			if (rawbits)
X			{
X				row[i][j] = getchar();
X			}
X			else
X			{
X				scanf ("%d", &row[i][j]);
X			}
X		}
X	}
X
X	/* Dither and reduce to 9 bits */
X	Dither (r, row);
X
X	/* Compute the best colors for this row */
X	ComputePalette ();
X
X	/* Convert this row */
X	ConvertRow (r);
X}
X
ComputePalette ()
X{
X	int i, j, c;
X
X	/* Uses popularity algorithm */
X
X	/* Clear the histogram */
X	for (i=0; i<512; i++)
X	{
X		hist[i] = 0;
X	}
X
X	/* Count the occurences of each color */
X	for (i=0; i<320; i++)
X	{
X		hist[pixel[i].color9]++;
X	}
X
X	/* Set the popularity of each pixel's color */
X	for (i=0; i<320; i++)
X	{
X		pixel[i].pop = hist[pixel[i].color9];
X	}
X
X	/* Sort to find the most popular colors */
X	Sort (0, 319);
X}
X
Setup48 ()
X/*
X *  For each pixel position, set up the indices into the 48-color
X *  palette
X */
X{
X	int i, j;
X
X	for (j=0; j<320; j++)
X	{
X		for (i=0; i<16; i++)
X		{
X			index48[j][i] = FindIndex (j, i);
X		}
X	}
X}
X
int FindIndex (x, c)
X/*
X *  Given an x-coordinate and a color index, returns the corresponding
X *  Spectrum palette index.
X */
X{
X	int r, x1;
X	
X	x1 = 10*c;
X	if (1&c)
X	{
X		x1 -= 5;
X	}
X	else
X	{
X		x1++;
X	}
X	r = c;
X	if ( (x >= x1) && (x < (x1+160)) ) r += 16;
X	if (x >= (x1+160)) r += 32;
X	
X	return (r);
X}
X
Dither (y, row)
int y;
char row[320][3];
X{
X	static int dith4[4][4] = { 0, 8, 2,10,
X				  12, 4,14, 6,
X				   3,11, 1, 9,
X				  15, 7,13, 5 };
X
X	static int dith2[2][2] = { 0, 2,
X				   3, 1 };
X
X	int c[3], i, x, t;
X
X	for (x=0; x<320; x++)
X	{
X		for (i=0; i<3; i++)
X		{
X			c[i] = 7 & ((0xe0 & row[x][i]) >> 5);
X
X			switch (dithflag)
X			{
X			case 0:	break;
X
X			case 2:	t = (0x18 & row[x][i]) >> 3;
X				if (t > dith2[x%2][y%2]) c[i]++;
X				break;
X
X			case 4:	t = (0x1e & row[x][i]) >> 1;
X				if (t > dith4[x%4][y%4]) c[i]++;
X				break;
X			}
X			if (c[i] > 7) c[i] = 7;
X		}
X		pixel[x].color9 = (c[0] << 6) | (c[1] << 3) | c[2];
X		pixel[x].x = x;
X	}
X}
X
int dist9 (x, y)
int x, y;
X/*
X *  Returns the distance between two 9-bit colors.
X */
X{
X	int d, i, t, x0[3], y0[3];
X
X	x0[0] = (x & 0x007);
X	x0[1] = (x & 0x038) >> 3;
X	x0[2] = (x & 0x1c0) >> 6;
X
X	y0[0] = (y & 0x007);
X	y0[1] = (y & 0x038) >> 3;
X	y0[2] = (y & 0x1c0) >> 6;
X
X	d = 0;
X
X	for (i=0; i<3; i++)
X	{
X		t = x0[i] - y0[i];
X		d += t * t;
X	}
X
X	return (d);
X}
X
ConvertRow (y)
int y;
X{
X	int i;
X
X	/* Mark palette entries as all free */
X	for (i=0; i<48; i++)
X	{
X		pal[y][i] = (-1);
X	}
X
X	/* Mark reserved palette entries */
X	pal[y][0]  = pal[y][15] = pal[y][16] = 0;
X	pal[y][31] = pal[y][32] = pal[y][47] = 0;
X
X	/* Convert each pixel */
X
X	/* Process the pixels in order of the popularity of the
X	   desired color */
X	for (i=319; i>=0; i--)
X	{
X		ConvertPixel (i, y);
X		SetPixel (pixel[i].x, y, pixel[i].index4);
X	}
X}
X
ConvertPixel (p, y)
int p, y;
X{
X	int i, ifree, d, b, t, x, c;
X
X	x = pixel[p].x;
X	c = pixel[p].color9;
X
X	ifree = (-1);		/* Set if free slot found */
X
X	/*
X	 *  Handle each possible case, from easiest to hardest,
X	 *  in the hopes the easy ones are more frequent.
X	 */
X
X	/* If it wants black, it gets it */
X	if (c == 0)
X	{
X		pixel[p].index4 = 0;
X		return;
X	}
X
X	/* If another pixel is using this color, it gets it */
X	for (i=1; i<15; i++)
X	{
X		/* Check for free slots while we're here */
X		if ( (ifree < 0) &&
X		     (pal[y][index48[x][i]] == (-1) ) )
X		{
X			ifree = i;
X		}
X		else if (c == pal[y][index48[x][i]])
X		{
X			pixel[p].index4 = i;
X			return;
X		}
X	}
X
X	/* If there are no free slots, we must use the closest
X	   entry in use so far */
X	if (ifree < 0)
X	{
X		d = 1000;
X		for (i=1; i<15; i++)
X		{
X			t = dist9 (c, pal[y][index48[x][i]]);
X			if (t < d)
X			{
X				d = t;
X				b = i;
X			}
X		}
X
X		/* See if it would be better off with black */
X		if (d > dist9(c,0)) b = 0;
X
X		pixel[p].index4 = b;
X		return;
X	}
X
X	/* Else use up a slot and give it what it wants */
X	pal[y][index48[x][ifree]] = c;
X	pixel[p].index4 = ifree;
X
X	return;
X}
X
WriteSPU ()
X{
X	int i, p, q, y;
X
X	/* Write the bitmap */
X	WriteScreen();
X
X	/* Write the palettes */
X	for (y=1; y<200; y++)
X	{
X		for (i=0; i<48; i++)
X		{
X			p = pal[y][i];
X			q = ( (p & 0x1c0) << 2) +
X			    ( (p & 0x038) << 1) +
X			      (p & 0x007);
X			putchar (0xff & (q >> 8));
X			putchar (q & 0xff);
X		}
X	}
X}
X
Sort (l, r)
int l, r;
X/*
X *  Good ol' Quicksort
X */
X{
X	struct pixel_type x, w;
X	int i, j;
X	
X	i = l;
X	j = r;
X	x = pixel[(l+r)/2];
X	
X	do
X	{
X		while (pixel[i].pop < x.pop) i++;
X		while (x.pop < pixel[j].pop) j--;
X		
X		if (i <= j)
X		{
X			w = pixel[i];
X			pixel[i] = pixel[j];
X			pixel[j] = w;
X			i++;
X			j--;
X		}
X	} while (i <= j);
X	
X	if (l < j) Sort (l, j);
X	if (i < r) Sort (i, r);
X}
X
SetPixel (x, y, c)
int x, y, c;
X{
X	int index, bit, plane;
X
X	/* In the next few statements, the bit operations are a little
X	   quicker, but the arithmetic versions are easier to read and
X	   maybe more portable.  Please try swapping them if you have
X	   trouble on your machine. */
X
X/*	index = (80 * y) + 4 * (x / 16);	*/
X	index = (y << 6) + (y << 4) + ((x >> 4) << 2);
X
X/*	bit = 0x8000 >> (x % 16);	*/
X	bit = 0x8000 >> (x & 0x0f);
X
X	for (plane=0; plane<4; plane++)
X	{
X		if (c & (1 << plane))
X		{
X			screen[index+plane] |= bit;
X		}
X	}
X}
X
WriteScreen ()
X{
X	int i;
X	char c0, c1;
X
X	for (i=0; i<16000; i++)
X	{
X		c0 = 0xff & (screen[i] >> 8);
X		c1 = 0xff & screen[i];
X		putchar (c0);
X		putchar (c1);
X	}
X}
X
GetString (s)
char *s;
X{
X	int i;
X	char c;
X
X	/* Skip leading white space */
X	do
X	{
X		c = GetChar();
X	} while ( (c == ' ') || (c == '\t') || (c == '\n') );
X
X	/* Build string */
X	i = 0;
X	do
X	{
X		s[i++] = c;
X		if (i > 8) break;
X		c = GetChar();
X	} while ( (c != ' ') && (c != '\t') && (c != '\n') );
X
X	s[i] = 0;
X	return;
X}
X
int GetChar()
X{
X	int c;
X
X	c = getchar();
X
X	if (c == EOF)
X	{
X		fprintf (stderr, "Premature EOF.\n");
X		exit (-1);
X	}
X
X	do
X	{
X		if (c == '#')	/* Comment character */
X		{
X			do	/* Skip to end-of-line */
X			{
X				c = getchar();
X				if (c == EOF)
X				{
X					fprintf (stderr,
X					  "Premature EOF.\n");
X					exit (-1);
X				}
X			} while (c != '\n');
X
X			c = getchar();
X		}
X	} while (c == '#');	/* In case there's another comment */
X
X	return (c);
X}
X
GetPPMHeader (magic, xres, yres, maxpix)
char *magic;
int *xres, *yres, *maxpix;
X{
X	char s[10];
X
X	GetString (magic);
X
X	GetString (s);
X	*xres = atoi(s);
X
X	GetString (s);
X	*yres = atoi(s);
X
X	GetString (s);
X	*maxpix = atoi(s);
X}
END_OF_FILE
if test 8712 -ne `wc -c <'ppmtospu.c'`; then
    echo shar: \"'ppmtospu.c'\" unpacked with wrong size!
fi
# end of 'ppmtospu.c'
fi
if test -f 'spctoppm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'spctoppm.c'\"
else
echo shar: Extracting \"'spctoppm.c'\" \(3695 characters\)
sed "s/^X//" >'spctoppm.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X *  spctoppm.c - Reads a compressed Spectrum file on stdin and writes
X *  a portable pixmap (ppm) file on stdout.
X *
X *  Copyright (C) 1990, Steve Belczyk
X */
X
X/* Remove the following definition if you don't want raw ppm files */
X#define RAWBITS
X
char	screen[32000];
short	sscreen[16000];
int	pal[200][48];
long	colormap_length, bitmap_length;
X
main (argc, argv)
int argc;
char *argv[];
X{
X	char c1, c2;
X
X	/* Check for bogus arguments */
X	if (argc > 1)
X	{
X		fprintf (stderr, "usage: %s <spcfile >ppmfile\n",
X			 argv[0]);
X		exit (-1);
X	}
X
X	/* Check SPC file header */
X	c1 = getchar();
X	c2 = getchar();
X
X	if ( ('S' != c1) || ('P' != c2) )
X	{
X		fprintf (stderr, "Not a Spectrum picture.\n");
X		exit (-1);
X	}
X
X	/* Skip reserved bytes */
X	getchar(); getchar();
X
X	/* Get length of bitmap data */
X	bitmap_length = GetLong();
X
X	/* and colormap */
X	colormap_length = GetLong();
X
X	/* Process bitmap */
X	DoBitmap();
X
X	/* Process colormap */
X	DoColormap();
X
X	/* Write the PPM file */
X	WritePPM();
X}
X
long GetLong()
X{
X	long l;
X
X	l  = (0xff & getchar()) << 24;
X	l |= (0xff & getchar()) << 16;
X	l |= (0xff & getchar()) << 8;
X	l |=  0xff & getchar();
X
X	return (l);
X}
X
int GetWord()
X{
X	int w;
X
X	w  = (0xff & getchar()) << 8;
X	w |=  0xff & getchar();
X
X	return (w);
X}
X
DoBitmap()
X{
X	int i;
X	long count, data;
X	char h, c;
X
X	/* Zero out first scan line */
X	for (i=0; i<160; screen[i++]=0);
X
X	/* 'count' counts number of input bytes */
X	count = 0;
X
X	/* 'data' counts just data bytes */
X	data = 0;
X
X	while (count < bitmap_length)
X	{
X		/* Get next record header */
X		h = getchar(); count++;
X
X		if ( (h >= 0) && (count < bitmap_length) )
X		{
X			for (i=0; i<=h; i++)
X			{
X				c = getchar(); count++;
X				DoChar (data, c);
X				data++;
X			}
X		}
X		else if ( (h < 0) && (count < bitmap_length) )
X		{
X			c = getchar(); count++;
X
X			for (i=0; i<(2-h); i++)
X			{
X				DoChar (data, c);
X				data++;
X			}
X		}
X	}
X
X	/* Convert the char version of the screen to short */
X	for (i=0; i<16000; i++)
X	{
X		sscreen[i] = (screen[i<<1] << 8) +
X		             (0xff & screen[(i<<1)+1]);
X	}
X}
X
DoChar (n, c)
int n;
char c;
X{
X	int i;
X
X	/* Compute screen index */
X	i = 160 + 2*(n/7960) + 8*((n%7960)/2) + (n&1);
X	screen[i] = c;
X}
X
DoColormap()
X{
X	int i, j, mask, bit, count;
X
X	count = 0;
X
X	/* Clear first three palettes */
X	for (i=0; i<48; pal[0][i++]=0);
X
X	for (i=1; i<200; i++)
X	{
X		for (j=0; j<3; j++)
X		{
X			mask = GetWord(); count+=2;
X			for (bit=0; bit<15; bit++)
X			{
X				if (mask & (1 << bit))
X				{
X					pal[i][(j*16)+bit] = GetWord();
X					count += 2;
X				}
X			}
X		}
X	}
X}
X
WritePPM()
X{
X	int x, y;
X
X	/* Write the PPM header */
X#ifdef RAWBITS
X	printf ("P6\n320 200\n255\n"); /* Magic, resolution, maxpix */
X#else
X	printf ("P3\n320 200\n255\n");
X#endif
X
X	/* Loop through pixels */
X	for (y=0; y<200; y++)
X	{
X		for (x=0; x<320; x++)
X		{
X			DoPixel (x, y);
X		}
X	}
X}
X
DoPixel (x, y)
int x, y;
X{
X	int c, x1;
X
X	c = GetPixel (x, y);
X
X	/* Compute palette index */
X	x1 = 10 * c;
X
X	if (1 & c)
X	{
X		x1 -= 5;
X	}
X	else
X	{
X		x1++;
X	}
X
X	if ( (x >= x1) && (x < (x1+160)) ) c += 16;
X	if (x >= (x1+160)) c += 32;
X
X	/* Write the proper color */
X	DoColor (pal[y][c]);
X}
X
DoColor (p)
int p;
X{
X	int r, g, b;
X
X	r = (0x700 & p) >> 3;
X	g = (0x070 & p) << 1;
X	b = (0x007 & p) << 5;
X
X#ifdef RAWBITS
X	putchar (0xff & r);
X	putchar (0xff & g);
X	putchar (0xff & b);
X#else
X	printf ("%d %d %d\n", r, g, b);
X#endif
X}
X
int GetPixel (x, y)
int x, y;
X{
X	int c, index, bit, plane;
X
X	c = 0;
X
X/*	index = (80*y) + 4*(x/16);	*/
X	index = (y << 6) + (y << 4) + ((x >> 4) << 2);
X
X/*	bit = 0x8000 >> (x % 16);	*/
X	bit = 0x8000 >> (x & 0x0f);
X
X	for (plane=0; plane<4; plane++)
X	{
X		if (bit & sscreen[index+plane])
X		{
X			c |= (1 << plane);
X		}
X	}
X	return (c);
X}
X
END_OF_FILE
if test 3695 -ne `wc -c <'spctoppm.c'`; then
    echo shar: \"'spctoppm.c'\" unpacked with wrong size!
fi
# end of 'spctoppm.c'
fi
if test -f 'sputoppm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sputoppm.c'\"
else
echo shar: Extracting \"'sputoppm.c'\" \(2123 characters\)
sed "s/^X//" >'sputoppm.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X *  sputoppm.c - Reads an uncompressed Spectrum file on stdin and
X *  writes a portable pixmap (ppm) file on stdout.
X *
X *  Copyright (C) 1990, Steve Belczyk
X */
X
X/* Remove the following definition if you don't want raw ppm files */
X#define RAWBITS
X
int pal[200][48];		/* Spectrum palettes, three per row */
short screen[16000];		/* Simulates the Atari's video RAM */
X
main (argc, argv)
int argc;
char *argv[];
X{
X	int x, y;
X
X	/* Check for bogus arguments */
X	if (argc > 1)
X	{
X		fprintf (stderr, "usage: %s <spufile >ppmfile\n",
X			 argv[0]);
X		exit (-1);
X	}
X
X	/* Read the SPU file */
X	ReadSPU();
X
X	/* Write PPM header */
X#ifdef RAWBITS
X	printf ("P6\n320 200\n255\n"); /* Magic, resolution, max pixel */
X#else
X	printf ("P3\n320 200\n255\n");
X#endif
X
X	/* Convert and write */
X	for (y=0; y<200; y++)
X	{
X		for (x=0; x<320; x++)
X		{
X			DoPixel (x, y);
X		}
X	}
X}
X
ReadSPU ()
X{
X	int i, j;
X
X	/* Read the screen data */
X	for (i=0; i<16000; i++)
X	{
X		screen[i] = GetWord();
X	}
X
X	/* Read the palettes */
X	for (i=1; i<200; i++)
X	{
X		for (j=0; j<48; j++)
X		{
X			pal[i][j] = GetWord();
X		}
X	}
X
X	/* Clear the first palette line */
X	for (i=0; i<48; pal[0][i++]=0);
X}
X
int GetWord()
X{
X	int w;
X
X	w  = (0xff & getchar()) << 8;
X	w |=  0xff & getchar();
X
X	return (w);
X}
X
DoPixel (x, y)
int x, y;
X{
X	int c, x1;
X
X	c = GetPixel (x, y);
X
X	/* Compute palette index */
X	x1 = 10 * c;
X
X	if (1 & c)
X	{
X		x1 -= 5;
X	}
X	else
X	{
X		x1++;
X	}
X
X	if ( (x >= x1) && (x < (x1+160)) ) c += 16;
X	if (x >= (x1+160)) c += 32;
X
X	/* Write the proper color */
X	DoColor (pal[y][c]);
X}
X
DoColor (p)
int p;
X{
X	int r, g, b;
X
X	r = (0x700 & p) >> 3;
X	g = (0x070 & p) << 1;
X	b = (0x007 & p) << 5;
X
X#ifdef RAWBITS
X	putchar (0xff & r);
X	putchar (0xff & g);
X	putchar (0xff & b);
X#else
X	printf ("%d %d %d\n", r, g, b);
X#endif
X}
X
int GetPixel (x, y)
int x, y;
X{
X	int c, index, bit, plane;
X
X	c = 0;
X
X/*	index = (80*y) + 4*(x/16);	*/
X	index = (y << 6) + (y << 4) + ((x >> 4) << 2);
X
X/*	bit = 0x8000 >> (x % 16);	*/
X	bit = 0x8000 >> (x & 0x0f);
X
X	for (plane=0; plane<4; plane++)
X	{
X		if (bit & screen[index+plane])
X		{
X			c |= (1 << plane);
X		}
X	}
X	return (c);
X}
X
END_OF_FILE
if test 2123 -ne `wc -c <'sputoppm.c'`; then
    echo shar: \"'sputoppm.c'\" unpacked with wrong size!
fi
# end of 'sputoppm.c'
fi
echo shar: End of shell archive.
exit 0