1 /* ppmtoxpm.c - read a portable pixmap and produce a (version 3) X11 pixmap
2 **
3 ** Copyright (C) 1990 by Mark W. Snitily
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 **
12 ** This tool was developed for Schlumberger Technologies, ATE Division, and
13 ** with their permission is being made available to the public with the above
14 ** copyright notice and permission notice.
15 **
16 ** Upgraded to XPM2 by
17 **   Paul Breslaw, Mecasoft SA, Zurich, Switzerland (paul@mecazh.uu.ch)
18 **   Thu Nov  8 16:01:17 1990
19 **
20 ** Upgraded to XPM version 3 by
21 **   Arnaud Le Hors (lehors@mirsa.inria.fr)
22 **   Tue Apr 9 1991
23 **
24 ** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
25 **  - Bug fix, should should malloc space for rgbn[j].name+1 in line 441
26 **    caused segmentation faults
27 **
28 **  - lowercase conversion of RGB names def'ed out,
29 **    considered harmful.
30 **
31 ** Michael Pall (pall@rz.uni-karlsruhe.de) - 29 Nov 93:
32 **  - Use the algorithm from xpm-lib for pixel encoding
33 **    (base 93 not base 28 -> saves a lot of space for colorful xpms)
34 */
35 
36 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
37 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
38 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
39 
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <string.h>
43 
44 #include "pm_c_util.h"
45 #include "ppm.h"
46 #include "shhopt.h"
47 #include "nstring.h"
48 #include "mallocvar.h"
49 
50 /* Max number of entries we will put in the XPM color map
51    Don't forget the one entry for transparency.
52 
53    We don't use this anymore.  Ppmtoxpm now has no arbitrary limit on
54    the number of colors.
55 
56    We're assuming this isn't in fact an XPM format requirement, because
57    we've seen it work with 257, and 257 seems to be common, because it's
58    the classic 256 colors, plus transparency.  The value was 256 for
59    ages before we added transparency capability to this program in May
60    2001.  At that time, we started failing with 256 color images.
61    Some limit was also necessary before then because ppm_computecolorhash()
62    required us to specify a maximum number of colors.  It doesn't anymore.
63 
64    If we find out that some XPM processing programs choke on more than
65    256 colors, we'll have to readdress this issue.  - Bryan.  2001.05.13.
66 */
67 #define MAXCOLORS    256
68 
69 #define MAXPRINTABLE 92         /* number of printable ascii chars
70                                  * minus \ and " for string compat
71                                  * and ? to avoid ANSI trigraphs. */
72 
73 static const char * const printable =
74 " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZ\
75 ASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
76 
77 
78 struct cmdlineInfo {
79     /* All the information the user supplied in the command line,
80        in a form easy for the program to use.
81     */
82     const char *inputFilename;
83     const char *name;
84     const char *rgb;
85     const char *alpha_filename;
86     unsigned int hexonly;
87     unsigned int verbose;
88 };
89 
90 
91 
92 static void
parseCommandLine(int argc,char ** argv,struct cmdlineInfo * const cmdlineP)93 parseCommandLine(int argc, char ** argv,
94                  struct cmdlineInfo * const cmdlineP) {
95 /*----------------------------------------------------------------------------
96    Note that the file spec array we return is stored in the storage that
97    was passed to us as the argv array.
98 -----------------------------------------------------------------------------*/
99     optEntry *option_def;
100         /* Instructions to OptParseOptions3 on how to parse our options.
101          */
102     optStruct3 opt;
103 
104     unsigned int option_def_index;
105     const char * nameOpt;
106     unsigned int nameSpec;
107 
108     MALLOCARRAY(option_def, 100);
109 
110     option_def_index = 0;   /* incremented by OPTENTRY */
111     OPTENT3(0,   "alphamask",   OPT_STRING, &cmdlineP->alpha_filename,
112             NULL, 0);
113     OPTENT3(0,   "name",        OPT_STRING, &nameOpt,
114             &nameSpec, 0);
115     OPTENT3(0,   "rgb",         OPT_STRING, &cmdlineP->rgb,
116             NULL, 0);
117     OPTENT3(0,   "hexonly",     OPT_FLAG, NULL,
118             &cmdlineP->hexonly, 0);
119     OPTENT3(0,   "verbose",     OPT_FLAG, NULL,
120             &cmdlineP->verbose, 0);
121 
122     /* Set the defaults */
123     cmdlineP->alpha_filename = NULL;  /* no transparency */
124     cmdlineP->rgb = NULL;      /* no rgb file specified */
125 
126     opt.opt_table = option_def;
127     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
128     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
129 
130     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
131         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
132 
133     if (argc-1 == 0)
134         cmdlineP->inputFilename = "-";
135     else if (argc-1 != 1)
136         pm_error("Program takes zero or one argument (filename).  You "
137                  "specified %d", argc-1);
138     else
139         cmdlineP->inputFilename = argv[1];
140 
141     /* If output filename not specified, use input filename as default. */
142     if (nameSpec)
143         cmdlineP->name = nameOpt;
144     else if (streq(cmdlineP->inputFilename, "-"))
145         cmdlineP->name = "noname";
146     else {
147         static char name[80+1];
148         char *cp;
149 
150         STRSCPY(name, cmdlineP->inputFilename);
151         cp = strchr(name, '.');
152         if (cp)
153             *cp = '\0';     /* remove extension */
154         cmdlineP->name = name;
155     }
156 }
157 
158 
159 typedef struct {            /* rgb values and ascii names (from
160                              * rgb text file) */
161     int r, g, b;            /* rgb values, range of 0 -> 65535 */
162     char *name;             /* color mnemonic of rgb value */
163 } rgb_names;
164 
165 typedef struct {
166     /* Information for an XPM color map entry */
167     char *cixel;
168        /* XPM color number, as might appear in the XPM raster */
169     const char *rgbname;
170        /* color name, e.g. "blue" or "#01FF23" */
171 } cixel_map;
172 
173 
174 
175 static char *
genNumstr(unsigned int const input,int const digits)176 genNumstr(unsigned int const input, int const digits) {
177 /*---------------------------------------------------------------------------
178    Given a number and a base (MAXPRINTABLE), this routine prints the
179    number into a malloc'ed string and returns it.  The length of the
180    string is specified by "digits".  It is not printed in decimal or
181    any other number system you're used to.  Rather, each digit is from
182    the set printable[], which contains MAXPRINTABLE characters; the
183    character printable[n] has value n.
184 
185    The string is printable[0] filled with high order zeroes.
186 
187    Example:
188      Assume:
189        printable[0] == 'q'
190        printable[1] == '%'
191        MAXPRINTABLE == 2
192        digits == 5
193        input == 3
194      Result is the malloc'ed string "qqq%%"
195 ---------------------------------------------------------------------------*/
196     char *str, *p;
197     int d;
198     unsigned int i;
199 
200     /* Allocate memory for printed number.  Abort if error. */
201     if (!(str = (char *) malloc(digits + 1)))
202         pm_error("out of memory");
203 
204     i = input;
205     /* Generate characters starting with least significant digit. */
206     p = str + digits;
207     *p-- = '\0';            /* nul terminate string */
208     while (p >= str) {
209         d = i % MAXPRINTABLE;
210         i /= MAXPRINTABLE;
211         *p-- = printable[d];
212     }
213 
214     if (i != 0)
215         pm_error("Overflow converting %d to %d digits in base %d",
216                  input, digits, MAXPRINTABLE);
217 
218     return str;
219 }
220 
221 
222 
223 static unsigned int
xpmMaxvalFromMaxval(pixval const maxval)224 xpmMaxvalFromMaxval(pixval const maxval) {
225 
226     unsigned int retval;
227 
228     /*
229      * Determine how many hex digits we'll be normalizing to if the rgb
230      * value doesn't match a color mnemonic.
231      */
232     if (maxval <= 0x000F)
233         retval = 0x000F;
234     else if (maxval <= 0x00FF)
235         retval = 0x00FF;
236     else if (maxval <= 0x0FFF)
237         retval = 0x0FFF;
238     else if (maxval <= 0xFFFF)
239         retval = 0xFFFF;
240     else
241         pm_error("Internal error - impossible maxval %x", maxval);
242 
243     return retval;
244 }
245 
246 
247 
248 static unsigned int
charsPerPixelForSize(unsigned int const cmapSize)249 charsPerPixelForSize(unsigned int const cmapSize) {
250 /*----------------------------------------------------------------------------
251    Return the number of characters it will take to represent a pixel in
252    an XPM that has a colormap of size 'cmapSize'.  Each pixel in an XPM
253    represents an index into the colormap with a base-92 scheme where each
254    character is one of 92 printable characters.  Ergo, if the colormap
255    has at most 92 characters, each pixel will be represented by a single
256    character.  If it has more than 92, but at most 92*92, it will take 2,
257    etc.
258 
259    If cmapSize is zero, there's no such thing as an XPM pixel, so we
260    return an undefined value.
261 -----------------------------------------------------------------------------*/
262     unsigned int charsPerPixel;
263 
264     if (cmapSize > 0) {
265         unsigned int j;
266 
267         for (charsPerPixel = 0, j = cmapSize-1; j > 0; ++charsPerPixel)
268             j /= MAXPRINTABLE;
269     }
270     return charsPerPixel;
271 }
272 
273 
274 
275 static void
genCmap(colorhist_vector const chv,int const ncolors,pixval const maxval,colorhash_table const colornameHash,const char * const colornames[],bool const includeTransparent,cixel_map ** const cmapP,unsigned int * const transIndexP,unsigned int * const cmapSizeP,unsigned int * const charsPerPixelP)276 genCmap(colorhist_vector const chv,
277         int              const ncolors,
278         pixval           const maxval,
279         colorhash_table  const colornameHash,
280         const char *     const colornames[],
281         bool             const includeTransparent,
282         cixel_map **     const cmapP,
283         unsigned int *   const transIndexP,
284         unsigned int *   const cmapSizeP,
285         unsigned int *   const charsPerPixelP) {
286 /*----------------------------------------------------------------------------
287    Generate the XPM color map in cixel_map format (which is just a step
288    away from the actual text that needs to be written the XPM file).  The
289    color map is defined by 'chv', which contains 'ncolors' colors which
290    have maxval 'maxval'.
291 
292    Output is in newly malloc'ed storage, with its address returned as
293    *cmapP.  We return the number of entries in it as *cmapSizeP.
294 
295    This map includes an entry for transparency, whether the raster uses
296    it or not.  We return its index as *transIndexP.
297 
298    In the map, identify colors by the names given by 'colornameHash' and
299    colornames[].  'colornameHash' maps a color in 'pixel' form to an
300    index into colornames[]; colornames[] contains the text to identify the
301    color in the XPM format.  The colors in 'colornameHash' have maxval 255.
302    If a color is not in 'colornameHash', use hexadecimal notation in the
303    output colormap.
304 
305    But if 'colornameHash' is null, don't use color names at all.  Just use
306    hexadecimal notation.
307 
308    Return as *charsPerPixel the number of characters, or digits, that
309    will be needed in the XPM raster to form an index into this color map.
310 -----------------------------------------------------------------------------*/
311     unsigned int const cmapSize = ncolors + (includeTransparent ? 1 : 0);
312 
313     cixel_map * cmap;  /* malloc'ed */
314     unsigned int cmapIndex;
315     unsigned int charsPerPixel;
316     unsigned int xpmMaxval;
317 
318     MALLOCARRAY(cmap, cmapSize);
319     if (cmapP == NULL)
320         pm_error("Out of memory allocating %u bytes for a color map.",
321                  (unsigned)sizeof(cixel_map) * (ncolors+1));
322 
323     xpmMaxval = xpmMaxvalFromMaxval(maxval);
324 
325     charsPerPixel = charsPerPixelForSize(cmapSize);
326 
327     /*
328      * Generate the character-pixel string and the rgb name for each
329      * colormap entry.
330      */
331     for (cmapIndex = 0; cmapIndex < ncolors; ++cmapIndex) {
332         pixel const color = chv[cmapIndex].color;
333 
334         pixel color255;
335             /* The color, scaled to maxval 255 */
336         const char * colorname;  /* malloc'ed */
337         /*
338          * The character-pixel string is simply a printed number in base
339          * MAXPRINTABLE where the digits of the number range from
340          * printable[0] .. printable[MAXPRINTABLE-1] and the printed length
341          * of the number is 'charsPerPixel'.
342          */
343         cmap[cmapIndex].cixel = genNumstr(cmapIndex, charsPerPixel);
344 
345         PPM_DEPTH(color255, color, maxval, 255);
346 
347         if (colornameHash == NULL)
348             colorname = NULL;
349         else {
350             int colornameIndex;
351             colornameIndex = ppm_lookupcolor(colornameHash, &color255);
352             if (colornameIndex >= 0)
353                 colorname = strdup(colornames[colornameIndex]);
354             else
355                 colorname = NULL;
356         }
357         if (colorname)
358             cmap[cmapIndex].rgbname = colorname;
359         else {
360             /* Color has no name; represent it in hexadecimal */
361 
362             pixel scaledColor;
363             const char * hexString;  /* malloc'ed */
364 
365             PPM_DEPTH(scaledColor, color, maxval, xpmMaxval);
366 
367             pm_asprintf(&hexString, xpmMaxval == 0x000F ? "#%X%X%X" :
368                         xpmMaxval == 0x00FF ? "#%02X%02X%02X" :
369                         xpmMaxval == 0x0FFF ? "#%03X%03X%03X" :
370                         "#%04X%04X%04X",
371                         PPM_GETR(scaledColor),
372                         PPM_GETG(scaledColor),
373                         PPM_GETB(scaledColor)
374                 );
375 
376             if (hexString == NULL)
377                 pm_error("Unable to allocate storage for hex string");
378             cmap[cmapIndex].rgbname = hexString;
379         }
380     }
381 
382     if (includeTransparent) {
383         /* Add the special transparency entry to the colormap */
384         unsigned int const transIndex = ncolors;
385         cmap[transIndex].rgbname = strdup("None");
386         cmap[transIndex].cixel = genNumstr(transIndex, charsPerPixel);
387         *transIndexP = transIndex;
388     }
389     *cmapP          = cmap;
390     *cmapSizeP      = cmapSize;
391     *charsPerPixelP = charsPerPixel;
392 }
393 
394 
395 
396 static void
destroyCmap(cixel_map * const cmap,unsigned int const cmapSize)397 destroyCmap(cixel_map *  const cmap,
398             unsigned int const cmapSize) {
399 
400     int i;
401     /* Free the real color entries */
402     for (i = 0; i < cmapSize; i++) {
403         pm_strfree(cmap[i].rgbname);
404         free(cmap[i].cixel);
405     }
406     free(cmap);
407 }
408 
409 
410 
411 static void
writeXpmFile(FILE * const outfile,pixel ** const pixels,gray ** const alpha,pixval const alphamaxval,char const name[],int const cols,int const rows,unsigned int const cmapSize,unsigned int const charsPerPixel,cixel_map const cmap[],colorhash_table const cht,unsigned int const transIndex)412 writeXpmFile(FILE *          const outfile,
413              pixel **        const pixels,
414              gray **         const alpha,
415              pixval          const alphamaxval,
416              char            const name[],
417              int             const cols,
418              int             const rows,
419              unsigned int    const cmapSize,
420              unsigned int    const charsPerPixel,
421              cixel_map       const cmap[],
422              colorhash_table const cht,
423              unsigned int    const transIndex) {
424 /*----------------------------------------------------------------------------
425    Write the whole XPM file to the open stream 'outfile'.
426 
427    'cmap' is the colormap to be placed in the XPM.  'cmapSize' is the
428    number of entries in it.  'cht' is a hash table that gives you an
429    index into 'cmap' given a color.  'transIndex' is the index into cmap
430    of the transparent color, and is valid only if 'alpha' is non-null
431    (otherwise, cmap might not contain a transparent color).
432 -----------------------------------------------------------------------------*/
433     /* First the header */
434     printf("/* XPM */\n");
435     fprintf(outfile, "static char *%s[] = {\n", name);
436     fprintf(outfile, "/* width height ncolors chars_per_pixel */\n");
437     fprintf(outfile, "\"%d %d %d %d\",\n", cols, rows,
438             cmapSize, charsPerPixel);
439 
440     {
441         int i;
442         /* Write out the colormap (part of header) */
443         fprintf(outfile, "/* colors */\n");
444         for (i = 0; i < cmapSize; i++) {
445             fprintf(outfile, "\"%s c %s\",\n", cmap[i].cixel, cmap[i].rgbname);
446         }
447     }
448     {
449         int row;
450 
451         /* And now the raster */
452         fprintf(outfile, "/* pixels */\n");
453         for (row = 0; row < rows; row++) {
454             int col;
455             fprintf(outfile, "\"");
456             for (col = 0; col < cols; col++) {
457                 if (alpha && alpha[row][col] <= alphamaxval/2)
458                     /* It's a transparent pixel */
459                     fprintf(outfile, "%s", cmap[transIndex].cixel);
460                 else
461                     fprintf(outfile, "%s",
462                             cmap[ppm_lookupcolor(cht,
463                                                  &pixels[row][col])].cixel);
464             }
465             fprintf(outfile, "\"%s\n", (row == (rows - 1) ? "" : ","));
466         }
467     }
468     /* And close up */
469     fprintf(outfile, "};\n");
470 }
471 
472 
473 
474 static void
readAlpha(const char filespec[],gray *** const alphaP,int const cols,int const rows,pixval * const alphamaxvalP)475 readAlpha(const char filespec[], gray *** const alphaP,
476           int const cols, int const rows, pixval * const alphamaxvalP) {
477 
478     FILE * alpha_file;
479     int alphacols, alpharows;
480 
481     alpha_file = pm_openr(filespec);
482     *alphaP = pgm_readpgm(alpha_file, &alphacols, &alpharows, alphamaxvalP);
483     pm_close(alpha_file);
484 
485     if (cols != alphacols || rows != alpharows)
486         pm_error("Alpha mask is not the same dimensions as the "
487                  "image.  Image is %d by %d, while mask is %d x %d.",
488                  cols, rows, alphacols, alpharows);
489 }
490 
491 
492 
493 static void
computecolorhash(pixel ** const pixels,gray ** const alpha,int const cols,int const rows,gray const alphaMaxval,colorhash_table * const chtP,unsigned int * const ncolorsP,bool * const transparentSomewhereP)494 computecolorhash(pixel **          const pixels,
495                  gray **           const alpha,
496                  int               const cols,
497                  int               const rows,
498                  gray              const alphaMaxval,
499                  colorhash_table * const chtP,
500                  unsigned int *    const ncolorsP,
501                  bool *            const transparentSomewhereP) {
502 /*----------------------------------------------------------------------------
503    Compute a colorhash_table with one entry for each color in 'pixels' that
504    is not mostly transparent according to alpha mask 'alpha' (which has
505    maxval 'alphaMaxval').  alpha == NULL means all pixels are opaque.
506 
507    The value associated with the color in the hash we build is meaningless.
508 
509    Return the colorhash_table as *chtP, and the number of colors in it
510    as *ncolorsP.  Return *transparentSomewhereP == TRUE iff the image has
511    at least one pixel that is mostly transparent.
512 -----------------------------------------------------------------------------*/
513     colorhash_table cht;
514     int row;
515 
516     cht = ppm_alloccolorhash( );
517     *ncolorsP = 0;   /* initial value */
518     *transparentSomewhereP = FALSE;  /* initial assumption */
519 
520     /* Go through the entire image, building a hash table of colors. */
521     for (row = 0; row < rows; ++row) {
522         int col;
523 
524         for (col = 0; col < cols; ++col) {
525             if (!alpha || alpha[row][col] > alphaMaxval/2) {
526                 /* It's mostly opaque, so add this color to the hash
527                    if it's not already there.
528                 */
529                 pixel const color = pixels[row][col];
530                 int const lookupRc = ppm_lookupcolor(cht, &color);
531 
532                 if (lookupRc < 0) {
533                     /* It's not in the hash yet, so add it */
534                     ppm_addtocolorhash(cht, &color, 0);
535                     ++(*ncolorsP);
536                 }
537             } else
538                 *transparentSomewhereP = TRUE;
539         }
540     }
541     *chtP = cht;
542 }
543 
544 
545 
546 static void
computeColormap(pixel ** const pixels,gray ** const alpha,int const cols,int const rows,gray const alphaMaxval,colorhist_vector * const chvP,colorhash_table * const chtP,unsigned int * const ncolorsP,bool * const transparentSomewhereP)547 computeColormap(pixel **           const pixels,
548                 gray **            const alpha,
549                 int                const cols,
550                 int                const rows,
551                 gray               const alphaMaxval,
552                 colorhist_vector * const chvP,
553                 colorhash_table *  const chtP,
554                 unsigned int *     const ncolorsP,
555                 bool *             const transparentSomewhereP) {
556 /*----------------------------------------------------------------------------
557    Compute the color map for the image 'pixels', which is 'cols' by 'rows',
558    in Netpbm data structures (a colorhist_vector for index-to-color lookups
559    and a colorhash_table for color-to-index lookups).
560 
561    Exclude pixels that alpha mask 'alpha' (which has maxval
562    'alphaMaxval') says are mostly transparent.  alpha == NULL means all
563    pixels are opaque.
564 
565    We return as *chvP an array of the colors present in 'pixels',
566    excluding those that are mostly transparent.  We return as
567    *ncolorsP the number of such colors.  We return
568    *transparentSomewhereP == TRUE iff the image has at least one
569    pixel that is mostly transparent.
570 -----------------------------------------------------------------------------*/
571     colorhash_table histCht;
572 
573     pm_message("(Computing colormap...");
574     computecolorhash(pixels, alpha, cols, rows, alphaMaxval,
575                      &histCht, ncolorsP, transparentSomewhereP);
576     pm_message("...Done.  %d colors found.)", *ncolorsP);
577 
578     *chvP = ppm_colorhashtocolorhist(histCht, *ncolorsP);
579     ppm_freecolorhash(histCht);
580     /* Despite the name, the following generates an index on *chvP,
581        with which given a color you can quickly find the entry number
582        in *chvP that contains that color.
583     */
584     *chtP = ppm_colorhisttocolorhash(*chvP, *ncolorsP);
585 }
586 
587 
588 
589 int
main(int argc,char * argv[])590 main(int argc, char *argv[]) {
591 
592     FILE *ifp;
593     int rows, cols;
594     unsigned int ncolors;
595     bool transparentSomewhere;
596     pixval maxval, alphaMaxval;
597     colorhash_table cht;
598     colorhist_vector chv;
599 
600     colorhash_table colornameHash;
601         /* Hash table to map colors to their names */
602     const char ** colornames;
603         /* Table of color names; 'colornameHash' yields an index into this
604            array.
605         */
606 
607     pixel **pixels;
608     gray **alpha;
609 
610     /* Used for rgb value -> character-pixel string mapping */
611     cixel_map *cmap;  /* malloc'ed */
612         /* The XPM colormap */
613     unsigned int cmapSize;
614         /* Number of entries in 'cmap' */
615     unsigned int transIndex;
616         /* Index into 'cmap' of the transparent color, if there is one */
617 
618     unsigned int charsPerPixel;
619 
620     struct cmdlineInfo cmdline;
621 
622     ppm_init(&argc, argv);
623 
624     parseCommandLine(argc, argv, &cmdline);
625 
626     ifp = pm_openr(cmdline.inputFilename);
627     pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
628     pm_close(ifp);
629 
630     if (cmdline.alpha_filename)
631         readAlpha(cmdline.alpha_filename, &alpha, cols, rows, &alphaMaxval);
632     else
633         alpha = NULL;
634 
635     computeColormap(pixels, alpha, cols, rows, alphaMaxval,
636                     &chv, &cht, &ncolors, &transparentSomewhere);
637 
638     if (cmdline.hexonly)
639         colornameHash = NULL;
640     else if (cmdline.rgb)
641         ppm_readcolornamefile(cmdline.rgb, TRUE, &colornameHash, &colornames);
642     else
643         ppm_readcolornamefile(NULL, FALSE, &colornameHash, &colornames);
644 
645     /* Now generate the character-pixel colormap table. */
646     genCmap(chv, ncolors, maxval,
647             colornameHash, colornames, transparentSomewhere,
648             &cmap, &transIndex, &cmapSize, &charsPerPixel);
649 
650     writeXpmFile(stdout, pixels, alpha, alphaMaxval,
651                  cmdline.name, cols, rows, cmapSize,
652                  charsPerPixel, cmap, cht, transIndex);
653 
654     if (colornameHash) {
655         ppm_freecolorhash(colornameHash);
656         ppm_freecolornames(colornames);
657     }
658     destroyCmap(cmap, cmapSize);
659     ppm_freearray(pixels, rows);
660     if (alpha) pgm_freearray(alpha, rows);
661 
662     return 0;
663 }
664 
665