home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Graphics / graphics-16000.iso / general / convrtrs / pbmplus / ntpbmsrc.lha / netpbm / ppm / ppmtoxpm.c < prev    next >
C/C++ Source or Header  |  1993-10-30  |  15KB  |  509 lines

  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.  
  32. #include <ctype.h>
  33. #include "ppm.h"
  34. #include "ppmcmap.h"
  35.  
  36.  
  37. /* Max number of colors allowed in ppm input. */
  38. #define MAXCOLORS    256
  39.  
  40. /* Max number of rgb mnemonics allowed in rgb text file. */
  41. #define MAX_RGBNAMES 1024
  42.  
  43. /* Lower bound and upper bound of character-pixels printed in XPM output.
  44.    Be careful, don't want the character '"' in this range. */
  45. /*#define LOW_CHAR  '#'  <-- minimum ascii character allowed */
  46. /*#define HIGH_CHAR '~'  <-- maximum ascii character allowed */
  47. #define LOW_CHAR  '`'
  48. #define HIGH_CHAR 'z'
  49.  
  50. #define max(a,b) ((a) > (b) ? (a) : (b))
  51.  
  52. typedef struct {            /* rgb values and ascii names (from
  53.                      * rgb text file) */
  54.     int r, g, b;            /* rgb values, range of 0 -> 65535 */
  55.     char *name;                /* color mnemonic of rgb value */
  56. }      rgb_names;
  57.  
  58. typedef struct {            /* character-pixel mapping */
  59.     char *cixel;            /* character string printed for
  60.                      * pixel */
  61.     char *rgbname;            /* ascii rgb color, either color
  62.                      * mnemonic or #rgb value */
  63. }      cixel_map;
  64.  
  65. /* prototypes */
  66. void    read_rgb_names ARGS((char *rgb_fname, rgb_names *rgbn, int *rgbn_max));
  67. char *  gen_numstr ARGS((int i, int base, int digits));
  68. void    gen_cmap ARGS((colorhist_vector chv, int ncolors, pixval maxval,
  69.                         int map_rgb_names, rgb_names *rgbn, int rgbn_max,
  70.                         cixel_map *cmap, int *charspp));
  71.  
  72.  
  73. static pixel **pixels;
  74.  
  75. int
  76. main(argc, argv)
  77.     int argc;
  78.     char *argv[];
  79.  
  80. {
  81.     FILE *ifd;
  82.     register pixel *pP;
  83.     int argn, rows, cols, ncolors, row, col, i;
  84.     pixval maxval;            /* pixval == unsigned char or
  85.                      * unsigned short */
  86.     colorhash_table cht;
  87.     colorhist_vector chv;
  88.  
  89.     int xpm = 3;
  90.  
  91.     /* Used for rgb value -> rgb mnemonic mapping */
  92.     int map_rgb_names = 0;
  93.     rgb_names rgbn[MAX_RGBNAMES];
  94.     int rgbn_max;
  95.  
  96.     /* Used for rgb value -> character-pixel string mapping */
  97.     cixel_map *cmap;
  98.     int charspp;            /* chars per pixel */
  99.  
  100.     char out_name[100], rgb_fname[100], *cp;
  101.     char *usage = "[-name <xpm-name>] [-rgb <rgb-textfile>] [-xpm1] [ppmfile]";
  102.  
  103.     ppm_init(&argc, argv);
  104.     out_name[0] = rgb_fname[0] = '\0';
  105.  
  106.     argn = 1;
  107.  
  108.     /* Check for command line options. */
  109.     while (argn < argc && argv[argn][0] == '-') {
  110.  
  111.     /* Case "-", use stdin for input. */
  112.     if (argv[argn][1] == '\0')
  113.         break;
  114.  
  115.     /* Case "-name <xpm-filename>", get output filename. */
  116.     if (strncmp(argv[argn], "-name", max(strlen(argv[argn]), 2)) == 0) {
  117.         argn++;
  118.         if (argn == argc || sscanf(argv[argn], "%s", out_name) != 1)
  119.         pm_usage(usage);
  120.     }
  121.     /* Case "-rgb <rgb-filename>", get rgb mnemonics filename. */
  122.     else if (strncmp(argv[argn], "-rgb", max(strlen(argv[argn]), 2)) == 0) {
  123.         argn++;
  124.         if (argn == argc || sscanf(argv[argn], "%s", rgb_fname) != 1)
  125.         pm_usage(usage);
  126.         map_rgb_names = 1;
  127.     }
  128.     else if (strncmp(argv[argn], "-xpm1", max(strlen(argv[argn]), 2)) == 0) {
  129.         xpm = 1;
  130.     }
  131.     /* Nothing else allowed... */
  132.     else
  133.         pm_usage(usage);
  134.  
  135.     argn++;
  136.     }
  137.  
  138.     /* Input file specified, open it and set output filename if necessary. */
  139.     if (argn < argc) {
  140.  
  141.     /* Open the input file. */
  142.     ifd = pm_openr(argv[argn]);
  143.  
  144.     /* If output filename not specified, use input filename as default. */
  145.     if (out_name[0] == '\0') {
  146.         strcpy(out_name, argv[argn]);
  147.         if (cp = index(out_name, '.'))
  148.         *cp = '\0';        /* remove extension */
  149.     }
  150.  
  151.     /*
  152.      * If (1) input file was specified as "-" we're using stdin, or (2)
  153.      * output filename was specified as "-", set output filename to the
  154.      * default. 
  155.      */
  156.     if (!strcmp(out_name, "-"))
  157.         strcpy(out_name, "noname");
  158.  
  159.     argn++;
  160.     }
  161.     /* No input file specified.  Using stdin so set default output filename. */
  162.     else {
  163.     ifd = stdin;
  164.     if (out_name[0] == '\0')
  165.         strcpy(out_name, "noname");
  166.     }
  167.  
  168.     /* Only 0 or 1 input files allowed. */
  169.     if (argn != argc)
  170.     pm_usage(usage);
  171.  
  172.     /*
  173.      * "maxval" is the largest value that can be be found in the ppm file.
  174.      * All pixel components are relative to this value. 
  175.      */
  176.     pixels = ppm_readppm(ifd, &cols, &rows, &maxval);
  177.     pm_close(ifd);
  178.  
  179.     /* Figure out the colormap. */
  180.     fprintf(stderr, "(Computing colormap...");
  181.     fflush(stderr);
  182.     chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &ncolors);
  183.     if (chv == (colorhist_vector) 0)
  184.     pm_error(
  185.      "too many colors - try running the pixmap through 'ppmquant 256'",
  186.          0, 0, 0, 0, 0);
  187.     fprintf(stderr, "  Done.  %d colors found.)\n", ncolors);
  188.  
  189.     /* Make a hash table for fast color lookup. */
  190.     cht = ppm_colorhisttocolorhash(chv, ncolors);
  191.  
  192.     /*
  193.      * If a rgb text file was specified, read in the rgb mnemonics. Does not
  194.      * return if fatal error occurs. 
  195.      */
  196.     if (map_rgb_names)
  197.     read_rgb_names(rgb_fname, rgbn, &rgbn_max);
  198.  
  199.     /* Now generate the character-pixel colormap table. */
  200.     gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
  201.          cmap, &charspp);
  202.  
  203.     /* Write out the XPM file. */
  204.  
  205.     if (xpm == 1)
  206.     {
  207.     printf("#define %s_format 1\n", out_name);
  208.     printf("#define %s_width %d\n", out_name, cols);
  209.     printf("#define %s_height %d\n", out_name, rows);
  210.     printf("#define %s_ncolors %d\n", out_name, ncolors);
  211.     printf("#define %s_chars_per_pixel %d\n", out_name, charspp);
  212.     printf("static char * %s_colors[] = {\n", out_name);
  213.     for (i = 0; i < ncolors; i++) {
  214.         printf("\"%s\" , \"%s\",\n", cmap[i].cixel, cmap[i].rgbname);
  215.     }
  216.     printf("} ;\n");
  217.     printf("static char * %s_pixels[] = {\n", out_name);
  218.     for (row = 0; row < rows; row++) {
  219.         printf("\"");
  220.         for (col = 0, pP = pixels[row]; col < cols; col++, pP++) {
  221.             printf("%s", cmap[ppm_lookupcolor(cht, pP)].cixel);
  222.         }
  223.         printf("\"%s\n", (row == (rows - 1) ? "" : ","));
  224.     }
  225.     printf("} ;\n");
  226.     }
  227.     else
  228.     {
  229.     printf("/* XPM */\n");
  230.     printf("static char *%s[] = {\n", out_name);
  231.     printf("/* width height ncolors chars_per_pixel */\n");
  232.     printf("\"%d %d %d %d\",\n", cols, rows, ncolors, charspp);
  233.     printf("/* colors */\n");
  234.     for (i = 0; i < ncolors; i++) {
  235.         printf("\"%s c %s\",\n", cmap[i].cixel, cmap[i].rgbname);
  236.     }
  237.     printf("/* pixels */\n");
  238.     for (row = 0; row < rows; row++) {
  239.         printf("\"");
  240.         for (col = 0, pP = pixels[row]; col < cols; col++, pP++) {
  241.             printf("%s", cmap[ppm_lookupcolor(cht, pP)].cixel);
  242.         }
  243.         printf("\"%s\n", (row == (rows - 1) ? "" : ","));
  244.     }
  245.     printf("};\n");
  246.     }
  247.  
  248.     exit(0);
  249.  
  250. }                    /* main */
  251.  
  252. /*---------------------------------------------------------------------------*/
  253. /* This routine reads a rgb text file.  It stores the rgb values (0->65535)
  254.    and the rgb mnemonics (malloc'ed) into the "rgbn" array.  Returns the
  255.    number of entries stored in "rgbn_max". */
  256. void
  257. read_rgb_names(rgb_fname, rgbn, rgbn_max)
  258.   char *rgb_fname;
  259.   rgb_names *rgbn;
  260.   int *rgbn_max;
  261.  
  262. {
  263.     FILE *rgbf;
  264.     int i, items, red, green, blue;
  265.     char line[512], name[512], *rgbname, *n, *m;
  266.  
  267.     /* Open the rgb text file.  Abort if error. */
  268.     if ((rgbf = fopen(rgb_fname, "r")) == NULL)
  269.     pm_error("error opening rgb text file \"%s\"", rgb_fname, 0, 0, 0, 0);
  270.  
  271.     /* Loop reading each line in the file. */
  272.     for (i = 0; fgets(line, sizeof(line), rgbf); i++) {
  273.  
  274.     /* Quit if rgb text file is too large. */
  275.     if (i == MAX_RGBNAMES) {
  276.         fprintf(stderr,
  277.         "Too many entries in rgb text file, truncated to %d entries.\n",
  278.             MAX_RGBNAMES);
  279.         fflush(stderr);
  280.         break;
  281.     }
  282.     /* Read the line.  Skip if bad. */
  283.     items = sscanf(line, "%d %d %d %[^\n]\n", &red, &green, &blue, name);
  284.     if (items != 4) {
  285.         fprintf(stderr, "rgb text file syntax error on line %d\n", i + 1);
  286.         fflush(stderr);
  287.         i--;
  288.         continue;
  289.     }
  290.     /* Make sure rgb values are within 0->255 range.  Skip if bad. */
  291.     if (red < 0 || red > 0xFF ||
  292.         green < 0 || green > 0xFF ||
  293.         blue < 0 || blue > 0xFF) {
  294.         fprintf(stderr, "rgb value for \"%s\" out of range, ignoring it\n",
  295.             name);
  296.         fflush(stderr);
  297.         i--;
  298.         continue;
  299.     }
  300.     /* Allocate memory for ascii name.  Abort if error. */
  301.     if (!(rgbname = (char *) malloc(strlen(name) + 1)))
  302.         pm_error("out of memory allocating rgb name", 0, 0, 0, 0, 0);
  303.         
  304. #ifdef NAMESLOWCASE
  305.     /* Copy string to ascii name and lowercase it. */
  306.     for (n = name, m = rgbname; *n; n++)
  307.         *m++ = isupper(*n) ? tolower(*n) : *n;
  308.     *m = '\0';
  309. #else
  310.     strcpy(rgbname, name);
  311. #endif
  312.  
  313.     /* Save the rgb values and ascii name in the array. */
  314.     rgbn[i].r = red << 8;
  315.     rgbn[i].g = green << 8;
  316.     rgbn[i].b = blue << 8;
  317.     rgbn[i].name = rgbname;
  318.     }
  319.  
  320.     /* Return the max number of rgb names. */
  321.     *rgbn_max = i - 1;
  322.  
  323.     fclose(rgbf);
  324.  
  325. }                    /* read_rgb_names */
  326.  
  327. /*---------------------------------------------------------------------------*/
  328. /* Given a number and a base, (base == HIGH_CHAR-LOW_CHAR+1), this routine
  329.    prints the number into a malloc'ed string and returns it.  The length of
  330.    the string is specified by "digits".  The ascii characters of the printed
  331.    number range from LOW_CHAR to HIGH_CHAR.  The string is LOW_CHAR filled,
  332.    (e.g. if LOW_CHAR==0, HIGH_CHAR==1, digits==5, i=3, routine would return
  333.    the malloc'ed string "00011"). */
  334. char *
  335. gen_numstr(i, base, digits)
  336.     int i, base, digits;
  337. {
  338.     char *str, *p;
  339.     int d;
  340.  
  341.     /* Allocate memory for printed number.  Abort if error. */
  342.     if (!(str = (char *) malloc(digits + 1)))
  343.     pm_error("out of memory", 0, 0, 0, 0, 0);
  344.  
  345.     /* Generate characters starting with least significant digit. */
  346.     p = str + digits;
  347.     *p-- = '\0';            /* nul terminate string */
  348.     while (p >= str) {
  349.     d = i % base;
  350.     i /= base;
  351.     *p-- = (char) ((int) LOW_CHAR + d);
  352.     }
  353.  
  354.     return str;
  355.  
  356. }                    /* gen_numstr */
  357.  
  358. /*---------------------------------------------------------------------------*/
  359. /* This routine generates the character-pixel colormap table. */
  360. void
  361. gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
  362.      cmap, charspp)
  363.   /* input: */
  364.   colorhist_vector chv;        /* contains rgb values for colormap */
  365.   int ncolors;            /* number of entries in colormap */
  366.   pixval maxval;        /* largest color value, all rgb
  367.                  * values relative to this, (pixval
  368.                  * == unsigned short) */
  369.   int map_rgb_names;        /* == 1 if mapping rgb values to rgb
  370.                  * mnemonics */
  371.   rgb_names *rgbn;            /* rgb mnemonics from rgb text file */
  372.   int rgbn_max;            /* number of rgb mnemonics in table */
  373.  
  374.   /* output: */
  375.   cixel_map *cmap;        /* pixel strings and ascii rgb
  376.                  * colors */
  377.   int *charspp;            /* characters per pixel */
  378.  
  379. {
  380.     int i, j, base, cpp, mval, red, green, blue, r, g, b, matched;
  381.     char *str;
  382.  
  383.     /*
  384.      * Figure out how many characters per pixel we'll be using.  Don't want
  385.      * to be forced to link with libm.a, so using a division loop rather
  386.      * than a log function. 
  387.      */
  388.     base = (int) HIGH_CHAR - (int) LOW_CHAR + 1;
  389.     for (cpp = 0, j = ncolors; j; cpp++)
  390.     j /= base;
  391.     *charspp = cpp;
  392.  
  393.     /*
  394.      * Determine how many hex digits we'll be normalizing to if the rgb
  395.      * value doesn't match a color mnemonic. 
  396.      */
  397.     mval = (int) maxval;
  398.     if (mval <= 0x000F)
  399.     mval = 0x000F;
  400.     else if (mval <= 0x00FF)
  401.     mval = 0x00FF;
  402.     else if (mval <= 0x0FFF)
  403.     mval = 0x0FFF;
  404.     else
  405.     mval = 0xFFFF;
  406.  
  407.     /*
  408.      * Generate the character-pixel string and the rgb name for each
  409.      * colormap entry. 
  410.      */
  411.     for (i = 0; i < ncolors; i++) {
  412.  
  413.     /*
  414.      * The character-pixel string is simply a printed number in base
  415.      * "base" where the digits of the number range from LOW_CHAR to
  416.      * HIGH_CHAR and the printed length of the number is "cpp". 
  417.      */
  418.     cmap[i].cixel = gen_numstr(i, base, cpp);
  419.  
  420.     /* Fetch the rgb value of the current colormap entry. */
  421.     red = PPM_GETR(chv[i].color);
  422.     green = PPM_GETG(chv[i].color);
  423.     blue = PPM_GETB(chv[i].color);
  424.  
  425.     /*
  426.      * If the ppm color components are not relative to 15, 255, 4095,
  427.      * 65535, normalize the color components here. 
  428.      */
  429.     if (mval != (int) maxval) {
  430.         red = (red * mval) / (int) maxval;
  431.         green = (green * mval) / (int) maxval;
  432.         blue = (blue * mval) / (int) maxval;
  433.     }
  434.  
  435.     /*
  436.      * If the "-rgb <rgbfile>" option was specified, attempt to map the
  437.      * rgb value to a color mnemonic. 
  438.      */
  439.     if (map_rgb_names) {
  440.  
  441.         /*
  442.          * The rgb values of the color mnemonics are normalized relative
  443.          * to 255 << 8, (i.e. 0xFF00).  [That's how the original MIT
  444.          * code did it, really should have been "v * 65535 / 255"
  445.          * instead of "v << 8", but have to use the same scheme here or
  446.          * else colors won't match...]  So, if our rgb values aren't
  447.          * already 16-bit values, need to shift left. 
  448.          */
  449.         if (mval == 0x000F) {
  450.         r = red << 12;
  451.         g = green << 12;
  452.         b = blue << 12;
  453.         /* Special case hack for "white". */
  454.         if (0xF000 == r && r == g && g == b)
  455.             r = g = b = 0xFF00;
  456.         } else if (mval == 0x00FF) {
  457.         r = red << 8;
  458.         g = green << 8;
  459.         b = blue << 8;
  460.         } else if (mval == 0x0FFF) {
  461.         r = red << 4;
  462.         g = green << 4;
  463.         b = blue << 4;
  464.         } else {
  465.         r = red;
  466.         g = green;
  467.         b = blue;
  468.         }
  469.  
  470.         /*
  471.          * Just perform a dumb linear search over the rgb values of the
  472.          * color mnemonics.  One could speed things up by sorting the
  473.          * rgb values and using a binary search, or building a hash
  474.          * table, etc... 
  475.          */
  476.         for (matched = 0, j = 0; j <= rgbn_max; j++)
  477.         if (r == rgbn[j].r && g == rgbn[j].g && b == rgbn[j].b) {
  478.  
  479.             /* Matched.  Allocate string, copy mnemonic, and exit. */
  480.             if (!(str = (char *) malloc(strlen(rgbn[j].name) + 1)))
  481.             pm_error("out of memory", 0, 0, 0, 0, 0);
  482.             strcpy(str, rgbn[j].name);
  483.             cmap[i].rgbname = str;
  484.             matched = 1;
  485.             break;
  486.         }
  487.         if (matched)
  488.         continue;
  489.     }
  490.  
  491.     /*
  492.      * Either not mapping to color mnemonics, or didn't find a match.
  493.      * Generate an absolute #RGB value string instead. 
  494.      */
  495.     if (!(str = (char *) malloc(mval == 0x000F ? 5 :
  496.                     mval == 0x00FF ? 8 :
  497.                     mval == 0x0FFF ? 11 :
  498.                     14)))
  499.         pm_error("out of memory", 0, 0, 0, 0, 0);
  500.  
  501.     sprintf(str, mval == 0x000F ? "#%X%X%X" :
  502.         mval == 0x00FF ? "#%02X%02X%02X" :
  503.         mval == 0x0FFF ? "#%03X%03X%03X" :
  504.         "#%04X%04X%04X", red, green, blue);
  505.     cmap[i].rgbname = str;
  506.     }
  507.  
  508. }                    /* gen_cmap */
  509.