home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
-
- /*
- * ppmtpspu.c - Reads a raw portable pixmap (ppm) file on stdin and
- * writes an uncompressed Spectrum file on stdout.
- *
- * Copyright (C) 1990, Steve Belczyk
- */
-
- /* These are the palettes, 3 16-color palettes per each of 200
- scan lines */
- int pal[200][48];
-
- /* This is the ST's video RAM */
- short screen[16000];
-
- /* This is the stuff to remember about each pixel */
- struct pixel_type
- {
- int index4; /* 4-bit color, used in bitmap */
- int x; /* Pixel's original x-position */
- int pop; /* Popularity of this pixel's color */
- int color9; /* 9-bit color this pixel actually got */
- } pixel[320];
-
- int index48[320][16]; /* Indices into the 48 color entries */
-
- /* Histogram for counting color occurences */
- int hist[512]; /* Count for each color */
-
- /* RAWBITS flag */
- int rawbits;
-
- /* Dithering flag */
- int dithflag = 2;
-
- main (argc, argv)
- int argc;
- char *argv[];
- {
- char magic[10];
- int xres, yres, maxpix, y, i;
-
- /* Parse arguments */
- for (i=1; i<argc; i++)
- {
- if (!strcmp (argv[i], "-d0"))
- {
- dithflag = 0;
- }
- else if (!strcmp (argv[i], "-d2"))
- {
- dithflag = 2;
- }
- else if (!strcmp (argv[i], "-d4"))
- {
- dithflag = 4;
- }
- else
- {
- fprintf (stderr,
- "usage: %s <ppmfile >spufile [-d0|-d2|-d4]\n",
- argv[0]);
- exit (-1);
- }
- }
-
- /* Read ppm header, validate fields */
- GetPPMHeader (magic, &xres, &yres, &maxpix);
-
- if ( (magic[0] != 'P') && (magic[0] != 'p') )
- {
- fprintf (stderr, "Not a ppm file.\n");
- exit (-1);
- }
- if (magic[1] == '3')
- {
- rawbits = 0;
- }
- else if (magic[1] == '6')
- {
- rawbits = 1;
- }
- else
- {
- fprintf (stderr, "Not a ppm file.\n");
- exit (-1);
- }
-
- if ( (xres != 320) || (yres != 200) )
- {
- fprintf (stderr,
- "Resolution must be 320x200. Sorry.\n");
- exit (-1);
- }
-
- /* Clear the bitmap */
- for (i=0; i<16000; screen[i++]=0);
-
- /* Set up the index48 variables */
- Setup48();
-
- /* Process each row */
- for (y=0; y<200; y++)
- {
- DoRow (y);
- }
-
- /* Write the SPU file */
- WriteSPU();
- }
-
- DoRow (r)
- int r;
- {
- int i, j;
- char row[320][3];
-
- /* First row is special */
- if (r == 0)
- {
- for (i=0; i<320; i++)
- {
- /* Skip image data */
- if (rawbits)
- {
- getchar(); getchar(); getchar();
- }
- else
- {
- scanf ("%d %d %d", &j, &j, &j);
- }
- }
-
- /* Set palettes to zero */
- for (i=0; i<200; i++)
- {
- for (j=0; j<48; pal[i][j++]=0);
- }
-
- /* Set first row of screen data to black */
- for (i=0; i<80; screen[i++]=0);
-
- return;
- }
-
- /* Else read the screen data */
- for (i=0; i<320; i++)
- {
- for (j=0; j<3; j++)
- {
- if (rawbits)
- {
- row[i][j] = getchar();
- }
- else
- {
- scanf ("%d", &row[i][j]);
- }
- }
- }
-
- /* Dither and reduce to 9 bits */
- Dither (r, row);
-
- /* Compute the best colors for this row */
- ComputePalette ();
-
- /* Convert this row */
- ConvertRow (r);
- }
-
- ComputePalette ()
- {
- int i, j, c;
-
- /* Uses popularity algorithm */
-
- /* Clear the histogram */
- for (i=0; i<512; i++)
- {
- hist[i] = 0;
- }
-
- /* Count the occurences of each color */
- for (i=0; i<320; i++)
- {
- hist[pixel[i].color9]++;
- }
-
- /* Set the popularity of each pixel's color */
- for (i=0; i<320; i++)
- {
- pixel[i].pop = hist[pixel[i].color9];
- }
-
- /* Sort to find the most popular colors */
- Sort (0, 319);
- }
-
- Setup48 ()
- /*
- * For each pixel position, set up the indices into the 48-color
- * palette
- */
- {
- int i, j;
-
- for (j=0; j<320; j++)
- {
- for (i=0; i<16; i++)
- {
- index48[j][i] = FindIndex (j, i);
- }
- }
- }
-
- int FindIndex (x, c)
- /*
- * Given an x-coordinate and a color index, returns the corresponding
- * Spectrum palette index.
- */
- {
- int r, x1;
-
- x1 = 10*c;
- if (1&c)
- {
- x1 -= 5;
- }
- else
- {
- x1++;
- }
- r = c;
- if ( (x >= x1) && (x < (x1+160)) ) r += 16;
- if (x >= (x1+160)) r += 32;
-
- return (r);
- }
-
- Dither (y, row)
- int y;
- char row[320][3];
- {
- static int dith4[4][4] = { 0, 8, 2,10,
- 12, 4,14, 6,
- 3,11, 1, 9,
- 15, 7,13, 5 };
-
- static int dith2[2][2] = { 0, 2,
- 3, 1 };
-
- int c[3], i, x, t;
-
- for (x=0; x<320; x++)
- {
- for (i=0; i<3; i++)
- {
- c[i] = 7 & ((0xe0 & row[x][i]) >> 5);
-
- switch (dithflag)
- {
- case 0: break;
-
- case 2: t = (0x18 & row[x][i]) >> 3;
- if (t > dith2[x%2][y%2]) c[i]++;
- break;
-
- case 4: t = (0x1e & row[x][i]) >> 1;
- if (t > dith4[x%4][y%4]) c[i]++;
- break;
- }
- if (c[i] > 7) c[i] = 7;
- }
- pixel[x].color9 = (c[0] << 6) | (c[1] << 3) | c[2];
- pixel[x].x = x;
- }
- }
-
- int dist9 (x, y)
- int x, y;
- /*
- * Returns the distance between two 9-bit colors.
- */
- {
- int d, i, t, x0[3], y0[3];
-
- x0[0] = (x & 0x007);
- x0[1] = (x & 0x038) >> 3;
- x0[2] = (x & 0x1c0) >> 6;
-
- y0[0] = (y & 0x007);
- y0[1] = (y & 0x038) >> 3;
- y0[2] = (y & 0x1c0) >> 6;
-
- d = 0;
-
- for (i=0; i<3; i++)
- {
- t = x0[i] - y0[i];
- d += t * t;
- }
-
- return (d);
- }
-
- ConvertRow (y)
- int y;
- {
- int i;
-
- /* Mark palette entries as all free */
- for (i=0; i<48; i++)
- {
- pal[y][i] = (-1);
- }
-
- /* Mark reserved palette entries */
- pal[y][0] = pal[y][15] = pal[y][16] = 0;
- pal[y][31] = pal[y][32] = pal[y][47] = 0;
-
- /* Convert each pixel */
-
- /* Process the pixels in order of the popularity of the
- desired color */
- for (i=319; i>=0; i--)
- {
- ConvertPixel (i, y);
- SetPixel (pixel[i].x, y, pixel[i].index4);
- }
- }
-
- ConvertPixel (p, y)
- int p, y;
- {
- int i, ifree, d, b, t, x, c;
-
- x = pixel[p].x;
- c = pixel[p].color9;
-
- ifree = (-1); /* Set if free slot found */
-
- /*
- * Handle each possible case, from easiest to hardest,
- * in the hopes the easy ones are more frequent.
- */
-
- /* If it wants black, it gets it */
- if (c == 0)
- {
- pixel[p].index4 = 0;
- return;
- }
-
- /* If another pixel is using this color, it gets it */
- for (i=1; i<15; i++)
- {
- /* Check for free slots while we're here */
- if ( (ifree < 0) &&
- (pal[y][index48[x][i]] == (-1) ) )
- {
- ifree = i;
- }
- else if (c == pal[y][index48[x][i]])
- {
- pixel[p].index4 = i;
- return;
- }
- }
-
- /* If there are no free slots, we must use the closest
- entry in use so far */
- if (ifree < 0)
- {
- d = 1000;
- for (i=1; i<15; i++)
- {
- t = dist9 (c, pal[y][index48[x][i]]);
- if (t < d)
- {
- d = t;
- b = i;
- }
- }
-
- /* See if it would be better off with black */
- if (d > dist9(c,0)) b = 0;
-
- pixel[p].index4 = b;
- return;
- }
-
- /* Else use up a slot and give it what it wants */
- pal[y][index48[x][ifree]] = c;
- pixel[p].index4 = ifree;
-
- return;
- }
-
- WriteSPU ()
- {
- int i, p, q, y;
-
- /* Write the bitmap */
- WriteScreen();
-
- /* Write the palettes */
- for (y=1; y<200; y++)
- {
- for (i=0; i<48; i++)
- {
- p = pal[y][i];
- q = ( (p & 0x1c0) << 2) +
- ( (p & 0x038) << 1) +
- (p & 0x007);
- putchar (0xff & (q >> 8));
- putchar (q & 0xff);
- }
- }
- }
-
- Sort (l, r)
- int l, r;
- /*
- * Good ol' Quicksort
- */
- {
- struct pixel_type x, w;
- int i, j;
-
- i = l;
- j = r;
- x = pixel[(l+r)/2];
-
- do
- {
- while (pixel[i].pop < x.pop) i++;
- while (x.pop < pixel[j].pop) j--;
-
- if (i <= j)
- {
- w = pixel[i];
- pixel[i] = pixel[j];
- pixel[j] = w;
- i++;
- j--;
- }
- } while (i <= j);
-
- if (l < j) Sort (l, j);
- if (i < r) Sort (i, r);
- }
-
- SetPixel (x, y, c)
- int x, y, c;
- {
- int index, bit, plane;
-
- /* In the next few statements, the bit operations are a little
- quicker, but the arithmetic versions are easier to read and
- maybe more portable. Please try swapping them if you have
- trouble on your machine. */
-
- /* index = (80 * y) + 4 * (x / 16); */
- index = (y << 6) + (y << 4) + ((x >> 4) << 2);
-
- /* bit = 0x8000 >> (x % 16); */
- bit = 0x8000 >> (x & 0x0f);
-
- for (plane=0; plane<4; plane++)
- {
- if (c & (1 << plane))
- {
- screen[index+plane] |= bit;
- }
- }
- }
-
- WriteScreen ()
- {
- int i;
- char c0, c1;
-
- for (i=0; i<16000; i++)
- {
- c0 = 0xff & (screen[i] >> 8);
- c1 = 0xff & screen[i];
- putchar (c0);
- putchar (c1);
- }
- }
-
- GetString (s)
- char *s;
- {
- int i;
- char c;
-
- /* Skip leading white space */
- do
- {
- c = GetChar();
- } while ( (c == ' ') || (c == '\t') || (c == '\n') );
-
- /* Build string */
- i = 0;
- do
- {
- s[i++] = c;
- if (i > 8) break;
- c = GetChar();
- } while ( (c != ' ') && (c != '\t') && (c != '\n') );
-
- s[i] = 0;
- return;
- }
-
- int GetChar()
- {
- int c;
-
- c = getchar();
-
- if (c == EOF)
- {
- fprintf (stderr, "Premature EOF.\n");
- exit (-1);
- }
-
- do
- {
- if (c == '#') /* Comment character */
- {
- do /* Skip to end-of-line */
- {
- c = getchar();
- if (c == EOF)
- {
- fprintf (stderr,
- "Premature EOF.\n");
- exit (-1);
- }
- } while (c != '\n');
-
- c = getchar();
- }
- } while (c == '#'); /* In case there's another comment */
-
- return (c);
- }
-
- GetPPMHeader (magic, xres, yres, maxpix)
- char *magic;
- int *xres, *yres, *maxpix;
- {
- char s[10];
-
- GetString (magic);
-
- GetString (s);
- *xres = atoi(s);
-
- GetString (s);
- *yres = atoi(s);
-
- GetString (s);
- *maxpix = atoi(s);
- }
-