home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 1
/
FFMCD01.bin
/
bbs
/
graphics
/
ppmqvga.lha
/
PPMQVGA
/
src
/
extra
/
ppmqvga.c.orig
< prev
Wrap
Text File
|
1993-08-31
|
14KB
|
508 lines
/*
* ppmqvga.c - quantize the colors in a pixmap down to a VGA
* (256 colors, 6 bits per pixel)
*
* original by Lyle Rains (lrains@netcom.com) as ppmq256 and ppmq256fs
* combined, commented, and enhanced by Bill Davidsen (davidsen@crd.ge.com)
*/
#define DUMPCOLORS 0
#define DUMPERRORS 0
#include <stdio.h>
#include <math.h>
#include <malloc.h>
#include "ppm.h"
#ifdef SYSV
#include <string.h>
#define srandom srand
#define random rand
#else /*SYSV*/
#include <strings.h>
#define strchr index
#define strrchr rindex
#endif /*SYSV*/
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#define RED_BITS 5
#define GREEN_BITS 6
#define BLUE_BITS 5
#define MAX_RED (1 << RED_BITS)
#define MAX_GREEN (1 << GREEN_BITS)
#define MAX_BLUE (1 << BLUE_BITS)
#define MAXWEIGHT 128
#define STDWEIGHT_DIV (2 << 8)
#define STDWEIGHT_MUL (2 << 10)
#define COLORS 256
#define GAIN 4
#define BARGRAPH "__________\b\b\b\b\b\b\b\b\b\b"
#define BARGRAPHLEN 10
int color_cube[MAX_RED][MAX_GREEN][MAX_BLUE];
unsigned char clut[COLORS][4];
int erropt[COLORS][4];
enum { red, green, blue, count };
int clutx;
int weight_convert[MAXWEIGHT];
int total_weight, cum_weight[MAX_GREEN];
int rep_weight, rep_threshold;
int r, g, b, dr, dg, db;
int dither = 0, verbose = 1;
pixval maxval;
/*
** 3-D error diffusion dither routine for points in the color cube; used to
** select the representative colors.
*/
diffuse()
{
int _7_32nds, _3_32nds, _1_16th;
if (clutx < COLORS) {
if (color_cube[r][g][b] > rep_threshold) {
clut[clutx][red] = ((2 * r + 1) * (maxval + 1)) / (2 * MAX_RED);
clut[clutx][green] = ((2 * g + 1) * (maxval + 1)) / (2 * MAX_GREEN);
clut[clutx][blue] = ((2 * b + 1) * (maxval + 1)) / (2 * MAX_BLUE);
#if DUMPCOLORS
if (verbose > 2) {
/* Dump new color */
if ((clutx & 3) == 0) {
fprintf(stderr, "\n %3d (%2d): ", clutx, rep_threshold);
}
fprintf(stderr,
" (%03d,%03d,%03d)", clut[clutx][red],
clut[clutx][green], clut[clutx][blue]
);
}
#endif
++clutx;
color_cube[r][g][b] -= rep_weight;
}
_7_32nds = (7 * color_cube[r][g][b]) / 32;
_3_32nds = (3 * color_cube[r][g][b]) / 32;
_1_16th = color_cube[r][g][b] - 3 * (_7_32nds + _3_32nds);
color_cube[ r ][ g ][ b ] = 0;
/* spread error evenly in color space. */
color_cube[ r ][ g ][b+db] += _7_32nds;
color_cube[ r ][g+dg][ b ] += _7_32nds;
color_cube[r+dr][ g ][ b ] += _7_32nds;
color_cube[ r ][g+dg][b+db] += _3_32nds;
color_cube[r+dr][ g ][b+db] += _3_32nds;
color_cube[r+dr][g+dg][ b ] += _3_32nds;
color_cube[r+dr][g+dg][b+db] += _1_16th;
/* Conserve the error at edges if possible (which it is, except the last pixel) */
if (color_cube[r][g][b] != 0) {
if (dg != 0) color_cube[r][g+dg][b] += color_cube[r][g][b];
else if (dr != 0) color_cube[r+dr][g][b] += color_cube[r][g][b];
else if (db != 0) color_cube[r][g][b+db] += color_cube[r][g][b];
else fprintf(stderr, "\nlost error term\n");
}
}
color_cube[r][g][b] = -1;
}
/*
** Find representative color nearest to requested color. Check color cube
** for a cached color index. If not cached, compute nearest and cache result.
*/
int nearest_color(pP)
register pixel *pP;
{
register unsigned char *test;
register unsigned i;
unsigned long min_dist_sqd, dist_sqd;
int nearest;
int *cache;
int r, g, b;
r = ((int)(PPM_GETR(*pP)));
g = ((int)(PPM_GETG(*pP)));
b = ((int)(PPM_GETB(*pP)));
if ((i = maxval + 1) == 256) {
cache = &(color_cube[r>>(8-RED_BITS)][g>>(8-GREEN_BITS)][b>>(8-BLUE_BITS)]);
}
else {
cache = &(color_cube[(r<<RED_BITS)/i][(g<<GREEN_BITS)/i][(b<<BLUE_BITS)/i]);
}
if (*cache >= 0) return *cache;
min_dist_sqd = ~0;
for (i = 0; i < COLORS; ++i) {
test = clut[i];
dist_sqd = 3 * (r - test[red]) * (r - test[red])
+ 4 * (g - test[green]) * (g - test[green])
+ 2 * (b - test[blue]) * (b - test[blue]);
if (dist_sqd < min_dist_sqd) {
nearest = i;
min_dist_sqd = dist_sqd;
}
}
return (*cache = nearest);
}
/* Errors are carried at FS_SCALE times actual size for accuracy */
#define _7x16ths(x) ((7 * (x)) / 16)
#define _5x16ths(x) ((5 * (x)) / 16)
#define _3x16ths(x) ((3 * (x)) / 16)
#define _1x16th(x) ((x) / 16)
#define NEXT(line) (!(line))
#define FS_SCALE 1024
typedef int fs_err_array[2][3];
void fs_diffuse (fs_err, line, color, err)
fs_err_array *fs_err;
int line, color;
int err;
{
fs_err[1] [line] [color] += _7x16ths(err);
fs_err[-1][NEXT(line)] [color] += _3x16ths(err);
fs_err[0] [NEXT(line)] [color] += _5x16ths(err);
fs_err[1] [NEXT(line)] [color] = _1x16th(err); /* straight assignment */
}
main(argc, argv)
int argc;
char *argv[];
{
FILE *ifd;
pixel **pixels;
register pixel *pP;
int rows, cols, row;
register int col;
int limitcol;
int i, j, k;
char *ccP;
int *errP;
unsigned char *clutP;
int nearest;
fs_err_array *fs_err_lines, *fs_err;
int fs_line = 0;
char *usage = "[ppmfile]";
char *pm_progname;
/* getopt stuff */
extern int optind;
extern char *optarg;
/* option parsing */
while ((i = getopt(argc, argv, "dvq")) >= 0) {
switch (i) {
case 'd': /* dither */
dither = 1; break;
case 'v': /* verbose */
++verbose;
break;
case 'q': /* quiet */
verbose = 0; break;
}
}
if ((pm_progname = strrchr(argv[0], '/')) != NULL) ++pm_progname;
else pm_progname = argv[0];
if ((argc - optind) > 1) pm_usage( usage );
if ((argc - optind) == 1) ifd = pm_openr( argv[optind] );
else ifd = stdin;
/*
** Step 0: read in the image.
*/
pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
pm_close( ifd );
/*
** Step 1: catalog the colors into a color cube.
*/
if (verbose) {
fprintf( stderr, "%s: building color tables %s", pm_progname, BARGRAPH);
j = (i = rows / BARGRAPHLEN) / 2;
}
/* Count all occurances of each color */
for (row = 0; row < rows; ++row) {
if (verbose) {
if (row > j) {
putc('*', stderr);
j += i;
}
}
if (maxval == 255) {
for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) {
++(color_cube[PPM_GETR(*pP) / (256 / MAX_RED)]
[PPM_GETG(*pP) / (256 / MAX_GREEN)]
[PPM_GETB(*pP) / (256 / MAX_BLUE)]
);
}
}
else {
for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) {
r = (PPM_GETR(*pP) * MAX_RED) / (maxval + 1);
g = (PPM_GETG(*pP) * MAX_GREEN)/ (maxval + 1);
b = (PPM_GETB(*pP) * MAX_BLUE) / (maxval + 1);
++(color_cube[r][g][b]);
}
}
}
/*
** Step 2: Determine weight of each color and the weight of a representative color.
*/
/* Initialize logarithmic weighing table */
for (i = 2; i < MAXWEIGHT; ++i) {
weight_convert[i] = (int) (100.0 * log((double)(i)));
}
k = rows * cols;
if ((k /= STDWEIGHT_DIV) == 0) k = 1;
total_weight = i = 0;
for (g = 0; g < MAX_GREEN; ++g) {
for (r = 0; r < MAX_RED; ++r) {
for (b = 0; b < MAX_BLUE; ++b) {
register int weight;
/* Normalize the weights, independent of picture size. */
weight = color_cube[r][g][b] * STDWEIGHT_MUL;
weight /= k;
if (weight) ++i;
if (weight >= MAXWEIGHT) weight = MAXWEIGHT - 1;
total_weight += (color_cube[r][g][b] = weight_convert[weight]);
}
}
cum_weight[g] = total_weight;
}
rep_weight = total_weight / COLORS;
if (verbose) {
putc('\n', stderr);
if (verbose > 1) {
fprintf(stderr, " found %d colors with total weight %d\n",
i, total_weight);
fprintf(stderr, " avg weight for colors used = %7.2f\n",
(float)total_weight/i);
fprintf(stderr, " avg weight for all colors = %7.2f\n",
(float)total_weight/(MAX_RED * MAX_GREEN * MAX_BLUE));
fprintf(stderr, " avg weight for final colors = %4d\n", rep_weight);
}
fprintf( stderr, "%s: selecting new colors..."