home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
netpbma.zip
/
ppm
/
ppmtoxpm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-31
|
15KB
|
489 lines
/* ppmtoxpm.c - read a portable pixmap and produce a (version 3) X11 pixmap
**
** Copyright (C) 1990 by Mark W. Snitily
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
**
** This tool was developed for Schlumberger Technologies, ATE Division, and
** with their permission is being made available to the public with the above
** copyright notice and permission notice.
**
** Upgraded to XPM2 by
** Paul Breslaw, Mecasoft SA, Zurich, Switzerland (paul@mecazh.uu.ch)
** Thu Nov 8 16:01:17 1990
**
** Upgraded to XPM version 3 by
** Arnaud Le Hors (lehors@mirsa.inria.fr)
** Tue Apr 9 1991
**
** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
** - Bug fix, should should malloc space for rgbn[j].name+1 in line 441
** caused segmentation faults
**
** - lowercase conversion of RGB names def'ed out,
** considered harmful.
*/
#include "ppm.h"
#include "ppmcmap.h"
/* Max number of colors allowed in ppm input. */
#define MAXCOLORS 256
/* Max number of rgb mnemonics allowed in rgb text file. */
#define MAX_RGBNAMES 1024
/* Lower bound and upper bound of character-pixels printed in XPM output.
Be careful, don't want the character '"' in this range. */
/*#define LOW_CHAR '#' <-- minimum ascii character allowed */
/*#define HIGH_CHAR '~' <-- maximum ascii character allowed */
#define LOW_CHAR '`'
#define HIGH_CHAR 'z'
#define max(a,b) ((a) > (b) ? (a) : (b))
typedef struct { /* rgb values and ascii names (from
* rgb text file) */
int r, g, b; /* rgb values, range of 0 -> 65535 */
char *name; /* color mnemonic of rgb value */
} rgb_names;
typedef struct { /* character-pixel mapping */
char *cixel; /* character string printed for
* pixel */
char *rgbname; /* ascii rgb color, either color
* mnemonic or #rgb value */
} cixel_map;
/* prototypes/forward reference */
static void read_rgb_names ARGS((char *, rgb_names *, int *));
static char * gen_numstr ARGS((int, int, int));
static void gen_cmap ARGS((colorhist_vector, int, pixval, int, rgb_names *, int, cixel_map *, int *));
pixel **pixels;
int
main(argc, argv)
int argc;
char *argv[];
{
FILE *ifd;
register pixel *pP;
int argn, rows, cols, ncolors, row, col, i;
pixval maxval; /* pixval == unsigned char or
* unsigned short */
colorhash_table cht;
colorhist_vector chv;
/* Used for rgb value -> rgb mnemonic mapping */
int map_rgb_names = 0;
rgb_names *rgbn; /* rgb_names rgbn[MAX_RGBNAMES]; */
int rgbn_max;
/* Used for rgb value -> character-pixel string mapping */
cixel_map *cmap; /* cixel_map cmap[MAXCOLORS]; */
int charspp; /* chars per pixel */
char out_name[100], rgb_fname[100], *cp;
char *usage = "[-name <xpm-name>] [-rgb <rgb-textfile>] [ppmfile]";
ppm_init(&argc, argv);
out_name[0] = rgb_fname[0] = '\0';
argn = 1;
/* Check for command line options. */
while (argn < argc && argv[argn][0] == '-') {
/* Case "-", use stdin for input. */
if (argv[argn][1] == '\0')
break;
/* Case "-name <xpm-filename>", get output filename. */
if (strncmp(argv[argn], "-name", max(strlen(argv[argn]), 2)) == 0) {
argn++;
if (argn == argc || sscanf(argv[argn], "%s", out_name) != 1)
pm_usage(usage);
}
/* Case "-rgb <rgb-filename>", get rgb mnemonics filename. */
else if (strncmp(argv[argn], "-rgb", max(strlen(argv[argn]), 2)) == 0) {
argn++;
if (argn == argc || sscanf(argv[argn], "%s", rgb_fname) != 1)
pm_usage(usage);
map_rgb_names = 1;
}
/* Nothing else allowed... */
else
pm_usage(usage);
argn++;
}
/* Input file specified, open it and set output filename if necessary. */
if (argn < argc) {
/* Open the input file. */
ifd = pm_openr(argv[argn]);
/* If output filename not specified, use input filename as default. */
if (out_name[0] == '\0') {
strcpy(out_name, argv[argn]);
if (cp = index(out_name, '.'))
*cp = '\0'; /* remove extension */
}
/*
* If (1) input file was specified as "-" we're using stdin, or (2)
* output filename was specified as "-", set output filename to the
* default.
*/
if (!strcmp(out_name, "-"))
strcpy(out_name, "noname");
argn++;
}
/* No input file specified. Using stdin so set default output filename. */
else {
ifd = stdin;
if (out_name[0] == '\0')
strcpy(out_name, "noname");
}
/* Only 0 or 1 input files allowed. */
if (argn != argc)
pm_usage(usage);
/*
* "maxval" is the largest value that can be be found in the ppm file.
* All pixel components are relative to this value.
*/
pixels = ppm_readppm(ifd, &cols, &rows, &maxval);
pm_close(ifd);
/* Figure out the colormap. */
fprintf(stderr, "(Computing colormap...");
fflush(stderr);
chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &ncolors);
if (chv == (colorhist_vector) 0)
pm_error(
"too many colors - try running the pixmap through 'ppmquant 256'",
0, 0, 0, 0, 0);
fprintf(stderr, " Done. %d colors found.)\n", ncolors);
/* Make a hash table for fast color lookup. */
cht = ppm_colorhisttocolorhash(chv, ncolors);
/*
* If a rgb text file was specified, read in the rgb mnemonics. Does not
* return if fatal error occurs.
*/
rgbn = (rgb_names *) malloc(MAX_RGBNAMES * sizeof(rgb_names));
if (rgbn == (rgb_names *) NULL)
pm_error("out of memory");
if (map_rgb_names)
read_rgb_names(rgb_fname, rgbn, &rgbn_max);
cmap = (cixel_map *)malloc(ncolors * sizeof(cixel_map));
if (cmap == (cixel_map *) NULL)
pm_error("out of memory");
/* Now generate the character-pixel colormap table. */
gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
cmap, &charspp);
/* Write out the XPM file. */
printf("/* XPM */\n");
printf("static char *%s[] = {\n", out_name);
printf("/* width height ncolors chars_per_pixel */\n");
printf("\"%d %d %d %d\",\n", cols, rows, ncolors, charspp);
printf("/* colors */\n");
for (i = 0; i < ncolors; i++) {
printf("\"%s c %s\",\n", cmap[i].cixel, cmap[i].rgbname);
}
printf("/* pixels */\n");
for (row = 0; row < rows; row++) {
printf("\"");
for (col = 0, pP = pixels[row]; col < cols; col++, pP++) {
printf("%s", cmap[ppm_lookupcolor(cht, pP)].cixel);
}
printf("\"%s\n", (row == (rows - 1) ? "" : ","));
}
printf("};\n");
exit(0);
} /* main */
/*---------------------------------------------------------------------------*/
/* This routine reads a rgb text file. It stores the rgb values (0->65535)
and the rgb mnemonics (malloc'ed) into the "rgbn" array. Returns the
number of entries stored in "rgbn_max". */
static
void
read_rgb_names(rgb_fname, rgbn, rgbn_max)
char *rgb_fname;
rgb_names *rgbn;
int *rgbn_max;
{
FILE *rgbf;
int i, items, red, green, blue;
char line[512], name[512], *rgbname, *n, *m;
/* Open the rgb text file. Abort if error. */
if ((rgbf = fopen(rgb_fname, "r")) == NULL)
pm_error("error opening rgb text file \"%s\"", rgb_fname, 0, 0, 0, 0);
/* Loop reading each line in the file. */
for (i = 0; fgets(line, sizeof(line), rgbf); i++) {
/* Quit if rgb text file is too large. */
if (i == MAX_RGBNAMES) {
fprintf(stderr,
"Too many entries in rgb text file, truncated to %d entries.\n",
MAX_RGBNAMES);
fflush(stderr);
break;
}
/* Read the line. Skip if bad. */
items = sscanf(line, "%d %d %d %[^\n]\n", &red, &green, &blue, name);
if (items != 4) {
fprintf(stderr, "rgb text file syntax error on line %d\n", i + 1);
fflush(stderr);
i--;
continue;
}
/* Make sure rgb values are within 0->255 range. Skip if bad. */
if (red < 0 || red > 0xFF ||
green < 0 || green > 0xFF ||
blue < 0 || blue > 0xFF) {
fprintf(stderr, "rgb value for \"%s\" out of range, ignoring it\n",
name);
fflush(stderr);
i--;
continue;
}
/* Allocate memory for ascii name. Abort if error. */
if (!(rgbname = (char *) malloc(strlen(name) + 1)))
pm_error("out of memory allocating rgb name", 0, 0, 0, 0, 0);
#ifdef NAMESLOWCASE
/* Copy string to ascii name and lowercase it. */
for (n = name, m = rgbname; *n; n++)
*m++ = isupper(*n) ? tolower(*n) : *n;
*m = '\0';
#else
strcpy(rgbname, name);
#endif
/* Save the rgb values and ascii name in the array. */
rgbn[i].r = red << 8;
rgbn[i].g = green << 8;
rgbn[i].b = blue << 8;
rgbn[i].name = rgbname;
}
/* Return the max number of rgb names. */
*rgbn_max = i - 1;
fclose(rgbf);
} /* read_rgb_names */
/*---------------------------------------------------------------------------*/
/* Given a number and a base, (base == HIGH_CHAR-LOW_CHAR+1), this routine
prints the number into a malloc'ed string and returns it. The length of
the string is specified by "digits". The ascii characters of the printed
number range from LOW_CHAR to HIGH_CHAR. The string is LOW_CHAR filled,
(e.g. if LOW_CHAR==0, HIGH_CHAR==1, digits==5, i=3, routine would return
the malloc'ed string "00011"). */
static
char *
gen_numstr(i, base, digits)
int i, base, digits;
{
char *str, *p;
int d;
/* Allocate memory for printed number. Abort if error. */
if (!(str = (char *) malloc(digits + 1)))
pm_error("out of memory", 0, 0, 0, 0, 0);
/* Generate characters starting with least significant digit. */
p = str + digits;
*p-- = '\0'; /* nul terminate string */
while (p >= str) {
d = i % base;
i /= base;
*p-- = (char) ((int) LOW_CHAR + d);
}
return str;
} /* gen_numstr */
/*---------------------------------------------------------------------------*/
/* This routine generates the character-pixel colormap table. */
static
void
gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
cmap, charspp)
/* input: */
colorhist_vector chv; /* contains rgb values for colormap */
int ncolors; /* number of entries in colormap */
pixval maxval; /* largest color value, all rgb
* values relative to this, (pixval
* == unsigned short) */
int map_rgb_names; /* == 1 if mapping rgb values to rgb
* mnemonics */
rgb_names *rgbn; /* rgb mnemonics from rgb text file */
int rgbn_max; /* number of rgb mnemonics in table */
/* output: */
cixel_map *cmap; /* pixel strings and ascii rgb
* colors */
int *charspp; /* characters per pixel */
{
int i, j, base, cpp, mval, red, green, blue, r, g, b, matched;
char *str;
/*
* Figure out how many characters per pixel we'll be using. Don't want
* to be forced to link with libm.a, so using a division loop rather
* than a log function.
*/
base = (int) HIGH_CHAR - (int) LOW_CHAR + 1;
for (cpp = 0, j = ncolors; j; cpp++)
j /= base;
*charspp = cpp;
/*
* Determine how many hex digits we'll be normalizing to if the rgb
* value doesn't match a color mnemonic.
*/
mval = (int) maxval;
if (mval <= 0x000F)
mval = 0x000F;
else if (mval <= 0x00FF)
mval = 0x00FF;
else if (mval <= 0x0FFF)
mval = 0x0FFF;
else
mval = 0xFFFF;
/*
* Generate the character-pixel string and the rgb name for each
* colormap entry.
*/
for (i = 0; i < ncolors; i++) {
/*
* The character-pixel string is simply a printed number in base
* "base" where the digits of the number range from LOW_CHAR to
* HIGH_CHAR and the printed length of the number is "cpp".
*/
cmap[i].cixel = gen_numstr(i, base, cpp);
/* Fetch the rgb value of the current colormap entry. */
red = PPM_GETR(chv[i].color);
green = PPM_GETG(chv[i].color);
blue = PPM_GETB(chv[i].color);
/*
* If the ppm color components are not relative to 15, 255, 4095,
* 65535, normalize the color components here.
*/
if (mval != (int) maxval) {
red = (red * mval) / (int) maxval;
green = (green * mval) / (int) maxval;
blue = (blue * mval) / (int) maxval;
}
/*
* If the "-rgb <rgbfile>" option was specified, attempt to map the
* rgb value to a color mnemonic.
*/
if (map_rgb_names) {
/*
* The rgb values of the color mnemonics are normalized relative
* to 255 << 8, (i.e. 0xFF00). [That's how the original MIT
* code did it, really should have been "v * 65535 / 255"
* instead of "v << 8", but have to use the same scheme here or
* else colors won't match...] So, if our rgb values aren't
* already 16-bit values, need to shift left.
*/
if (mval == 0x000F) {
r = red << 12;
g = green << 12;
b = blue << 12;
/* Special case hack for "white". */
if (0xF000 == r && r == g && g == b)
r = g = b = 0xFF00;
} else if (mval == 0x00FF) {
r = red << 8;
g = green << 8;
b = blue << 8;
} else if (mval == 0x0FFF) {
r = red << 4;
g = green << 4;
b = blue << 4;
} else {
r = red;
g = green;
b = blue;
}
/*
* Just perform a dumb linear search over the rgb values of the
* color mnemonics. One could speed things up by sorting the
* rgb values and using a binary search, or building a hash
* table, etc...
*/
for (matched = 0, j = 0; j <= rgbn_max; j++)
if (r == rgbn[j].r && g == rgbn[j].g && b == rgbn[j].b) {
/* Matched. Allocate string, copy mnemonic, and exit. */
if (!(str = (char *) malloc(strlen(rgbn[j].name) + 1)))
pm_error("out of memory", 0, 0, 0, 0, 0);
strcpy(str, rgbn[j].name);
cmap[i].rgbname = str;
matched = 1;
break;
}
if (matched)
continue;
}
/*
* Either not mapping to color mnemonics, or didn't find a match.
* Generate an absolute #RGB value string instead.
*/
if (!(str = (char *) malloc(mval == 0x000F ? 5 :
mval == 0x00FF ? 8 :
mval == 0x0FFF ? 11 :
14)))
pm_error("out of memory", 0, 0, 0, 0, 0);
sprintf(str, mval == 0x000F ? "#%X%X%X" :
mval == 0x00FF ? "#%02X%02X%02X" :
mval == 0x0FFF ? "#%03X%03X%03X" :
"#%04X%04X%04X", red, green, blue);
cmap[i].rgbname = str;
}
} /* gen_cmap */