|
Packit |
78deda |
/* xpmtoppm.c - convert XPM file (X11 pixmap) to PPM
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Copyright and history information is at end of file
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
|
|
Packit |
78deda |
#define _BSD_SOURCE /* Make sure strdup() is in string.h */
|
|
Packit |
78deda |
#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include <assert.h>
|
|
Packit |
78deda |
#include <string.h>
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "pm_c_util.h"
|
|
Packit |
78deda |
#include "mallocvar.h"
|
|
Packit |
78deda |
#include "shhopt.h"
|
|
Packit |
78deda |
#include "nstring.h"
|
|
Packit |
78deda |
#include "ppm.h"
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#define MAX_LINE (8 * 1024)
|
|
Packit |
78deda |
/* The maximum size XPM input line we can handle. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* number of xpmColorKeys */
|
|
Packit |
78deda |
#define NKEYS 5
|
|
Packit |
78deda |
|
|
Packit |
78deda |
const char *xpmColorKeys[] =
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
"s", /* key #1: symbol */
|
|
Packit |
78deda |
"m", /* key #2: mono visual */
|
|
Packit |
78deda |
"g4", /* key #3: 4 grays visual */
|
|
Packit |
78deda |
"g", /* key #4: gray visual */
|
|
Packit |
78deda |
"c", /* key #5: color visual */
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct cmdlineInfo {
|
|
Packit |
78deda |
/* All the information the user supplied in the command line,
|
|
Packit |
78deda |
in a form easy for the program to use.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
const char * input_filespec; /* Filespecs of input files */
|
|
Packit |
78deda |
const char * alpha_filename;
|
|
Packit |
78deda |
int alpha_stdout;
|
|
Packit |
78deda |
int verbose;
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static bool verbose;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
parseCommandLine(int argc, char ** argv,
|
|
Packit |
78deda |
struct cmdlineInfo *cmdlineP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Note that the file spec array we return is stored in the storage that
|
|
Packit |
78deda |
was passed to us as the argv array.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
optEntry * option_def;
|
|
Packit |
78deda |
/* Instructions to OptParseOptions2 on how to parse our options.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
optStruct3 opt;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int option_def_index;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
MALLOCARRAY_NOFAIL(option_def, 100);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
option_def_index = 0; /* incremented by OPTENT3 */
|
|
Packit |
78deda |
OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename,
|
|
Packit |
78deda |
NULL, 0);
|
|
Packit |
78deda |
OPTENT3(0, "verbose", OPT_FLAG, &cmdlineP->verbose,
|
|
Packit |
78deda |
NULL, 0);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
cmdlineP->alpha_filename = NULL;
|
|
Packit |
78deda |
cmdlineP->verbose = FALSE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
opt.opt_table = option_def;
|
|
Packit |
78deda |
opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
|
|
Packit |
78deda |
opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
|
|
Packit |
78deda |
/* Uses and sets argc, argv, and some of *cmdlineP and others. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (argc - 1 == 0)
|
|
Packit |
78deda |
cmdlineP->input_filespec = NULL; /* he wants stdin */
|
|
Packit |
78deda |
else if (argc - 1 == 1)
|
|
Packit |
78deda |
cmdlineP->input_filespec = strdup(argv[1]);
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
pm_error("Too many arguments. The only argument accepted\n"
|
|
Packit |
78deda |
"is the input file specification");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdlineP->alpha_filename &&
|
|
Packit |
78deda |
streq(cmdlineP->alpha_filename, "-"))
|
|
Packit |
78deda |
cmdlineP->alpha_stdout = TRUE;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
cmdlineP->alpha_stdout = FALSE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct ColorNameHashTableEntry {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
An entry in the color name hash table. It maps a color name to a
|
|
Packit |
78deda |
color, or is empty.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
bool empty;
|
|
Packit |
78deda |
char colorName[3];
|
|
Packit |
78deda |
/* Actual length 0-3. NOT NUL-terminated */
|
|
Packit |
78deda |
pixel color;
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
typedef struct {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
This is a color map which is primarily a hash table that maps an XPM
|
|
Packit |
78deda |
color name to a color. An XPM color name is a 0-3 character name that
|
|
Packit |
78deda |
appears in the raster of an XPM image to uniquely identify a color.
|
|
Packit |
78deda |
The header of the XPM contains a listing of all the color names that
|
|
Packit |
78deda |
appear in the raster, identifying a color for each.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
We represent a color as a 'pixel'.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
unsigned int nameSize;
|
|
Packit |
78deda |
/* Size of color names in this hash. 0-3 */
|
|
Packit |
78deda |
struct ColorNameHashTableEntry * transparentP;
|
|
Packit |
78deda |
/* The element of 'table' that is for the transparent color.
|
|
Packit |
78deda |
NULL if there is none.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* This is an internally chained hash table, i.e. there are no linked
|
|
Packit |
78deda |
lists. You use the hash function to get an index into the hash table.
|
|
Packit |
78deda |
If the entry indexed by that is not for the color name you're looking
|
|
Packit |
78deda |
for, you look at the next entry down, and keep going down until you
|
|
Packit |
78deda |
either find the color name you're looking for or hit an empty entry.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
So that we never run out of space for new color names, we make the
|
|
Packit |
78deda |
creator of the hash table tell us the maximum number of colors there
|
|
Packit |
78deda |
will be. We allocate twice that size in order to reduce average hash
|
|
Packit |
78deda |
chain length.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
unsigned int size;
|
|
Packit |
78deda |
struct ColorNameHashTableEntry * table;
|
|
Packit |
78deda |
} ColorNameHash;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static ColorNameHash *
|
|
Packit |
78deda |
hash_create(unsigned int const nColors,
|
|
Packit |
78deda |
unsigned int const nameSize) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ColorNameHash * hashP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
MALLOCVAR_NOFAIL(hashP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hashP->nameSize = nameSize;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hashP->size = nColors * 2;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
MALLOCARRAY(hashP->table, hashP->size);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!hashP->table)
|
|
Packit |
78deda |
pm_error("Failed to allocate memory for a %u-entry "
|
|
Packit |
78deda |
"color name hash table.", hashP->size);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
for (i = 0; i < hashP->size; ++i)
|
|
Packit |
78deda |
hashP->table[i].empty = true;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hashP->transparentP = NULL;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return hashP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
hash_destroy(ColorNameHash * const hashP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
free(hashP->table);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
free(hashP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static unsigned int
|
|
Packit |
78deda |
hashColorName(const char * const name,
|
|
Packit |
78deda |
unsigned int const size,
|
|
Packit |
78deda |
unsigned int const hashTableSize) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Return the hash value (initial index into the color name hash table)
|
|
Packit |
78deda |
for the color name 'name', which is 'size' characters long. The hash
|
|
Packit |
78deda |
is to be in the range [0, hashTableSize).
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
/* I have no idea if this is an appropriate hash function. I borrowed
|
|
Packit |
78deda |
it from pnm_hashTuple()
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int const hash_factor[] = {1, 33, 33*33};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
unsigned int hash;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hash = 0; /* initial value */
|
|
Packit |
78deda |
for (i = 0; i < size; ++i) {
|
|
Packit |
78deda |
hash += name[i] * hash_factor[i];
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
hash %= hashTableSize;
|
|
Packit |
78deda |
return hash;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static bool
|
|
Packit |
78deda |
entryMatch(struct ColorNameHashTableEntry const entry,
|
|
Packit |
78deda |
const char * const name,
|
|
Packit |
78deda |
unsigned int const size) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (entry.empty)
|
|
Packit |
78deda |
return true;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
assert(size <= ARRAY_SIZE(entry.colorName));
|
|
Packit |
78deda |
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (i = 0; i < size; ++i) {
|
|
Packit |
78deda |
if (name[i] != entry.colorName[i])
|
|
Packit |
78deda |
return false;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return true;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
bumpIndex(unsigned int * const indexP,
|
|
Packit |
78deda |
unsigned int const tableSize,
|
|
Packit |
78deda |
unsigned int const limit) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Bump *indexP to the next entry in a table of size 'tableSize', in a
|
|
Packit |
78deda |
circular fashion. But abort the program if this would take us to
|
|
Packit |
78deda |
'limit'.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
*indexP += 1;
|
|
Packit |
78deda |
if (*indexP >= tableSize)
|
|
Packit |
78deda |
*indexP = 0;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (*indexP == limit)
|
|
Packit |
78deda |
pm_error("INTERNAL ERROR: color name hash table is full");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
hash_find(const ColorNameHash * const hashP,
|
|
Packit |
78deda |
const char * const name,
|
|
Packit |
78deda |
struct ColorNameHashTableEntry ** const entryPP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Find the entry in the color hash table *hashP for the color
|
|
Packit |
78deda |
named 'name' in the lexicon of this XPM file. If the color is in the
|
|
Packit |
78deda |
table, this is where it is. If it isn't, this is where it should go.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
unsigned int const initialIndex =
|
|
Packit |
78deda |
hashColorName(name, hashP->nameSize, hashP->size);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (i = initialIndex;
|
|
Packit |
78deda |
!entryMatch(hashP->table[i], name, hashP->nameSize);
|
|
Packit |
78deda |
bumpIndex(&i, hashP->size, initialIndex));
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*entryPP = &hashP->table[i];
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
hash_add(ColorNameHash * const hashP,
|
|
Packit |
78deda |
const char * const name,
|
|
Packit |
78deda |
pixel const color,
|
|
Packit |
78deda |
bool const isTransparent) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct ColorNameHashTableEntry * entryP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hash_find(hashP, name, &entryP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!entryP->empty)
|
|
Packit |
78deda |
pm_error("Color name appears multiple times in color map");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
entryP->empty = false;
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
for (i = 0; i < hashP->nameSize; ++i)
|
|
Packit |
78deda |
entryP->colorName[i] = name[i];
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
entryP->color = color;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (isTransparent) {
|
|
Packit |
78deda |
if (hashP->transparentP)
|
|
Packit |
78deda |
pm_error("There are multiple NONE (transparent) entries in "
|
|
Packit |
78deda |
"the XPM color map");
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
hashP->transparentP = entryP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static pixel
|
|
Packit |
78deda |
hash_color(const ColorNameHash * const hashP,
|
|
Packit |
78deda |
const char * const name) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct ColorNameHashTableEntry * entryP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hash_find(hashP, name, &entryP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (entryP->empty)
|
|
Packit |
78deda |
pm_error("Color name in raster is not in color map");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return entryP->color;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static bool
|
|
Packit |
78deda |
hash_isTransparent(const ColorNameHash * const hashP,
|
|
Packit |
78deda |
const char * const name) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct ColorNameHashTableEntry * entryP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hash_find(hashP, name, &entryP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return (entryP == hashP->transparentP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static char lastInputLine[MAX_LINE+1];
|
|
Packit |
78deda |
/* contents of line most recently read from input */
|
|
Packit |
78deda |
static bool backup;
|
|
Packit |
78deda |
/* TRUE means next read should be a reread of the most recently read
|
|
Packit |
78deda |
line, i.e. lastInputLine, instead of a read from the input file.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
getLine(char * const line,
|
|
Packit |
78deda |
size_t const size,
|
|
Packit |
78deda |
FILE * const stream) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the next line from the input file 'stream', through the one-line
|
|
Packit |
78deda |
buffer lastInputLine[].
|
|
Packit |
78deda |
|
|
Packit |
78deda |
If 'backup' is true, the "next line" is the previously read line, i.e.
|
|
Packit |
78deda |
the one in that one-line buffer. Otherwise, the "next line" is the next
|
|
Packit |
78deda |
line from the real file. After reading the backed up line, we reset
|
|
Packit |
78deda |
'backup' to false.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Return the line as a null terminated string in *line, which is an
|
|
Packit |
78deda |
array of 'size' bytes.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Exit program if the line doesn't fit in the buffer.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
if (size > sizeof(lastInputLine))
|
|
Packit |
78deda |
pm_error("INTERNAL ERROR: getLine() received 'size' parameter "
|
|
Packit |
78deda |
"which is out of bounds");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (backup) {
|
|
Packit |
78deda |
strncpy(line, lastInputLine, size);
|
|
Packit |
78deda |
backup = FALSE;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
if (fgets(line, size, stream) == NULL)
|
|
Packit |
78deda |
pm_error("EOF or read error on input file");
|
|
Packit |
78deda |
if (strlen(line) == size - 1)
|
|
Packit |
78deda |
pm_error("Input file has line that is too long (longer than "
|
|
Packit |
78deda |
"%u bytes).", (unsigned)size - 1);
|
|
Packit |
78deda |
STRSCPY(lastInputLine, line);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
getword(char * const output, char ** const cursorP) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
char *t1;
|
|
Packit |
78deda |
char *t2;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (t1=*cursorP; ISSPACE(*t1); t1++); /* skip white space */
|
|
Packit |
78deda |
for (t2 = t1; !ISSPACE(*t2) && *t2 != '"' && *t2 != '\0'; t2++);
|
|
Packit |
78deda |
/* Move to next white space, ", or eol */
|
|
Packit |
78deda |
if (t2 > t1)
|
|
Packit |
78deda |
strncpy(output, t1, t2 - t1);
|
|
Packit |
78deda |
output[t2 - t1] = '\0';
|
|
Packit |
78deda |
*cursorP = t2;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
addToColorMap(ColorNameHash * const hashP,
|
|
Packit |
78deda |
const char * const colorName,
|
|
Packit |
78deda |
char const colorspec[],
|
|
Packit |
78deda |
bool const isTransparent) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Add the color named by colorspec[] to the colormap represented by *hashP,
|
|
Packit |
78deda |
as the color associated with XPM color name 'colorNumber'.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Note that *hashP determines how long 'colorName' is.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
hash_add(hashP, colorName, ppm_parsecolor(colorspec, PPM_MAXMAXVAL),
|
|
Packit |
78deda |
isTransparent);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
validateColorName(const char * const name,
|
|
Packit |
78deda |
unsigned int const charsPerPixel) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (i = 0; i < charsPerPixel; ++i) {
|
|
Packit |
78deda |
if (name[i] == '"')
|
|
Packit |
78deda |
pm_error("A color map entry ends in the middle of the colormap "
|
|
Packit |
78deda |
"index");
|
|
Packit |
78deda |
else if (name[i] == '\0')
|
|
Packit |
78deda |
pm_error("The XPM file ends in the middle of a color map entry");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
interpretXpm3ColorTableLine(char const line[],
|
|
Packit |
78deda |
unsigned int const seqNum,
|
|
Packit |
78deda |
unsigned int const charsPerPixel,
|
|
Packit |
78deda |
ColorNameHash * const hashP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Interpret one line of the color table in the XPM header. 'line' is the
|
|
Packit |
78deda |
line from the XPM file. It is the seqNum'th color table entry in the file.
|
|
Packit |
78deda |
The raster in the file uses 'charsPerPixel' characters per pixel (i.e.
|
|
Packit |
78deda |
a an XPM color name is 'charsPerPixel' characters).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Add the information from this color table entry to the color name hash
|
|
Packit |
78deda |
*hashP.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
The line may include values for multiple kinds of color (grayscale,
|
|
Packit |
78deda |
color, etc.). We take the highest of these (e.g. color over grayscale).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
If a color table entry indicates transparency, set *transparentP
|
|
Packit |
78deda |
to indicate the XPM color name.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
/* Note: this code seems to allow for multi-word color specifications,
|
|
Packit |
78deda |
but I'm not aware that such are legal. Ultimately, ppm_parsecolor()
|
|
Packit |
78deda |
interprets the name, and I believe it takes only single word
|
|
Packit |
78deda |
color specifications. -Bryan 2001.05.06.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
char str2[MAX_LINE+1];
|
|
Packit |
78deda |
char * t1;
|
|
Packit |
78deda |
char * t2;
|
|
Packit |
78deda |
int endOfEntry; /* boolean */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int curkey, key, highkey; /* current color key */
|
|
Packit |
78deda |
bool lastwaskey;
|
|
Packit |
78deda |
/* The last token we processes was a key, and we have processed
|
|
Packit |
78deda |
at least one token.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
char curbuf[BUFSIZ]; /* current buffer */
|
|
Packit |
78deda |
bool isTransparent;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
const char * colorName;
|
|
Packit |
78deda |
/* The 0-3 character name this color map line gives the color
|
|
Packit |
78deda |
(i.e. the name that the raster uses). This is NOT NUL-terminated.
|
|
Packit |
78deda |
It's length is bytesPerPixel.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* read the chars */
|
|
Packit |
78deda |
t1 = strchr(line, '"');
|
|
Packit |
78deda |
if (t1 == NULL)
|
|
Packit |
78deda |
pm_error("A line that is supposed to be an entry in the color "
|
|
Packit |
78deda |
"table does not start with a quote. The line is '%s'. "
|
|
Packit |
78deda |
"It is the %uth entry in the color table.",
|
|
Packit |
78deda |
line, seqNum);
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
++t1; /* Points now to first color number character */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
validateColorName(t1, charsPerPixel);
|
|
Packit |
78deda |
colorName = t1;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
t1 += charsPerPixel;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/*
|
|
Packit |
78deda |
* read color keys and values
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
curkey = 0;
|
|
Packit |
78deda |
highkey = 1;
|
|
Packit |
78deda |
lastwaskey = FALSE;
|
|
Packit |
78deda |
t2 = t1;
|
|
Packit |
78deda |
endOfEntry = FALSE;
|
|
Packit |
78deda |
while ( !endOfEntry ) {
|
|
Packit |
78deda |
int isKey; /* boolean */
|
|
Packit |
78deda |
getword(str2, &t2;;
|
|
Packit |
78deda |
if (strlen(str2) == 0)
|
|
Packit |
78deda |
endOfEntry = TRUE;
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
/* See if the word we got is a valid key (and get its key
|
|
Packit |
78deda |
number if so)
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
for (key = 1;
|
|
Packit |
78deda |
key <= NKEYS && !streq(xpmColorKeys[key - 1], str2);
|
|
Packit |
78deda |
key++);
|
|
Packit |
78deda |
isKey = (key <= NKEYS);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (lastwaskey || !isKey) {
|
|
Packit |
78deda |
/* This word is a color specification (or "none" for
|
|
Packit |
78deda |
transparent).
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
if (!curkey)
|
|
Packit |
78deda |
pm_error("Missing color key token in color table line "
|
|
Packit |
78deda |
"'%s' before '%s'.", line, str2);
|
|
Packit |
78deda |
if (!lastwaskey)
|
|
Packit |
78deda |
strcat(curbuf, " "); /* append space */
|
|
Packit |
78deda |
if ( (strneq(str2, "None", 4))
|
|
Packit |
78deda |
|| (strneq(str2, "none", 4)) ) {
|
|
Packit |
78deda |
/* This entry identifies the transparent color number */
|
|
Packit |
78deda |
strcat(curbuf, "#000000"); /* Make it black */
|
|
Packit |
78deda |
isTransparent = TRUE;
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
strcat(curbuf, str2); /* append buf */
|
|
Packit |
78deda |
lastwaskey = FALSE;
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
/* This word is a key. So we've seen the last of the
|
|
Packit |
78deda |
info for the previous key, and we must either put it
|
|
Packit |
78deda |
in the color map or ignore it if we already have a higher
|
|
Packit |
78deda |
color form in the colormap for this colormap entry.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
if (curkey > highkey) { /* flush string */
|
|
Packit |
78deda |
addToColorMap(hashP, colorName, curbuf, isTransparent);
|
|
Packit |
78deda |
highkey = curkey;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
/* intialize state to process this new key */
|
|
Packit |
78deda |
curkey = key;
|
|
Packit |
78deda |
curbuf[0] = '\0';
|
|
Packit |
78deda |
isTransparent = FALSE;
|
|
Packit |
78deda |
lastwaskey = TRUE;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (*t2 == '"') break;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
/* Put the info for the last key in the line into the colormap (or
|
|
Packit |
78deda |
ignore it if there's already a higher color form for this colormap
|
|
Packit |
78deda |
entry in it)
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
if (curkey > highkey) {
|
|
Packit |
78deda |
addToColorMap(hashP, colorName, curbuf, isTransparent);
|
|
Packit |
78deda |
highkey = curkey;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (highkey == 1)
|
|
Packit |
78deda |
pm_error("C error scanning color table");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
readV3ColorTable(FILE * const ifP,
|
|
Packit |
78deda |
ColorNameHash ** const colorNameHashPP,
|
|
Packit |
78deda |
unsigned int const nColors,
|
|
Packit |
78deda |
unsigned int const charsPerPixel) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the color table from the XPM Version 3 header.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Assume *ifP is positioned to the color table; leave it positioned after.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int seqNum;
|
|
Packit |
78deda |
/* Sequence number of entry within color table in XPM header */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (seqNum = 0; seqNum < nColors; ++seqNum) {
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
/* skip the comment line if any */
|
|
Packit |
78deda |
if (strneq(line, "/*", 2))
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
interpretXpm3ColorTableLine(line, seqNum, charsPerPixel,
|
|
Packit |
78deda |
colorNameHashP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
*colorNameHashPP = colorNameHashP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
readXpm3Header(FILE * const ifP,
|
|
Packit |
78deda |
unsigned int * const widthP,
|
|
Packit |
78deda |
unsigned int * const heightP,
|
|
Packit |
78deda |
unsigned int * const charsPerPixelP,
|
|
Packit |
78deda |
ColorNameHash ** const colorNameHashPP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the header of the XPM file on stream *ifP. Assume the
|
|
Packit |
78deda |
getLine() stream is presently positioned to the beginning of the
|
|
Packit |
78deda |
file and it is a Version 3 XPM file. Leave the stream positioned
|
|
Packit |
78deda |
after the header.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Return as *widthP and *heightP the dimensions of the image indicated
|
|
Packit |
78deda |
by the header.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Return as *charsPerPixelP the number of characters the header says the
|
|
Packit |
78deda |
raster uses for each pixel, i.e. the XPM color name length.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Return the color map as *colorNameHashPP.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
const char * xpm3_signature = "/* XPM */";
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int width, height;
|
|
Packit |
78deda |
unsigned int nColors;
|
|
Packit |
78deda |
unsigned int charsPerPixel;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Read the XPM signature comment */
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
if (!strneq(line, xpm3_signature, strlen(xpm3_signature)))
|
|
Packit |
78deda |
pm_error("Apparent XPM 3 file does not start with '/* XPM */'. "
|
|
Packit |
78deda |
"First line is '%s'", xpm3_signature);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Read the assignment line */
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
if (!strneq(line, "static char", 11))
|
|
Packit |
78deda |
pm_error("Cannot find data structure declaration. Expected a "
|
|
Packit |
78deda |
"line starting with 'static char', but found the line "
|
|
Packit |
78deda |
"'%s'.", line);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Skip the comment block, if one starts here */
|
|
Packit |
78deda |
if (strneq(line, "/*", 2)) {
|
|
Packit |
78deda |
while (!strstr(line, "*/"))
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Parse the hints line */
|
|
Packit |
78deda |
if (sscanf(line, "\"%u %u %u %u\",", &width, &height,
|
|
Packit |
78deda |
&nColors, &charsPerPixel) != 4)
|
|
Packit |
78deda |
pm_error("error scanning hints line");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (verbose) {
|
|
Packit |
78deda |
pm_message("Width x Height: %u x %u", width, height);
|
|
Packit |
78deda |
pm_message("no. of colors: %u", nColors);
|
|
Packit |
78deda |
pm_message("chars per pixel: %u", charsPerPixel);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
readV3ColorTable(ifP, colorNameHashPP, nColors, charsPerPixel);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
*widthP = width;
|
|
Packit |
78deda |
*heightP = height;
|
|
Packit |
78deda |
*charsPerPixelP = charsPerPixel;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
readV1ColorTable(FILE * const ifP,
|
|
Packit |
78deda |
ColorNameHash ** const colorNameHashPP,
|
|
Packit |
78deda |
unsigned int const nColors,
|
|
Packit |
78deda |
unsigned int const charsPerPixel) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the color table from the XPM Version 1 header.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Assume *ifP is positioned to the color table; leave it positioned after.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (i = 0; i < nColors; ++i) {
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
char str1[MAX_LINE+1];
|
|
Packit |
78deda |
char str2[MAX_LINE+1];
|
|
Packit |
78deda |
char * t1;
|
|
Packit |
78deda |
char * t2;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if ((t1 = strchr(line, '"')) == NULL)
|
|
Packit |
78deda |
pm_error("D error scanning color table");
|
|
Packit |
78deda |
if ((t2 = strchr(t1 + 1, '"')) == NULL)
|
|
Packit |
78deda |
pm_error("E error scanning color table");
|
|
Packit |
78deda |
if (t2 - t1 - 1 != charsPerPixel)
|
|
Packit |
78deda |
pm_error("wrong number of chars per pixel in color table");
|
|
Packit |
78deda |
strncpy(str1, t1 + 1, t2 - t1 - 1);
|
|
Packit |
78deda |
str1[t2 - t1 - 1] = '\0';
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if ((t1 = strchr(t2 + 1, '"')) == NULL)
|
|
Packit |
78deda |
pm_error("F error scanning color table");
|
|
Packit |
78deda |
if ((t2 = strchr(t1 + 1, '"')) == NULL)
|
|
Packit |
78deda |
pm_error("G error scanning color table");
|
|
Packit |
78deda |
strncpy(str2, t1 + 1, t2 - t1 - 1);
|
|
Packit |
78deda |
str2[t2 - t1 - 1] = '\0';
|
|
Packit |
78deda |
|
|
Packit |
78deda |
addToColorMap(colorNameHashP, str1, str2, false);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
*colorNameHashPP = colorNameHashP;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
readXpm1Header(FILE * const ifP,
|
|
Packit |
78deda |
unsigned int * const widthP,
|
|
Packit |
78deda |
unsigned int * const heightP,
|
|
Packit |
78deda |
unsigned int * const charsPerPixelP,
|
|
Packit |
78deda |
ColorNameHash ** const colorNameHashPP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the header of the XPM file on stream *ifP. Assume the
|
|
Packit |
78deda |
getLine() stream is presently positioned to the beginning of the
|
|
Packit |
78deda |
file and it is a Version 1 XPM file. Leave the stream positioned
|
|
Packit |
78deda |
after the header.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Return the information from the header the same as for readXpm3Header.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
int format, v;
|
|
Packit |
78deda |
bool processedStaticChar;
|
|
Packit |
78deda |
/* We have read up to and interpreted the "static char..." line */
|
|
Packit |
78deda |
char * t1;
|
|
Packit |
78deda |
unsigned int nColors;
|
|
Packit |
78deda |
bool gotPixel, gotNColors, gotWidth, gotHeight, gotFormat;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
gotNColors = false;
|
|
Packit |
78deda |
gotWidth = false;
|
|
Packit |
78deda |
gotHeight = false;
|
|
Packit |
78deda |
gotFormat = false;
|
|
Packit |
78deda |
gotPixel = false;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Read the initial defines. */
|
|
Packit |
78deda |
processedStaticChar = FALSE;
|
|
Packit |
78deda |
while (!processedStaticChar) {
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
char str1[MAX_LINE+1];
|
|
Packit |
78deda |
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (sscanf(line, "#define %s %d", str1, &v) == 2) {
|
|
Packit |
78deda |
if ((t1 = strrchr(str1, '_')) == NULL)
|
|
Packit |
78deda |
t1 = str1;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
++t1;
|
|
Packit |
78deda |
if (streq(t1, "format")) {
|
|
Packit |
78deda |
gotFormat = true;
|
|
Packit |
78deda |
format = v;
|
|
Packit |
78deda |
} else if (streq(t1, "width")) {
|
|
Packit |
78deda |
gotWidth = true;
|
|
Packit |
78deda |
*widthP = v;
|
|
Packit |
78deda |
} else if (streq(t1, "height")) {
|
|
Packit |
78deda |
gotHeight = true;
|
|
Packit |
78deda |
*heightP = v;
|
|
Packit |
78deda |
} else if (streq(t1, "nColors")) {
|
|
Packit |
78deda |
gotNColors = true;
|
|
Packit |
78deda |
nColors = v;
|
|
Packit |
78deda |
} else if (streq(t1, "pixel")) {
|
|
Packit |
78deda |
gotPixel = TRUE;
|
|
Packit |
78deda |
*charsPerPixelP = v;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
} else if (strneq(line, "static char", 11)) {
|
|
Packit |
78deda |
if ((t1 = strrchr(line, '_')) == NULL)
|
|
Packit |
78deda |
t1 = line;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
++t1;
|
|
Packit |
78deda |
processedStaticChar = TRUE;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
/* File is positioned to "static char" line, which is in line[] and
|
|
Packit |
78deda |
t1 points to position of last "_" in the line, or the beginning of
|
|
Packit |
78deda |
the line if there is no "_"
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
if (!gotPixel)
|
|
Packit |
78deda |
pm_error("No 'pixel' value (characters per pixel)");
|
|
Packit |
78deda |
if (!gotFormat)
|
|
Packit |
78deda |
pm_error("missing or invalid format");
|
|
Packit |
78deda |
if (format != 1)
|
|
Packit |
78deda |
pm_error("can't handle XPM version %d", format);
|
|
Packit |
78deda |
if (!gotWidth)
|
|
Packit |
78deda |
pm_error("missing or invalid width");
|
|
Packit |
78deda |
if (!gotHeight)
|
|
Packit |
78deda |
pm_error("missing or invalid height");
|
|
Packit |
78deda |
if (!gotNColors)
|
|
Packit |
78deda |
pm_error("missing or invalid nColors");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (*charsPerPixelP > 2)
|
|
Packit |
78deda |
pm_message("WARNING: > 2 characters per pixel uses a lot of memory");
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* If there's a monochrome color table, skip it. */
|
|
Packit |
78deda |
if (strneq(t1, "mono", 4)) {
|
|
Packit |
78deda |
for (;;) {
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
if (strneq(line, "static char", 11))
|
|
Packit |
78deda |
break;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
readV1ColorTable(ifP, colorNameHashPP, nColors, *charsPerPixelP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Position to first line of raster (which is the line after
|
|
Packit |
78deda |
"static char ...").
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
for (;;) {
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
if (strneq(line, "static char", 11))
|
|
Packit |
78deda |
break;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
validateRasterPixel(const char * const pixelChars,
|
|
Packit |
78deda |
unsigned int const charsPerPixel) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (i = 0; i < charsPerPixel; ++i) {
|
|
Packit |
78deda |
if (pixelChars[i] == '\0')
|
|
Packit |
78deda |
pm_error("XPM input file ends in the middle of a string "
|
|
Packit |
78deda |
"that represents a raster line");
|
|
Packit |
78deda |
else if (pixelChars[i] == '"')
|
|
Packit |
78deda |
pm_error("A string that represents a raster line in the "
|
|
Packit |
78deda |
"XPM input file is too short to contain all the "
|
|
Packit |
78deda |
"pixels (%u characters each)",
|
|
Packit |
78deda |
charsPerPixel);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
convertRow(char const line[],
|
|
Packit |
78deda |
unsigned int const width,
|
|
Packit |
78deda |
unsigned int const charsPerPixel,
|
|
Packit |
78deda |
const ColorNameHash * const colorNameHashP,
|
|
Packit |
78deda |
pixel * const pixrow,
|
|
Packit |
78deda |
bit * const alpharow) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Convert one row from XPM input, which describes one raster line of the
|
|
Packit |
78deda |
image, to PPM. The XPM line is in 'line', and its format is 'width' pixel,
|
|
Packit |
78deda |
'charsPerPixel' characters per pixel. *colorNameHashP is the color table
|
|
Packit |
78deda |
that applies to the line.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Put the PPM pixels in 'pixrow'.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Also produce PBM row 'alpharow' with the transparency information from the
|
|
Packit |
78deda |
row.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
If the line doesn't start with a quote (e.g. it is empty), we issue
|
|
Packit |
78deda |
a warning and just treat the line as one that describes no pixels.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Abort program if there aren't exactly 'width' pixels in the line.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
const char * lineCursor;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
lineCursor = strchr(line, '"'); /* position to 1st quote in line */
|
|
Packit |
78deda |
if (lineCursor == NULL) {
|
|
Packit |
78deda |
/* We've seen a purported XPM that had a blank line in it. Just
|
|
Packit |
78deda |
ignoring it was the right thing to do. 05.05.27.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
pm_message("WARNING: No opening quotation mark in XPM input "
|
|
Packit |
78deda |
"line which is supposed to be a line of raster data: "
|
|
Packit |
78deda |
"'%s'. Ignoring this line.", line);
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
unsigned int col;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
++lineCursor; /* Skip to first character after quote */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Handle pixels until a close quote, eol, or we've returned all
|
|
Packit |
78deda |
the pixels Caller wants.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
for (col = 0; col < width; ++col) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
validateRasterPixel(lineCursor, charsPerPixel);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pixrow[col] = hash_color(colorNameHashP, lineCursor);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
alpharow[col] = hash_isTransparent(colorNameHashP, lineCursor) ?
|
|
Packit |
78deda |
PBM_BLACK : PBM_WHITE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
lineCursor += charsPerPixel;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (*lineCursor != '"')
|
|
Packit |
78deda |
pm_error("A raster line continues past width of image");
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
convertRaster(FILE * const ifP,
|
|
Packit |
78deda |
unsigned int const cols,
|
|
Packit |
78deda |
unsigned int const rows,
|
|
Packit |
78deda |
unsigned int const charsPerPixel,
|
|
Packit |
78deda |
const ColorNameHash * const colorNameHashP,
|
|
Packit |
78deda |
FILE * const imageOutFileP,
|
|
Packit |
78deda |
FILE * const alphaOutFileP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the XPM raster from *ifP and write the PPM raster to *imageOutFileP
|
|
Packit |
78deda |
and the alpha channel to *alphaOutFileP (where those are, respectively,
|
|
Packit |
78deda |
non-null).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
The dimensions are 'cols' by 'rows' and the color map for the XPM
|
|
Packit |
78deda |
raster is *colorNameHashP.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
pixel * pixrow;
|
|
Packit |
78deda |
bit * alpharow;
|
|
Packit |
78deda |
unsigned int row;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pixrow = ppm_allocrow(cols);
|
|
Packit |
78deda |
alpharow = pbm_allocrow(cols);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (row = 0; row < rows; ++row) {
|
|
Packit |
78deda |
bool haveLine;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (haveLine = false; !haveLine; ) {
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (strneq(line, "/*", 2)) {
|
|
Packit |
78deda |
/* It's a comment. Ignore it. */
|
|
Packit |
78deda |
} else
|
|
Packit |
78deda |
haveLine = true;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
convertRow(line, cols, charsPerPixel, colorNameHashP,
|
|
Packit |
78deda |
pixrow, alpharow);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (imageOutFileP)
|
|
Packit |
78deda |
ppm_writeppmrow(imageOutFileP,
|
|
Packit |
78deda |
pixrow, cols, PPM_MAXMAXVAL, 0);
|
|
Packit Service |
2370ca |
if (alphaOutFileP)
|
|
Packit Service |
2370ca |
pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pbm_freerow(alpharow);
|
|
Packit |
78deda |
ppm_freerow(pixrow);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
readXpmHeader(FILE * const ifP,
|
|
Packit |
78deda |
unsigned int * const widthP,
|
|
Packit |
78deda |
unsigned int * const heightP,
|
|
Packit |
78deda |
unsigned int * const charsPerPixelP,
|
|
Packit |
78deda |
ColorNameHash ** const colorNameHashPP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Read the XPM header, including color map.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
In the colormap, put black for the transparent color, if the XPM image
|
|
Packit |
78deda |
contains one.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
char line[MAX_LINE+1];
|
|
Packit |
78deda |
char str1[MAX_LINE+1];
|
|
Packit |
78deda |
int rc;
|
|
Packit |
78deda |
unsigned int charsPerPixel;
|
|
Packit |
78deda |
unsigned int width, height;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
backup = FALSE;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Read the header line */
|
|
Packit |
78deda |
getLine(line, sizeof(line), ifP);
|
|
Packit |
78deda |
backup = TRUE; /* back up so next read reads this line again */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
rc = sscanf(line, "/* %s */", str1);
|
|
Packit |
78deda |
if (rc == 1 && strneq(str1, "XPM", 3)) {
|
|
Packit |
78deda |
/* It's an XPM version 3 file */
|
|
Packit |
78deda |
readXpm3Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
|
|
Packit |
78deda |
} else {
|
|
Packit |
78deda |
/* Assume it's an XPM version 1 file */
|
|
Packit |
78deda |
readXpm1Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
*widthP = width;
|
|
Packit |
78deda |
*heightP = height;
|
|
Packit |
78deda |
*charsPerPixelP = charsPerPixel;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
main(int argc, char *argv[]) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
FILE * ifP;
|
|
Packit |
78deda |
FILE * alphaOutFileP;
|
|
Packit |
78deda |
FILE * imageOutFileP;
|
|
Packit |
78deda |
unsigned int cols, rows;
|
|
Packit |
78deda |
unsigned int charsPerPixel;
|
|
Packit |
78deda |
ColorNameHash * colorNameHashP;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct cmdlineInfo cmdline;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ppm_init(&argc, argv);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
parseCommandLine(argc, argv, &cmdline);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
verbose = cmdline.verbose;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if ( cmdline.input_filespec != NULL )
|
|
Packit |
78deda |
ifP = pm_openr( cmdline.input_filespec);
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
ifP = stdin;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdline.alpha_stdout)
|
|
Packit |
78deda |
alphaOutFileP = stdout;
|
|
Packit |
78deda |
else if (cmdline.alpha_filename == NULL)
|
|
Packit |
78deda |
alphaOutFileP = NULL;
|
|
Packit |
78deda |
else {
|
|
Packit |
78deda |
alphaOutFileP = pm_openw(cmdline.alpha_filename);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdline.alpha_stdout)
|
|
Packit |
78deda |
imageOutFileP = NULL;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
imageOutFileP = stdout;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
readXpmHeader(ifP, &cols, &rows, &charsPerPixel, &colorNameHashP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (imageOutFileP)
|
|
Packit |
78deda |
ppm_writeppminit(imageOutFileP, cols, rows, PPM_MAXMAXVAL, 0);
|
|
Packit |
78deda |
if (alphaOutFileP)
|
|
Packit |
78deda |
pbm_writepbminit(alphaOutFileP, cols, rows, 0);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
convertRaster(ifP, cols, rows, charsPerPixel, colorNameHashP,
|
|
Packit |
78deda |
imageOutFileP, alphaOutFileP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_close(ifP);
|
|
Packit |
78deda |
if (imageOutFileP)
|
|
Packit |
78deda |
pm_close(imageOutFileP);
|
|
Packit |
78deda |
if (alphaOutFileP)
|
|
Packit |
78deda |
pm_close(alphaOutFileP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
hash_destroy(colorNameHashP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/*
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Copyright (C) 1991 by Jef Poskanzer.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Permission to use, copy, modify, and distribute this software and its
|
|
Packit |
78deda |
** documentation for any purpose and without fee is hereby granted, provided
|
|
Packit |
78deda |
** that the above copyright notice appear in all copies and that both that
|
|
Packit |
78deda |
** copyright notice and this permission notice appear in supporting
|
|
Packit |
78deda |
** documentation. This software is provided "as is" without express or
|
|
Packit |
78deda |
** implied warranty.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Upgraded to handle XPM version 3 by
|
|
Packit |
78deda |
** Arnaud Le Hors (lehors@mirsa.inria.fr)
|
|
Packit |
78deda |
** Tue Apr 9 1991
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
|
|
Packit |
78deda |
** - Bug fix, no advance of read ptr, would not read
|
|
Packit |
78deda |
** colors like "ac c black" because it would find
|
|
Packit |
78deda |
** the "c" of "ac" and then had problems with "c"
|
|
Packit |
78deda |
** as color.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** - Now understands multiword X11 color names
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** - Now reads multiple color keys. Takes the color
|
|
Packit |
78deda |
** of the hightest available key. Lines no longer need
|
|
Packit |
78deda |
** to begin with key 'c'.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** - expanded line buffer to from 500 to 2048 for bigger files
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|