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