diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2016-03-27 01:46:26 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2016-03-27 01:46:26 +0000 |
commit | dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa (patch) | |
tree | b147568ccffc4cdba9e2a98de1452450ba8e55c3 /converter/ppm/xpmtoppm.c | |
parent | 4ce684c4978610d1ea42be1b00f7332f3f5f337a (diff) | |
download | netpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.tar.gz netpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.tar.xz netpbm-mirror-dff6b9fdfeb78fe21a66aa459ddc1d5f7e362dfa.zip |
Promote Advanced (10.73) to Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@2692 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm/xpmtoppm.c')
-rw-r--r-- | converter/ppm/xpmtoppm.c | 1149 |
1 files changed, 698 insertions, 451 deletions
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c index 235c3867..27f17931 100644 --- a/converter/ppm/xpmtoppm.c +++ b/converter/ppm/xpmtoppm.c @@ -1,43 +1,19 @@ -/* xpmtoppm.c - read an X11 pixmap file and produce a portable pixmap -** -** Copyright (C) 1991 by Jef Poskanzer. -** -** 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. -** -** Upgraded to handle 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, no advance of read ptr, would not read -** colors like "ac c black" because it would find -** the "c" of "ac" and then had problems with "c" -** as color. -** -** - Now understands multiword X11 color names -** -** - Now reads multiple color keys. Takes the color -** of the hightest available key. Lines no longer need -** to begin with key 'c'. -** -** - expanded line buffer to from 500 to 2048 for bigger files +/* xpmtoppm.c - convert XPM file (X11 pixmap) to PPM + + Copyright and history information is at end of file */ #define _BSD_SOURCE /* Make sure strdup() is in string.h */ #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ +#include <assert.h> #include <string.h> #include "pm_c_util.h" -#include "ppm.h" +#include "mallocvar.h" #include "shhopt.h" #include "nstring.h" -#include "mallocvar.h" +#include "ppm.h" #define MAX_LINE (8 * 1024) /* The maximum size XPM input line we can handle. */ @@ -47,72 +23,321 @@ const char *xpmColorKeys[] = { - "s", /* key #1: symbol */ - "m", /* key #2: mono visual */ - "g4", /* key #3: 4 grays visual */ - "g", /* key #4: gray visual */ - "c", /* key #5: color visual */ + "s", /* key #1: symbol */ + "m", /* key #2: mono visual */ + "g4", /* key #3: 4 grays visual */ + "g", /* key #4: gray visual */ + "c", /* key #5: color visual */ }; -struct cmdline_info { +struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - char *input_filespec; /* Filespecs of input files */ - char *alpha_filename; + const char * input_filespec; /* Filespecs of input files */ + const char * alpha_filename; int alpha_stdout; int verbose; }; -static int verbose; +static bool verbose; + static void -parse_command_line(int argc, char ** argv, - struct cmdline_info *cmdline_p) { +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ - optStruct *option_def = malloc(100*sizeof(optStruct)); + optEntry * option_def; /* Instructions to OptParseOptions2 on how to parse our options. */ - optStruct2 opt; + optStruct3 opt; unsigned int option_def_index; - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENTRY(0, "alphaout", OPT_STRING, &cmdline_p->alpha_filename, 0); - OPTENTRY(0, "verbose", OPT_FLAG, &cmdline_p->verbose, 0); + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename, + NULL, 0); + OPTENT3(0, "verbose", OPT_FLAG, &cmdlineP->verbose, + NULL, 0); - cmdline_p->alpha_filename = NULL; - cmdline_p->verbose = FALSE; + cmdlineP->alpha_filename = NULL; + cmdlineP->verbose = FALSE; opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */ - optParseOptions2(&argc, argv, opt, 0); - /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (argc - 1 == 0) - cmdline_p->input_filespec = NULL; /* he wants stdin */ + cmdlineP->input_filespec = NULL; /* he wants stdin */ else if (argc - 1 == 1) - cmdline_p->input_filespec = strdup(argv[1]); + cmdlineP->input_filespec = strdup(argv[1]); else pm_error("Too many arguments. The only argument accepted\n" "is the input file specification"); - if (cmdline_p->alpha_filename && - streq(cmdline_p->alpha_filename, "-")) - cmdline_p->alpha_stdout = TRUE; + if (cmdlineP->alpha_filename && + streq(cmdlineP->alpha_filename, "-")) + cmdlineP->alpha_stdout = TRUE; else - cmdline_p->alpha_stdout = FALSE; + cmdlineP->alpha_stdout = FALSE; + +} + + + +struct ColorNameHashTableEntry { +/*---------------------------------------------------------------------------- + An entry in the color name hash table. It maps a color name to a + color, or is empty. +-----------------------------------------------------------------------------*/ + bool empty; + char colorName[3]; + /* Actual length 0-3. NOT NUL-terminated */ + pixel color; +}; + + + +typedef struct { +/*---------------------------------------------------------------------------- + This is a color map which is primarily a hash table that maps an XPM + color name to a color. An XPM color name is a 0-3 character name that + appears in the raster of an XPM image to uniquely identify a color. + The header of the XPM contains a listing of all the color names that + appear in the raster, identifying a color for each. + + We represent a color as a 'pixel'. +-----------------------------------------------------------------------------*/ + unsigned int nameSize; + /* Size of color names in this hash. 0-3 */ + struct ColorNameHashTableEntry * transparentP; + /* The element of 'table' that is for the transparent color. + NULL if there is none. + */ + + /* This is an internally chained hash table, i.e. there are no linked + lists. You use the hash function to get an index into the hash table. + If the entry indexed by that is not for the color name you're looking + for, you look at the next entry down, and keep going down until you + either find the color name you're looking for or hit an empty entry. + + So that we never run out of space for new color names, we make the + creator of the hash table tell us the maximum number of colors there + will be. We allocate twice that size in order to reduce average hash + chain length. + */ + unsigned int size; + struct ColorNameHashTableEntry * table; +} ColorNameHash; + + + +static ColorNameHash * +hash_create(unsigned int const nColors, + unsigned int const nameSize) { + + ColorNameHash * hashP; + + MALLOCVAR_NOFAIL(hashP); + + hashP->nameSize = nameSize; + + hashP->size = nColors * 2; + + MALLOCARRAY(hashP->table, hashP->size); + + if (!hashP->table) + pm_error("Failed to allocate memory for a %u-entry " + "color name hash table.", hashP->size); + + { + unsigned int i; + for (i = 0; i < hashP->size; ++i) + hashP->table[i].empty = true; + } + + hashP->transparentP = NULL; + + return hashP; +} + + + +static void +hash_destroy(ColorNameHash * const hashP) { + + free(hashP->table); + + free(hashP); +} + + + +static unsigned int +hashColorName(const char * const name, + unsigned int const size, + unsigned int const hashTableSize) { +/*---------------------------------------------------------------------------- + Return the hash value (initial index into the color name hash table) + for the color name 'name', which is 'size' characters long. The hash + is to be in the range [0, hashTableSize). +-----------------------------------------------------------------------------*/ + /* I have no idea if this is an appropriate hash function. I borrowed + it from pnm_hashTuple() + */ + + unsigned int const hash_factor[] = {1, 33, 33*33}; + + unsigned int i; + unsigned int hash; + hash = 0; /* initial value */ + for (i = 0; i < size; ++i) { + hash += name[i] * hash_factor[i]; + } + hash %= hashTableSize; + return hash; +} + + + +static bool +entryMatch(struct ColorNameHashTableEntry const entry, + const char * const name, + unsigned int const size) { + + if (entry.empty) + return true; + + assert(size <= ARRAY_SIZE(entry.colorName)); + + { + unsigned int i; + + for (i = 0; i < size; ++i) { + if (name[i] != entry.colorName[i]) + return false; + } + } + + return true; +} + + + +static void +bumpIndex(unsigned int * const indexP, + unsigned int const tableSize, + unsigned int const limit) { +/*---------------------------------------------------------------------------- + Bump *indexP to the next entry in a table of size 'tableSize', in a + circular fashion. But abort the program if this would take us to + 'limit'. +-----------------------------------------------------------------------------*/ + *indexP += 1; + if (*indexP >= tableSize) + *indexP = 0; + + if (*indexP == limit) + pm_error("INTERNAL ERROR: color name hash table is full"); +} + + + +static void +hash_find(const ColorNameHash * const hashP, + const char * const name, + struct ColorNameHashTableEntry ** const entryPP) { +/*---------------------------------------------------------------------------- + Find the entry in the color hash table *hashP for the color + named 'name' in the lexicon of this XPM file. If the color is in the + table, this is where it is. If it isn't, this is where it should go. +-----------------------------------------------------------------------------*/ + unsigned int const initialIndex = + hashColorName(name, hashP->nameSize, hashP->size); + + unsigned int i; + + for (i = initialIndex; + !entryMatch(hashP->table[i], name, hashP->nameSize); + bumpIndex(&i, hashP->size, initialIndex)); + + *entryPP = &hashP->table[i]; } + +static void +hash_add(ColorNameHash * const hashP, + const char * const name, + pixel const color, + bool const isTransparent) { + + struct ColorNameHashTableEntry * entryP; + + hash_find(hashP, name, &entryP); + + if (!entryP->empty) + pm_error("Color name appears multiple times in color map"); + + entryP->empty = false; + { + unsigned int i; + for (i = 0; i < hashP->nameSize; ++i) + entryP->colorName[i] = name[i]; + } + entryP->color = color; + + if (isTransparent) { + if (hashP->transparentP) + pm_error("There are multiple NONE (transparent) entries in " + "the XPM color map"); + else + hashP->transparentP = entryP; + } +} + + + +static pixel +hash_color(const ColorNameHash * const hashP, + const char * const name) { + + struct ColorNameHashTableEntry * entryP; + + hash_find(hashP, name, &entryP); + + if (entryP->empty) + pm_error("Color name in raster is not in color map"); + + return entryP->color; +} + + + +static bool +hash_isTransparent(const ColorNameHash * const hashP, + const char * const name) { + + struct ColorNameHashTableEntry * entryP; + + hash_find(hashP, name, &entryP); + + return (entryP == hashP->transparentP); +} + + + static char lastInputLine[MAX_LINE+1]; /* contents of line most recently read from input */ static bool backup; @@ -121,6 +346,7 @@ static bool backup; */ + static void getLine(char * const line, size_t const size, @@ -158,21 +384,6 @@ getLine(char * const line, -static unsigned int -getNumber(char * const p, unsigned int const size) { - - unsigned int retval; - unsigned char * q; - - retval = 0; - for (q = p; q < p+size; ++q) - retval = (retval << 8) + *q; - - return retval; -} - - - static void getword(char * const output, char ** const cursorP) { @@ -191,90 +402,97 @@ getword(char * const output, char ** const cursorP) { static void -addToColorMap(unsigned int const seqNum, - unsigned int const colorNumber, - pixel * const colors, int * const ptab, - char colorspec[], int const isTransparent, - int * const transparentP) { +addToColorMap(ColorNameHash * const hashP, + const char * const colorName, + char const colorspec[], + bool const isTransparent) { /*---------------------------------------------------------------------------- - Add the color named by colorspec[] to the colormap contained in - 'colors' and 'ptab', as the color associated with XPM color number - 'colorNumber', which is the seqNum'th color in the XPM color map. + Add the color named by colorspec[] to the colormap represented by *hashP, + as the color associated with XPM color name 'colorNumber'. - Iff 'transparent', set *transparentP to the colormap index that - corresponds to this color. + Note that *hashP determines how long 'colorName' is. -----------------------------------------------------------------------------*/ - if (ptab == NULL) { - /* Index into table. */ - colors[colorNumber] = ppm_parsecolor(colorspec, - (pixval) PPM_MAXMAXVAL); - if (isTransparent) - *transparentP = colorNumber; - } else { - /* Set up linear search table. */ - colors[seqNum] = ppm_parsecolor(colorspec, - (pixval) PPM_MAXMAXVAL); - ptab[seqNum] = colorNumber; - if (isTransparent) - *transparentP = seqNum; + hash_add(hashP, colorName, ppm_parsecolor(colorspec, PPM_MAXMAXVAL), + isTransparent); +} + + + +static void +validateColorName(const char * const name, + unsigned int const charsPerPixel) { + + unsigned int i; + + for (i = 0; i < charsPerPixel; ++i) { + if (name[i] == '"') + pm_error("A color map entry ends in the middle of the colormap " + "index"); + else if (name[i] == '\0') + pm_error("The XPM file ends in the middle of a color map entry"); } } static void -interpretXpm3ColorTableLine(char line[], int const seqNum, - int const chars_per_pixel, - pixel * const colors, int * const ptab, - int * const transparentP) { +interpretXpm3ColorTableLine(char const line[], + unsigned int const seqNum, + unsigned int const charsPerPixel, + ColorNameHash * const hashP) { /*---------------------------------------------------------------------------- - Interpret one line of the color table in the XPM header. 'line' is - the line from the XPM file. It is the seqNum'th color table entry in - the file. The file uses 'chars_per_pixel' characters per pixel. + Interpret one line of the color table in the XPM header. 'line' is the + line from the XPM file. It is the seqNum'th color table entry in the file. + The raster in the file uses 'charsPerPixel' characters per pixel (i.e. + a an XPM color name is 'charsPerPixel' characters). - Add the information from this color table entry to the color table - 'colors' and, if it isn't NULL, the corresponding lookup shadow table - 'ptab' (see readXpm3ColorTable for a description of these data - structures). + Add the information from this color table entry to the color name hash + *hashP. The line may include values for multiple kinds of color (grayscale, color, etc.). We take the highest of these (e.g. color over grayscale). If a color table entry indicates transparency, set *transparentP - to the colormap index that corresponds to the indicated color. + to indicate the XPM color name. -----------------------------------------------------------------------------*/ /* Note: this code seems to allow for multi-word color specifications, but I'm not aware that such are legal. Ultimately, ppm_parsecolor() - interprets the name, and I believe it only takes single word + interprets the name, and I believe it takes only single word color specifications. -Bryan 2001.05.06. */ char str2[MAX_LINE+1]; - char *t1; - char *t2; + char * t1; + char * t2; int endOfEntry; /* boolean */ - unsigned int curkey, key, highkey; /* current color key */ - unsigned int lastwaskey; + unsigned int curkey, key, highkey; /* current color key */ + bool lastwaskey; /* The last token we processes was a key, and we have processed at least one token. */ - char curbuf[BUFSIZ]; /* current buffer */ - int isTransparent; + char curbuf[BUFSIZ]; /* current buffer */ + bool isTransparent; - int colorNumber; - /* A color number that appears in the raster */ + const char * colorName; + /* The 0-3 character name this color map line gives the color + (i.e. the name that the raster uses). This is NOT NUL-terminated. + It's length is bytesPerPixel. + */ + /* read the chars */ t1 = strchr(line, '"'); if (t1 == NULL) pm_error("A line that is supposed to be an entry in the color " "table does not start with a quote. The line is '%s'. " - "It is the %dth entry in the color table.", + "It is the %uth entry in the color table.", line, seqNum); else - t1++; /* Points now to first color number character */ + ++t1; /* Points now to first color number character */ + + validateColorName(t1, charsPerPixel); + colorName = t1; - colorNumber = getNumber(t1, chars_per_pixel); - t1 += chars_per_pixel; + t1 += charsPerPixel; /* * read color keys and values @@ -306,30 +524,30 @@ interpretXpm3ColorTableLine(char line[], int const seqNum, pm_error("Missing color key token in color table line " "'%s' before '%s'.", line, str2); if (!lastwaskey) - strcat(curbuf, " "); /* append space */ - if ( (strncmp(str2, "None", 4) == 0) - || (strncmp(str2, "none", 4) == 0) ) { + strcat(curbuf, " "); /* append space */ + if ( (strneq(str2, "None", 4)) + || (strneq(str2, "none", 4)) ) { /* This entry identifies the transparent color number */ strcat(curbuf, "#000000"); /* Make it black */ isTransparent = TRUE; } else - strcat(curbuf, str2); /* append buf */ - lastwaskey = 0; + strcat(curbuf, str2); /* append buf */ + lastwaskey = FALSE; } else { /* This word is a key. So we've seen the last of the info for the previous key, and we must either put it in the color map or ignore it if we already have a higher color form in the colormap for this colormap entry. */ - if (curkey > highkey) { /* flush string */ - addToColorMap(seqNum, colorNumber, colors, ptab, curbuf, - isTransparent, transparentP); + if (curkey > highkey) { /* flush string */ + addToColorMap(hashP, colorName, curbuf, isTransparent); highkey = curkey; } - curkey = key; /* set new key */ - curbuf[0] = '\0'; /* reset curbuf */ + /* intialize state to process this new key */ + curkey = key; + curbuf[0] = '\0'; isTransparent = FALSE; - lastwaskey = 1; + lastwaskey = TRUE; } if (*t2 == '"') break; } @@ -339,8 +557,7 @@ interpretXpm3ColorTableLine(char line[], int const seqNum, entry in it) */ if (curkey > highkey) { - addToColorMap(seqNum, colorNumber, colors, ptab, curbuf, - isTransparent, transparentP); + addToColorMap(hashP, colorName, curbuf, isTransparent); highkey = curkey; } if (highkey == 1) @@ -350,152 +567,208 @@ interpretXpm3ColorTableLine(char line[], int const seqNum, static void -readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, - int * const chars_per_pixelP, int * const ncolorsP, - pixel ** const colorsP, int ** const ptabP, - int * const transparentP) { +readV3ColorTable(FILE * const ifP, + ColorNameHash ** const colorNameHashPP, + unsigned int const nColors, + unsigned int const charsPerPixel) { /*---------------------------------------------------------------------------- - Read the header of the XPM file on stream 'stream'. Assume the + Read the color table from the XPM Version 3 header. + + Assume *ifP is positioned to the color table; leave it positioned after. +-----------------------------------------------------------------------------*/ + ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel); + + unsigned int seqNum; + /* Sequence number of entry within color table in XPM header */ + + for (seqNum = 0; seqNum < nColors; ++seqNum) { + char line[MAX_LINE+1]; + getLine(line, sizeof(line), ifP); + /* skip the comment line if any */ + if (strneq(line, "/*", 2)) + getLine(line, sizeof(line), ifP); + + interpretXpm3ColorTableLine(line, seqNum, charsPerPixel, + colorNameHashP); + + } + *colorNameHashPP = colorNameHashP; +} + + + +static void +readXpm3Header(FILE * const ifP, + unsigned int * const widthP, + unsigned int * const heightP, + unsigned int * const charsPerPixelP, + ColorNameHash ** const colorNameHashPP) { +/*---------------------------------------------------------------------------- + Read the header of the XPM file on stream *ifP. Assume the getLine() stream is presently positioned to the beginning of the file and it is a Version 3 XPM file. Leave the stream positioned after the header. - We have two ways to return the colormap, depending on the number of - characters per pixel in the XPM: - - If it is 1 or 2 characters per pixel, we return the colormap as a - Netpbm 'pixel' array *colorsP (in newly malloc'ed storage), such - that if a color in the raster is identified by index N, then - (*colorsP)[N] is that color. So this array is either 256 or 64K - pixels. In this case, we return *ptabP = NULL. - - If it is more than 2 characters per pixel, we return the colormap as - both a Netpbm 'pixel' array *colorsP and a lookup table *ptabP (both - in newly malloc'ed storage). - - If a color in the raster is identified by index N, then for some I, - (*ptabP)[I] is N and (*colorsP)[I] is the color in question. So - you iterate through *ptabP looking for N and then look at the - corresponding entry in *colorsP to get the color. - - Return as *transColorNumberP the value of the XPM color number that - represents a transparent pixel, or -1 if no color number does. + Return as *widthP and *heightP the dimensions of the image indicated + by the header. + + Return as *charsPerPixelP the number of characters the header says the + raster uses for each pixel, i.e. the XPM color name length. + + Return the color map as *colorNameHashPP. -----------------------------------------------------------------------------*/ char line[MAX_LINE+1]; const char * xpm3_signature = "/* XPM */"; - *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1; + unsigned int width, height; + unsigned int nColors; + unsigned int charsPerPixel; /* Read the XPM signature comment */ - getLine(line, sizeof(line), stream); - if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) + getLine(line, sizeof(line), ifP); + if (!strneq(line, xpm3_signature, strlen(xpm3_signature))) pm_error("Apparent XPM 3 file does not start with '/* XPM */'. " "First line is '%s'", xpm3_signature); /* Read the assignment line */ - getLine(line, sizeof(line), stream); - if (strncmp(line, "static char", 11) != 0) + getLine(line, sizeof(line), ifP); + if (!strneq(line, "static char", 11)) pm_error("Cannot find data structure declaration. Expected a " "line starting with 'static char', but found the line " "'%s'.", line); - /* Read the hints line */ - getLine(line, sizeof(line), stream); - /* skip the comment line if any */ - if (!strncmp(line, "/*", 2)) { + getLine(line, sizeof(line), ifP); + + /* Skip the comment block, if one starts here */ + if (strneq(line, "/*", 2)) { while (!strstr(line, "*/")) - getLine(line, sizeof(line), stream); - getLine(line, sizeof(line), stream); + getLine(line, sizeof(line), ifP); + getLine(line, sizeof(line), ifP); } - if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP, - ncolorsP, chars_per_pixelP) != 4) + + /* Parse the hints line */ + if (sscanf(line, "\"%u %u %u %u\",", &width, &height, + &nColors, &charsPerPixel) != 4) pm_error("error scanning hints line"); - if (verbose == 1) - { - pm_message("Width x Height: %d x %d", *widthP, *heightP); - pm_message("no. of colors: %d", *ncolorsP); - pm_message("chars per pixel: %d", *chars_per_pixelP); + if (verbose) { + pm_message("Width x Height: %u x %u", width, height); + pm_message("no. of colors: %u", nColors); + pm_message("chars per pixel: %u", charsPerPixel); } - /* Allocate space for color table. */ - if (*chars_per_pixelP <= 2) { - /* Set up direct index (see above) */ - *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256); - *ptabP = NULL; - } else { - /* Set up lookup table (see above) */ - *colorsP = ppm_allocrow(*ncolorsP); - MALLOCARRAY(*ptabP, *ncolorsP); - if (*ptabP == NULL) - pm_error("Unable to allocate memory for %d colors", *ncolorsP); - } - - { - /* Read the color table */ - int seqNum; - /* Sequence number of entry within color table in XPM header */ - - *transparentP = -1; /* initial value */ - - for (seqNum = 0; seqNum < *ncolorsP; seqNum++) { - getLine(line, sizeof(line), stream); - /* skip the comment line if any */ - if (!strncmp(line, "/*", 2)) - getLine(line, sizeof(line), stream); - - interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, - *colorsP, *ptabP, transparentP); - } + readV3ColorTable(ifP, colorNameHashPP, nColors, charsPerPixel); + + *widthP = width; + *heightP = height; + *charsPerPixelP = charsPerPixel; +} + + + +static void +readV1ColorTable(FILE * const ifP, + ColorNameHash ** const colorNameHashPP, + unsigned int const nColors, + unsigned int const charsPerPixel) { +/*---------------------------------------------------------------------------- + Read the color table from the XPM Version 1 header. + + Assume *ifP is positioned to the color table; leave it positioned after. +-----------------------------------------------------------------------------*/ + ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel); + + unsigned int i; + + for (i = 0; i < nColors; ++i) { + char line[MAX_LINE+1]; + char str1[MAX_LINE+1]; + char str2[MAX_LINE+1]; + char * t1; + char * t2; + + getLine(line, sizeof(line), ifP); + + if ((t1 = strchr(line, '"')) == NULL) + pm_error("D error scanning color table"); + if ((t2 = strchr(t1 + 1, '"')) == NULL) + pm_error("E error scanning color table"); + if (t2 - t1 - 1 != charsPerPixel) + pm_error("wrong number of chars per pixel in color table"); + strncpy(str1, t1 + 1, t2 - t1 - 1); + str1[t2 - t1 - 1] = '\0'; + + if ((t1 = strchr(t2 + 1, '"')) == NULL) + pm_error("F error scanning color table"); + if ((t2 = strchr(t1 + 1, '"')) == NULL) + pm_error("G error scanning color table"); + strncpy(str2, t1 + 1, t2 - t1 - 1); + str2[t2 - t1 - 1] = '\0'; + + addToColorMap(colorNameHashP, str1, str2, false); } + *colorNameHashPP = colorNameHashP; } + static void -readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, - int * const chars_per_pixelP, int * const ncolorsP, - pixel ** const colorsP, int ** const ptabP) { +readXpm1Header(FILE * const ifP, + unsigned int * const widthP, + unsigned int * const heightP, + unsigned int * const charsPerPixelP, + ColorNameHash ** const colorNameHashPP) { /*---------------------------------------------------------------------------- - Read the header of the XPM file on stream 'stream'. Assume the + Read the header of the XPM file on stream *ifP. Assume the getLine() stream is presently positioned to the beginning of the file and it is a Version 1 XPM file. Leave the stream positioned after the header. Return the information from the header the same as for readXpm3Header. -----------------------------------------------------------------------------*/ - char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1]; - char *t1; - char *t2; - int format; - unsigned int v; - int i, j; + int format, v; bool processedStaticChar; /* We have read up to and interpreted the "static char..." line */ + char * t1; + unsigned int nColors; + bool gotPixel, gotNColors, gotWidth, gotHeight, gotFormat; - *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1; + gotNColors = false; + gotWidth = false; + gotHeight = false; + gotFormat = false; + gotPixel = false; /* Read the initial defines. */ processedStaticChar = FALSE; while (!processedStaticChar) { - getLine(line, sizeof(line), stream); + char line[MAX_LINE+1]; + char str1[MAX_LINE+1]; + + getLine(line, sizeof(line), ifP); if (sscanf(line, "#define %s %d", str1, &v) == 2) { - char *t1; if ((t1 = strrchr(str1, '_')) == NULL) t1 = str1; else ++t1; - if (streq(t1, "format")) + if (streq(t1, "format")) { + gotFormat = true; format = v; - else if (streq(t1, "width")) + } else if (streq(t1, "width")) { + gotWidth = true; *widthP = v; - else if (streq(t1, "height")) + } else if (streq(t1, "height")) { + gotHeight = true; *heightP = v; - else if (streq(t1, "ncolors")) - *ncolorsP = v; - else if (streq(t1, "pixel")) - *chars_per_pixelP = v; - } else if (!strncmp(line, "static char", 11)) { + } else if (streq(t1, "nColors")) { + gotNColors = true; + nColors = v; + } else if (streq(t1, "pixel")) { + gotPixel = TRUE; + *charsPerPixelP = v; + } + } else if (strneq(line, "static char", 11)) { if ((t1 = strrchr(line, '_')) == NULL) t1 = line; else @@ -507,85 +780,40 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, t1 points to position of last "_" in the line, or the beginning of the line if there is no "_" */ - if (format == -1) + if (!gotPixel) + pm_error("No 'pixel' value (characters per pixel)"); + if (!gotFormat) pm_error("missing or invalid format"); if (format != 1) pm_error("can't handle XPM version %d", format); - if (*widthP == -1) + if (!gotWidth) pm_error("missing or invalid width"); - if (*heightP == -1) + if (!gotHeight) pm_error("missing or invalid height"); - if (*ncolorsP == -1) - pm_error("missing or invalid ncolors"); - if (*chars_per_pixelP == -1) - pm_error("missing or invalid *chars_per_pixelP"); - if (*chars_per_pixelP > 2) - pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory"); + if (!gotNColors) + pm_error("missing or invalid nColors"); + + if (*charsPerPixelP > 2) + pm_message("WARNING: > 2 characters per pixel uses a lot of memory"); /* If there's a monochrome color table, skip it. */ - if (!strncmp(t1, "mono", 4)) { + if (strneq(t1, "mono", 4)) { for (;;) { - getLine(line, sizeof(line), stream); - if (!strncmp(line, "static char", 11)) + char line[MAX_LINE+1]; + getLine(line, sizeof(line), ifP); + if (strneq(line, "static char", 11)) break; } } - /* Allocate space for color table. */ - if (*chars_per_pixelP <= 2) { - /* Up to two chars per pixel, we can use an indexed table. */ - v = 1; - for (i = 0; i < *chars_per_pixelP; ++i) - v *= 256; - *colorsP = ppm_allocrow(v); - *ptabP = NULL; - } else { - /* Over two chars per pixel, we fall back on linear search. */ - *colorsP = ppm_allocrow(*ncolorsP); - MALLOCARRAY(*ptabP, *ncolorsP); - if (*ptabP == NULL) - pm_error("Unable to allocate memory for %d colors", *ncolorsP); - } - - /* Read color table. */ - for (i = 0; i < *ncolorsP; ++i) { - getLine(line, sizeof(line), stream); + readV1ColorTable(ifP, colorNameHashPP, nColors, *charsPerPixelP); - if ((t1 = strchr(line, '"')) == NULL) - pm_error("D error scanning color table"); - if ((t2 = strchr(t1 + 1, '"')) == NULL) - pm_error("E error scanning color table"); - if (t2 - t1 - 1 != *chars_per_pixelP) - pm_error("wrong number of chars per pixel in color table"); - strncpy(str1, t1 + 1, t2 - t1 - 1); - str1[t2 - t1 - 1] = '\0'; - - if ((t1 = strchr(t2 + 1, '"')) == NULL) - pm_error("F error scanning color table"); - if ((t2 = strchr(t1 + 1, '"')) == NULL) - pm_error("G error scanning color table"); - strncpy(str2, t1 + 1, t2 - t1 - 1); - str2[t2 - t1 - 1] = '\0'; - - v = 0; - for (j = 0; j < *chars_per_pixelP; ++j) - v = (v << 8) + str1[j]; - if (*chars_per_pixelP <= 2) - /* Index into table. */ - (*colorsP)[v] = ppm_parsecolor(str2, - (pixval) PPM_MAXMAXVAL); - else { - /* Set up linear search table. */ - (*colorsP)[i] = ppm_parsecolor(str2, - (pixval) PPM_MAXMAXVAL); - (*ptabP)[i] = v; - } - } /* Position to first line of raster (which is the line after "static char ..."). */ for (;;) { - getLine(line, sizeof(line), stream); - if (strncmp(line, "static char", 11) == 0) + char line[MAX_LINE+1]; + getLine(line, sizeof(line), ifP); + if (strneq(line, "static char", 11)) break; } } @@ -593,29 +821,49 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, static void -interpretXpmLine(char const line[], - int const chars_per_pixel, - int const ncolors, - int * const ptab, - int ** const cursorP, - int * const maxCursor) { +validateRasterPixel(const char * const pixelChars, + unsigned int const charsPerPixel) { + + unsigned int i; + + for (i = 0; i < charsPerPixel; ++i) { + if (pixelChars[i] == '\0') + pm_error("XPM input file ends in the middle of a string " + "that represents a raster line"); + else if (pixelChars[i] == '"') + pm_error("A string that represents a raster line in the " + "XPM input file is too short to contain all the " + "pixels (%u characters each)", + charsPerPixel); + } +} + + + +static void +convertRow(char const line[], + unsigned int const width, + unsigned int const charsPerPixel, + const ColorNameHash * const colorNameHashP, + pixel * const pixrow, + bit * const alpharow) { /*---------------------------------------------------------------------------- - Interpret one line of XPM input. The line is in 'line', and its - format is 'chars_per_pixel' characters per pixel. 'ptab' is the - color table that applies to the line, which table has 'ncolors' - colors. + Convert one row from XPM input, which describes one raster line of the + image, to PPM. The XPM line is in 'line', and its format is 'width' pixel, + 'charsPerPixel' characters per pixel. *colorNameHashP is the color table + that applies to the line. + + Put the PPM pixels in 'pixrow'. - Put the colormap indexes for the pixels represented in 'line' at - *cursorP, lined up in the order they are in 'line', and return - *cursorP positioned just after the last one. + Also produce PBM row 'alpharow' with the transparency information from the + row. If the line doesn't start with a quote (e.g. it is empty), we issue a warning and just treat the line as one that describes no pixels. - Stop processing the line either at the end of the line or when - the output would go beyond maxCursor, whichever comes first. + Abort program if there aren't exactly 'width' pixels in the line. -----------------------------------------------------------------------------*/ - char * lineCursor; + const char * lineCursor; lineCursor = strchr(line, '"'); /* position to 1st quote in line */ if (lineCursor == NULL) { @@ -626,209 +874,208 @@ interpretXpmLine(char const line[], "line which is supposed to be a line of raster data: " "'%s'. Ignoring this line.", line); } else { + unsigned int col; + ++lineCursor; /* Skip to first character after quote */ /* Handle pixels until a close quote, eol, or we've returned all the pixels Caller wants. */ - while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) { - int colorNumber; - int i; - colorNumber = 0; /* initial value */ - for (i = 0; i < chars_per_pixel; ++i) - colorNumber = (colorNumber << 8) + *(lineCursor++); - if (ptab == NULL) - /* colormap is indexed directly by XPM color number */ - *(*cursorP)++ = colorNumber; - else { - /* colormap shadows ptab[]. Find this color # in ptab[] */ - int i; - for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i); - if (i < ncolors) - *(*cursorP)++ = i; - else - pm_error("Color number %d is in raster, but not in " - "colormap. Line it's in: '%s'", - colorNumber, line); - } + for (col = 0; col < width; ++col) { + + validateRasterPixel(lineCursor, charsPerPixel); + + pixrow[col] = hash_color(colorNameHashP, lineCursor); + + alpharow[col] = hash_isTransparent(colorNameHashP, lineCursor) ? + PBM_BLACK : PBM_WHITE; + + lineCursor += charsPerPixel; } + if (*lineCursor != '"') + pm_error("A raster line continues past width of image"); } } static void -ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, - pixel ** const colorsP, int ** const dataP, - int * const transparentP) { +convertRaster(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + unsigned int const charsPerPixel, + const ColorNameHash * const colorNameHashP, + FILE * const imageOutFileP, + FILE * const alphaOutFileP) { /*---------------------------------------------------------------------------- - Read the XPM file from stream 'stream'. + Read the XPM raster from *ifP and write the PPM raster to *imageOutFileP + and the alpha channel to *alphaOutFileP (where those are, respectively, + non-null). - Return the dimensions of the image as *widthP and *heightP. - Return the color map as *colorsP, which is an array of *ncolorsP - colors. + The dimensions are 'cols' by 'rows' and the color map for the XPM + raster is *colorNameHashP. +-----------------------------------------------------------------------------*/ + char line[MAX_LINE+1]; + pixel * pixrow; + bit * alpharow; + unsigned int row; - Return the raster in newly malloced storage, an array of *widthP by - *heightP integers, each of which is an index into the colormap - *colorsP (and therefore less than *ncolorsP). Return the address - of the array as *dataP. + pixrow = ppm_allocrow(cols); + alpharow = pbm_allocrow(cols); - In the colormap, put black for the transparent color, if the XPM - image contains one. ------------------------------------------------------------------------------*/ - char line[MAX_LINE+1], str1[MAX_LINE+1]; - int totalpixels; - int *cursor; /* cursor into *dataP */ - int *maxcursor; /* value of above cursor for last pixel in image */ - int *ptab; /* colormap - malloc'ed */ - int rc; - int ncolors; - int chars_per_pixel; + for (row = 0; row < rows; ++row) { + bool haveLine; - backup = FALSE; + for (haveLine = false; !haveLine; ) { + getLine(line, sizeof(line), ifP); - /* Read the header line */ - getLine(line, sizeof(line), stream); - backup = TRUE; /* back up so next read reads this line again */ - - rc = sscanf(line, "/* %s */", str1); - if (rc == 1 && strncmp(str1, "XPM", 3) == 0) { - /* It's an XPM version 3 file */ - readXpm3Header(stream, widthP, heightP, &chars_per_pixel, - &ncolors, colorsP, &ptab, transparentP); - } else { /* try as an XPM version 1 file */ - /* Assume it's an XPM version 1 file */ - readXpm1Header(stream, widthP, heightP, &chars_per_pixel, - &ncolors, colorsP, &ptab); - *transparentP = -1; /* No transparency in version 1 */ - } - totalpixels = *widthP * *heightP; - MALLOCARRAY(*dataP, totalpixels); - if (*dataP == NULL) - pm_error("Could not get %d bytes of memory for image", totalpixels); - cursor = *dataP; - maxcursor = *dataP + totalpixels - 1; - getLine(line, sizeof(line), stream); - /* read next line (first line may not always start with comment) */ - while (cursor <= maxcursor) { - if (strncmp(line, "/*", 2) == 0) { - /* It's a comment. Ignore it. */ - } else { - interpretXpmLine(line, chars_per_pixel, - ncolors, ptab, &cursor, maxcursor); + if (strneq(line, "/*", 2)) { + /* It's a comment. Ignore it. */ + } else + haveLine = true; } - if (cursor <= maxcursor) - getLine(line, sizeof(line), stream); + convertRow(line, cols, charsPerPixel, colorNameHashP, + pixrow, alpharow); + + if (imageOutFileP) + ppm_writeppmrow(imageOutFileP, + pixrow, cols, PPM_MAXMAXVAL, 0); + if (alphaOutFileP) + pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0); } - if (ptab) free(ptab); + + pbm_freerow(alpharow); + ppm_freerow(pixrow); } static void -writeOutput(FILE * const imageout_file, - FILE * const alpha_file, - int const cols, int const rows, - pixel * const colors, int * const data, - int transparent) { +readXpmHeader(FILE * const ifP, + unsigned int * const widthP, + unsigned int * const heightP, + unsigned int * const charsPerPixelP, + ColorNameHash ** const colorNameHashPP) { /*---------------------------------------------------------------------------- - Write the image in 'data' to open PPM file stream 'imageout_file', - and the alpha mask for it to open PBM file stream 'alpha_file', - except if either is NULL, skip it. + Read the XPM header, including color map. - 'data' is an array of cols * rows integers, each one being an index - into the colormap 'colors'. - - Where the index 'transparent' occurs in 'data', the pixel is supposed - to be transparent. If 'transparent' < 0, no pixels are transparent. + In the colormap, put black for the transparent color, if the XPM image + contains one. -----------------------------------------------------------------------------*/ - int row; - pixel *pixrow; - bit * alpharow; - - if (imageout_file) - ppm_writeppminit(imageout_file, cols, rows, PPM_MAXMAXVAL, 0); - if (alpha_file) - pbm_writepbminit(alpha_file, cols, rows, 0); - - pixrow = ppm_allocrow(cols); - alpharow = pbm_allocrow(cols); + char line[MAX_LINE+1]; + char str1[MAX_LINE+1]; + int rc; + unsigned int charsPerPixel; + unsigned int width, height; - for (row = 0; row < rows; ++row ) { - int col; - int * const datarow = data+(row*cols); + backup = FALSE; - for (col = 0; col < cols; ++col) { - pixrow[col] = colors[datarow[col]]; - if (datarow[col] == transparent) - alpharow[col] = PBM_BLACK; - else - alpharow[col] = PBM_WHITE; - } - if (imageout_file) - ppm_writeppmrow(imageout_file, - pixrow, cols, (pixval) PPM_MAXMAXVAL, 0); - if (alpha_file) - pbm_writepbmrow(alpha_file, alpharow, cols, 0); + /* Read the header line */ + getLine(line, sizeof(line), ifP); + backup = TRUE; /* back up so next read reads this line again */ + + rc = sscanf(line, "/* %s */", str1); + if (rc == 1 && strneq(str1, "XPM", 3)) { + /* It's an XPM version 3 file */ + readXpm3Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP); + } else { + /* Assume it's an XPM version 1 file */ + readXpm1Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP); } - ppm_freerow(pixrow); - pbm_freerow(alpharow); - - if (imageout_file) - pm_close(imageout_file); - if (alpha_file) - pm_close(alpha_file); -} - + *widthP = width; + *heightP = height; + *charsPerPixelP = charsPerPixel; +} + int main(int argc, char *argv[]) { - FILE *ifp; - FILE *alpha_file, *imageout_file; - pixel *colormap; - int cols, rows; - int transparent; /* value of 'data' that means transparent */ - int *data; - /* The image as an array of width * height integers, each one - being an index int colormap[]. - */ + FILE * ifP; + FILE * alphaOutFileP; + FILE * imageOutFileP; + unsigned int cols, rows; + unsigned int charsPerPixel; + ColorNameHash * colorNameHashP; - struct cmdline_info cmdline; + struct cmdlineInfo cmdline; ppm_init(&argc, argv); - parse_command_line(argc, argv, &cmdline); + parseCommandLine(argc, argv, &cmdline); verbose = cmdline.verbose; if ( cmdline.input_filespec != NULL ) - ifp = pm_openr( cmdline.input_filespec); + ifP = pm_openr( cmdline.input_filespec); else - ifp = stdin; + ifP = stdin; if (cmdline.alpha_stdout) - alpha_file = stdout; + alphaOutFileP = stdout; else if (cmdline.alpha_filename == NULL) - alpha_file = NULL; + alphaOutFileP = NULL; else { - alpha_file = pm_openw(cmdline.alpha_filename); + alphaOutFileP = pm_openw(cmdline.alpha_filename); } if (cmdline.alpha_stdout) - imageout_file = NULL; + imageOutFileP = NULL; else - imageout_file = stdout; + imageOutFileP = stdout; - ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent); - - pm_close(ifp); + readXpmHeader(ifP, &cols, &rows, &charsPerPixel, &colorNameHashP); - writeOutput(imageout_file, alpha_file, cols, rows, colormap, data, - transparent); + if (imageOutFileP) + ppm_writeppminit(imageOutFileP, cols, rows, PPM_MAXMAXVAL, 0); + if (alphaOutFileP) + pbm_writepbminit(alphaOutFileP, cols, rows, 0); - free(colormap); + + convertRaster(ifP, cols, rows, charsPerPixel, colorNameHashP, + imageOutFileP, alphaOutFileP); + + pm_close(ifP); + if (imageOutFileP) + pm_close(imageOutFileP); + if (alphaOutFileP) + pm_close(alphaOutFileP); + + hash_destroy(colorNameHashP); return 0; } + + + +/* +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** 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. +** +** Upgraded to handle 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, no advance of read ptr, would not read +** colors like "ac c black" because it would find +** the "c" of "ac" and then had problems with "c" +** as color. +** +** - Now understands multiword X11 color names +** +** - Now reads multiple color keys. Takes the color +** of the hightest available key. Lines no longer need +** to begin with key 'c'. +** +** - expanded line buffer to from 500 to 2048 for bigger files +*/ + |