diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/ppm/xpmtoppm.c | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm/xpmtoppm.c')
-rw-r--r-- | converter/ppm/xpmtoppm.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c new file mode 100644 index 00000000..9dddfd83 --- /dev/null +++ b/converter/ppm/xpmtoppm.c @@ -0,0 +1,832 @@ +/* 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 +*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> + +#include "ppm.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" + +#define MAX_LINE (8 * 1024) + /* The maximum size XPM input line we can handle. */ + +/* number of xpmColorKeys */ +#define NKEYS 5 + +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 */ +}; + +struct cmdline_info { + /* 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; + int alpha_stdout; + int verbose; +}; + + +static int verbose; + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info *cmdline_p) { +/*---------------------------------------------------------------------------- + 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)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct2 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); + + cmdline_p->alpha_filename = NULL; + cmdline_p->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. */ + + if (argc - 1 == 0) + cmdline_p->input_filespec = NULL; /* he wants stdin */ + else if (argc - 1 == 1) + cmdline_p->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; + else + cmdline_p->alpha_stdout = FALSE; + +} + + +static char lastInputLine[MAX_LINE+1]; + /* contents of line most recently read from input */ +static bool backup; + /* TRUE means next read should be a reread of the most recently read + line, i.e. lastInputLine, instead of a read from the input file. + */ + + +static void +getline(char * const line, + size_t const size, + FILE * const stream) { +/*---------------------------------------------------------------------------- + Read the next line from the input file 'stream', through the one-line + buffer lastInputLine[]. + + If 'backup' is true, the "next line" is the previously read line, i.e. + the one in that one-line buffer. Otherwise, the "next line" is the next + line from the real file. After reading the backed up line, we reset + 'backup' to false. + + Return the line as a null terminated string in *line, which is an + array of 'size' bytes. + + Exit program if the line doesn't fit in the buffer. +-----------------------------------------------------------------------------*/ + if (size > sizeof(lastInputLine)) + pm_error("INTERNAL ERROR: getline() received 'size' parameter " + "which is out of bounds"); + + if (backup) { + strncpy(line, lastInputLine, size); + backup = FALSE; + } else { + if (fgets(line, size, stream) == NULL) + pm_error("EOF or read error on input file"); + if (strlen(line) == size - 1) + pm_error("Input file has line that is too long (longer than " + "%u bytes).", (unsigned)size - 1); + STRSCPY(lastInputLine, line); + } +} + + + +static unsigned int +getNumber(char * const p, unsigned int const size) { + + unsigned int retval; + 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) { + + char *t1; + char *t2; + + for (t1=*cursorP; ISSPACE(*t1); t1++); /* skip white space */ + for (t2 = t1; !ISSPACE(*t2) && *t2 != '"' && *t2 != '\0'; t2++); + /* Move to next white space, ", or eol */ + if (t2 > t1) + strncpy(output, t1, t2 - t1); + output[t2 - t1] = '\0'; + *cursorP = t2; +} + + + +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) { +/*---------------------------------------------------------------------------- + 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. + + Iff 'transparent', set *transparentP to the colormap index that + corresponds to this color. +-----------------------------------------------------------------------------*/ + 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; + } +} + + + +static void +interpretXpm3ColorTableLine(char line[], int const seqNum, + int const chars_per_pixel, + pixel * const colors, int * const ptab, + int * const transparentP) { +/*---------------------------------------------------------------------------- + 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. + + 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). + + 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. +-----------------------------------------------------------------------------*/ + /* 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 + color specifications. -Bryan 2001.05.06. + */ + char str2[MAX_LINE+1]; + char *t1; + char *t2; + int endOfEntry; /* boolean */ + + unsigned int curkey, key, highkey; /* current color key */ + unsigned int lastwaskey; + /* The last token we processes was a key, and we have processed + at least one token. + */ + char curbuf[BUFSIZ]; /* current buffer */ + int isTransparent; + + int colorNumber; + /* A color number that appears in the raster */ + /* 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.", + line, seqNum); + else + t1++; /* Points now to first color number character */ + + colorNumber = getNumber(t1, chars_per_pixel); + t1 += chars_per_pixel; + + /* + * read color keys and values + */ + curkey = 0; + highkey = 1; + lastwaskey = FALSE; + t2 = t1; + endOfEntry = FALSE; + while ( !endOfEntry ) { + int isKey; /* boolean */ + getword(str2, &t2); + if (strlen(str2) == 0) + endOfEntry = TRUE; + else { + /* See if the word we got is a valid key (and get its key + number if so) + */ + for (key = 1; + key <= NKEYS && !STREQ(xpmColorKeys[key - 1], str2); + key++); + isKey = (key <= NKEYS); + + if (lastwaskey || !isKey) { + /* This word is a color specification (or "none" for + transparent). + */ + if (!curkey) + 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) ) { + /* This entry identifies the transparent color number */ + strcat(curbuf, "#000000"); /* Make it black */ + isTransparent = TRUE; + } else + strcat(curbuf, str2); /* append buf */ + lastwaskey = 0; + } 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); + highkey = curkey; + } + curkey = key; /* set new key */ + curbuf[0] = '\0'; /* reset curbuf */ + isTransparent = FALSE; + lastwaskey = 1; + } + if (*t2 == '"') break; + } + } + /* Put the info for the last key in the line into the colormap (or + ignore it if there's already a higher color form for this colormap + entry in it) + */ + if (curkey > highkey) { + addToColorMap(seqNum, colorNumber, colors, ptab, curbuf, + isTransparent, transparentP); + highkey = curkey; + } + if (highkey == 1) + pm_error("C error scanning color table"); +} + + + +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) { +/*---------------------------------------------------------------------------- + Read the header of the XPM file on stream 'stream'. 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. +-----------------------------------------------------------------------------*/ + char line[MAX_LINE+1]; + const char * xpm3_signature = "/* XPM */"; + + *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1; + + /* Read the XPM signature comment */ + getline(line, sizeof(line), stream); + if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) + 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) + 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)) { + while (!strstr(line, "*/")) + getline(line, sizeof(line), stream); + getline(line, sizeof(line), stream); + } + if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP, + ncolorsP, chars_per_pixelP) != 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); + } + + /* 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); + } + } +} + + +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) { +/*---------------------------------------------------------------------------- + Read the header of the XPM file on stream 'stream'. 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, v; + int i, j; + bool processedStaticChar; + /* We have read up to and interpreted the "static char..." line */ + + *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1; + + /* Read the initial defines. */ + processedStaticChar = FALSE; + while (!processedStaticChar) { + getline(line, sizeof(line), stream); + + if (sscanf(line, "#define %s %d", str1, &v) == 2) { + char *t1; + if ((t1 = strrchr(str1, '_')) == NULL) + t1 = str1; + else + ++t1; + if (STREQ(t1, "format")) + format = v; + else if (STREQ(t1, "width")) + *widthP = v; + else if (STREQ(t1, "height")) + *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)) { + if ((t1 = strrchr(line, '_')) == NULL) + t1 = line; + else + ++t1; + processedStaticChar = TRUE; + } + } + /* File is positioned to "static char" line, which is in line[] and + t1 points to position of last "_" in the line, or the beginning of + the line if there is no "_" + */ + if (format == -1) + pm_error("missing or invalid format"); + if (format != 1) + pm_error("can't handle XPM version %d", format); + if (*widthP == -1) + pm_error("missing or invalid width"); + if (*heightP == -1) + 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 there's a monochrome color table, skip it. */ + if (!strncmp(t1, "mono", 4)) { + for (;;) { + getline(line, sizeof(line), stream); + if (!strncmp(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); + + 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) + break; + } +} + + + +static void +interpretXpmLine(char const line[], + int const chars_per_pixel, + int const ncolors, + int * const ptab, + int ** const cursorP, + int * const maxCursor) { +/*---------------------------------------------------------------------------- + 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. + + 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. + + 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. +-----------------------------------------------------------------------------*/ + char * lineCursor; + + lineCursor = strchr(line, '"'); /* position to 1st quote in line */ + if (lineCursor == NULL) { + /* We've seen a purported XPM that had a blank line in it. Just + ignoring it was the right thing to do. 05.05.27. + */ + pm_message("WARNING: No opening quotation mark in XPM input " + "line which is supposed to be a line of raster data: " + "'%s'. Ignoring this line.", line); + } else { + ++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); + } + } + } +} + + + +static void +ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, + pixel ** const colorsP, int ** const dataP, + int * const transparentP) { +/*---------------------------------------------------------------------------- + Read the XPM file from stream 'stream'. + + Return the dimensions of the image as *widthP and *heightP. + Return the color map as *colorsP, which is an array of *ncolorsP + colors. + + 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. + + 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; + + backup = FALSE; + + /* 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 (cursor <= maxcursor) + getline(line, sizeof(line), stream); + } + if (ptab) free(ptab); +} + + + +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) { +/*---------------------------------------------------------------------------- + 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. + + '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. +-----------------------------------------------------------------------------*/ + 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); + + for (row = 0; row < rows; ++row ) { + int col; + int * const datarow = data+(row*cols); + + 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); + } + ppm_freerow(pixrow); + pbm_freerow(alpharow); + + if (imageout_file) + pm_close(imageout_file); + if (alpha_file) + pm_close(alpha_file); +} + + + +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[]. + */ + + struct cmdline_info cmdline; + + ppm_init(&argc, argv); + + parse_command_line(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + if ( cmdline.input_filespec != NULL ) + ifp = pm_openr( cmdline.input_filespec); + else + ifp = stdin; + + if (cmdline.alpha_stdout) + alpha_file = stdout; + else if (cmdline.alpha_filename == NULL) + alpha_file = NULL; + else { + alpha_file = pm_openw(cmdline.alpha_filename); + } + + if (cmdline.alpha_stdout) + imageout_file = NULL; + else + imageout_file = stdout; + + ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent); + + pm_close(ifp); + + writeOutput(imageout_file, alpha_file, cols, rows, colormap, data, + transparent); + + free(colormap); + + return 0; +} |