home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / cfuncs / ppm.c < prev    next >
C/C++ Source or Header  |  2000-07-29  |  16KB  |  582 lines

  1. /*
  2. ############################################################################
  3. #
  4. #    File:     ppm.c
  5. #
  6. #    Subject:  Functions to manipulate PPM files in memory
  7. #
  8. #    Author:   Gregg M. Townsend
  9. #
  10. #    Date:     November 17, 1997
  11. #
  12. ############################################################################
  13. #
  14. #   This file is in the public domain.
  15. #
  16. ############################################################################
  17. #
  18. #  These functions manipulate raw (P6) PPM image files in memory.
  19. #  The images must not contain comment strings.
  20. #  ppmwidth(s) -- return width of PPM image.
  21. #  ppmheight(s) -- return height of PPM image.
  22. #  ppmmax(s) -- return maximum value in PPM header.
  23. #  ppmdata(s) -- return data portion of PPM image.
  24. #  ppmimage(s,p,f) -- quantify image s using palette p, with flags f.
  25. #     Returns an Icon image string.  Flag "o" selects ordered dithering.
  26. #     Defaults:  p="c6",  f="o"
  27. #  ppmstretch(s,lo,hi,max) -- apply contrast stretch operation
  28. #     Returns a PPM string image that results from setting all
  29. #     values <= lo to zero, all values >= hi to max, with values
  30. #     between scaling linearly.  If hi = lo + 1, this becomes a 
  31. #     simple threshold operation.  If lo=0 and hi=ppmmax(s), this
  32. #     simply scales an image to a new maximum.
  33. #     Requirements: 0 <= lo < hi <= ppmmax(s), 1 <= max <= 255.
  34. #     Defaults:      lo=0, hi=ppmmax(s), max=255.
  35. #  ppm3x3(s,a,b,c,d,e,f,g,h,i) -- apply 3x3 convolution to PPM image.
  36. #     The matrix of real numbers [[a,b,c],[d,e,f],[g,h,i]] is used
  37. #     as a transformation matrix applied independently to the three
  38. #     color components of the image.
  39. #
  40. ############################################################################
  41. #
  42. #  Requires:  Dynamic loading
  43. #
  44. ############################################################################
  45. */
  46.  
  47.  
  48.  
  49. #include "icall.h"
  50. #include <ctype.h>
  51. #include <string.h>
  52. #include <stdlib.h>
  53.  
  54. int palnum(descriptor *d);
  55. char *rgbkey(int p, double r, double g, double b);
  56.  
  57.  
  58.  
  59. typedef struct {    /* ppminfo: struct describing a ppm image */
  60.    int w, h;        /* width and height */
  61.    int max;        /* maximum value */
  62.    long npixels;    /* total number of pixels */
  63.    long nbytes;        /* total number of pixels */
  64.    char *data;        /* pointer to start of raw data; null indicates error */
  65. } ppminfo;
  66.  
  67. static ppminfo ppmcrack(descriptor d);
  68. static descriptor ppmalc(int w, int h, int max);
  69. static char *rowextend(char *dst, char *src, int w, int nbr);
  70. static int ppmrows(ppminfo hdr, int nbr, int (*func) (), long arg);
  71. static int sharpenrow(char *a[], int w, int i, long max);
  72. static int convrow(char *a[], int w, int i, long max);
  73.  
  74. static char *out;    /* general purpose global output pointer */
  75.  
  76.  
  77.  
  78. /* macros */
  79.  
  80. /* ArgPPM(int n, ppminfo hdr) -- validate arg n, init hdr */
  81. #define ArgPPM(n,hdr) do {\
  82.    ArgString(n); \
  83.    hdr = ppmcrack(argv[n]); \
  84.    if (!hdr.data) Fail; \
  85. } while(0)
  86.  
  87. /* AlcResult(int w, h, max, ppminfo hdr) -- alc result string, init hdr */
  88. /* WARNING -- can move other strings; refresh addresses from descriptors. */
  89. #define AlcResult(w, h, max, hdr) do {\
  90.    descriptor d = ppmalc(w, h, max); \
  91.    if (d.vword == 0) Error(306); \
  92.    hdr = ppmcrack(argv[0] = d); \
  93. } while(0)
  94.  
  95.  
  96.  
  97. /* ppm info functions */
  98.  
  99. int ppmwidth(int argc, descriptor *argv)    /*: extract width of PPM string */
  100.    {
  101.    ppminfo hdr;
  102.  
  103.    ArgPPM(1, hdr);
  104.    RetInteger(hdr.w);
  105.    }
  106.  
  107. int ppmheight(int argc, descriptor *argv)    /*: extract height of PPM string */
  108.    {
  109.    ppminfo hdr;
  110.  
  111.    ArgPPM(1, hdr);
  112.    RetInteger(hdr.h);
  113.    }
  114.  
  115. int ppmmax(int argc, descriptor *argv)        /*: extract max of PPM string */
  116.    {
  117.    ppminfo hdr;
  118.  
  119.    ArgPPM(1, hdr);
  120.    RetInteger(hdr.max);
  121.    }
  122.  
  123. int ppmdata(int argc, descriptor *argv)        /*: extract data from PPM string */
  124.    {
  125.    ppminfo hdr;
  126.  
  127.    ArgPPM(1, hdr);
  128.    RetAlcString(hdr.data, hdr.nbytes);
  129.    }
  130.  
  131.  
  132.  
  133. /* ppmstretch(s,lo,hi) -- apply contrast stretch operation */
  134.  
  135. int ppmstretch(int argc, descriptor *argv) /*: stretch contrast of PPM string */
  136.    {
  137.    ppminfo src, dst;
  138.    int lo, hi, max, i, v;
  139.    float m;
  140.    char *d, *s;
  141.  
  142.    ArgPPM(1, src);
  143.  
  144.    if (argc < 2 || IconType(argv[2]) == 'n')
  145.       lo = 0;
  146.    else {
  147.       ArgInteger(2);
  148.       lo = IntegerVal(argv[2]);
  149.       if (lo < 0 || lo >= src.max)
  150.          ArgError(2, 205);
  151.       }
  152.  
  153.    if (argc < 3 || IconType(argv[3]) == 'n')
  154.       hi = src.max;
  155.    else {
  156.       ArgInteger(3);
  157.       hi = IntegerVal(argv[3]);
  158.       if (hi <= lo || hi > src.max)
  159.          ArgError(3, 205);
  160.       }
  161.  
  162.    if (argc < 4 || IconType(argv[4]) == 'n')
  163.       max = 255;
  164.    else {
  165.       ArgInteger(4);
  166.       max = IntegerVal(argv[4]);
  167.       if (max < 1 || max > 255)
  168.          ArgError(4, 205);
  169.       }
  170.  
  171.    m = (float)(max + 1) / (hi - lo);
  172.  
  173.    AlcResult(src.w, src.h, max, dst);
  174.    src = ppmcrack(argv[1]);            /* may have moved */
  175.    d = dst.data;
  176.    s = src.data;
  177.    for (i = 0; i < dst.nbytes; i++) {
  178.       v = m * ((*s++ & 0xFF) - lo);
  179.       if (v < 0) v = 0;
  180.       else if (v > dst.max) v = dst.max;
  181.       *d++ = v;
  182.       }
  183.    Return;
  184.    }
  185.  
  186.  
  187.  
  188. /* ppmsharpen(s) -- apply fixed sharpening convolution  */
  189.  
  190. int ppmsharpen(int argc, descriptor *argv)    /*: sharpen a PPM string */
  191.    {
  192.    int rv;
  193.    ppminfo src, dst;
  194.  
  195.    ArgPPM(1, src);
  196.    AlcResult(src.w, src.h, src.max, dst);
  197.    src = ppmcrack(argv[1]);            /* may have moved */
  198.  
  199.    out = dst.data;
  200.    rv = ppmrows(src, 1, sharpenrow, src.max);
  201.    if (rv == 0)
  202.       Return;
  203.    argv[0] = nulldesc;
  204.    return rv;
  205.    }
  206.  
  207. static int sharpenrow(char *a[], int w, int i, long max)
  208.    {
  209.    unsigned char *prev, *curr, *next;
  210.    int v;
  211.  
  212.    prev = (unsigned char *) a[-1];
  213.    curr = (unsigned char *) a[0];
  214.    next = (unsigned char *) a[1];
  215.    w *= 3;
  216.    while (w--) {
  217.       v = 2.0 * curr[0]
  218.     - .10 * (prev[-3] + prev[3] + next[-3] + next[3])
  219.     - .15 * (prev[0] + curr[-3] + curr[3] + next[0]);
  220.       if (v < 0)
  221.      v = 0;
  222.       else if (v > max)
  223.      v = max;
  224.       *out++ = v;
  225.       prev++;
  226.       curr++;
  227.       next++;
  228.       }
  229.    return 0;
  230.    }
  231.  
  232.  
  233.  
  234. /* ppm3x3(s,a,b,c,d,e,f,g,h,i) -- apply 3x3 convolution matrix */
  235.  
  236. static float cells[9];
  237.  
  238. int ppm3x3(int argc, descriptor *argv)        /*: convolve PPM with matrix */
  239.    {
  240.    int rv, i;
  241.    ppminfo src, dst;
  242.  
  243.    ArgPPM(1, src);
  244.    for (i = 0; i < 9; i++) {
  245.       ArgReal(i + 2);
  246.       cells[i] = RealVal(argv[i + 2]);
  247.       }
  248.  
  249.    AlcResult(src.w, src.h, src.max, dst);
  250.    src = ppmcrack(argv[1]);            /* may have moved */
  251.  
  252.    out = dst.data;
  253.    rv = ppmrows(src, 1, convrow, src.max);
  254.    if (rv == 0)
  255.       Return;
  256.    argv[0] = nulldesc;
  257.    return rv;
  258.    }
  259.  
  260. static int convrow(char *a[], int w, int i, long max)
  261.    {
  262.    unsigned char *prev, *curr, *next;
  263.    int v;
  264.  
  265.    prev = (unsigned char *) a[-1];
  266.    curr = (unsigned char *) a[0];
  267.    next = (unsigned char *) a[1];
  268.    w *= 3;
  269.    while (w--) {
  270.       v = cells[0] * prev[-3] + cells[1] * prev[0] + cells[2] * prev[3]
  271.         + cells[3] * curr[-3] + cells[4] * curr[0] + cells[5] * curr[3]
  272.         + cells[6] * next[-3] + cells[7] * next[0] + cells[8] * next[3];
  273.       if (v < 0)
  274.      v = 0;
  275.       else if (v > max)
  276.      v = max;
  277.       *out++ = v;
  278.       prev++;
  279.       curr++;
  280.       next++;
  281.       }
  282.    return 0;
  283.    }
  284.  
  285.  
  286.  
  287. /* ppmimage(s,p,f) -- quantify image s using palette p, returning Icon image. */
  288.  
  289. #define MDIM 16            /* dither matrix dimension */
  290. #define MSIZE (MDIM * MDIM)    /* total size */
  291.  
  292. int ppmimage(int argc, descriptor *argv)    /*: dither PPM to Icon image */
  293.    {
  294.    int i, p, row, col, ir, ig, ib;
  295.    double m, gd, r, g, b, dither[MSIZE], *dp, d;
  296.    char *pname, *flags, *s, *t, *rv;
  297.    ppminfo hdr;
  298.    static double dmults[7] = {0., 1./3., 1./1., 1./2., 1./3., 1./4., 1./5.};
  299.    static double gmults[7] = {0., 3./6., 1./2., 1./3., 1./4., 1./5., 1./6.};
  300.    static unsigned char dfactor[MSIZE] = {
  301.         0,128, 32,160,  8,136, 40,168,  2,130, 34,162, 10,138, 42,170,
  302.       192, 64,224, 96,200, 72,232,104,194, 66,226, 98,202, 74,234,106,
  303.        48,176, 16,144, 56,184, 24,152, 50,178, 18,146, 58,186, 26,154,
  304.       240,112,208, 80,248,120,216, 88,242,114,210, 82,250,122,218, 90,
  305.        12,140, 44,172,  4,132, 36,164, 14,142, 46,174,  6,134, 38,166,
  306.       204, 76,236,108,196, 68,228,100,206, 78,238,110,198, 70,230,102,
  307.        60,188, 28,156, 52,180, 20,148, 62,190, 30,158, 54,182, 22,150,
  308.       252,124,220, 92,244,116,212, 84,254,126,222, 94,246,118,214, 86,
  309.         3,131, 35,163, 11,139, 43,171,  1,129, 33,161,  9,137, 41,169,
  310.       195, 67,227, 99,203, 75,235,107,193, 65,225, 97,201, 73,233,105,
  311.        51,179, 19,147, 59,187, 27,155, 49,177, 17,145, 57,185, 25,153,
  312.       243,115,211, 83,251,123,219, 91,241,113,209, 81,249,121,217, 89,
  313.        15,143, 47,175,  7,135, 39,167, 13,141, 45,173,  5,133, 37,165,
  314.       207, 79,239,111,199, 71,231,103,205, 77,237,109,197, 69,229,101,
  315.        63,191, 31,159, 55,183, 23,151, 61,189, 29,157, 53,181, 21,149,
  316.       255,127,223, 95,247,119,215, 87,253,125,221, 93,245,117,213, 85,
  317. };
  318.  
  319.    ArgString(1);
  320.  
  321.    if (argc < 2 || IconType(argv[2]) == 'n') {
  322.       p = 6;
  323.       pname = "c6";
  324.       }
  325.    else {
  326.       ArgString(2);
  327.       p = palnum(&argv[2]);
  328.       if (p == 0)  Fail;
  329.       if (p == -1) ArgError(1, 103);
  330.       pname = StringVal(argv[2]);
  331.       }
  332.  
  333.    if (argc < 3 || IconType(argv[3]) == 'n')
  334.       flags = "o";
  335.    else {
  336.       ArgString(3);
  337.       flags = StringVal(argv[3]);
  338.       }
  339.  
  340.    hdr = ppmcrack(argv[1]);
  341.    if (!hdr.data)
  342.       Fail;                /* PPM format error */
  343.  
  344.    if (!strchr(flags, 'o'))
  345.       m = gd = 0.0;            /* no dithering */
  346.    else if (p > 0) {
  347.       m = dmults[p] - .0001;        /* color dithering magnitude */
  348.       gd = gmults[p];            /* correction factor if gray input */
  349.       }
  350.    else {
  351.       m = 1.0 / (-p - .9999);        /* grayscale dithering magnitude */
  352.       gd = 1.0;                /* no correction needed */        
  353.       }
  354.  
  355.    for (i = 0; i < MSIZE; i++)        /* build dithering table */
  356.       dither[i] = m * (dfactor[i] / (double)(MSIZE)- 0.5);
  357.  
  358.    rv = alcstr(NULL, 10 + hdr.npixels);    /* allocate room for output string */
  359.    if (!rv)
  360.       Error(306);
  361.    hdr = ppmcrack(argv[1]);        /* get addr again -- may have moved */
  362.    sprintf(rv, "%d,%s,", hdr.w, pname);
  363.    t = rv + strlen(rv);
  364.  
  365.    m = 1.0 / hdr.max;
  366.    s = hdr.data;
  367.    for (row = hdr.h; row > 0; row--) {
  368.       dp = &dither[MDIM * (row & (MDIM - 1))];
  369.       for (col = hdr.w; col > 0; col--) {
  370.      d = dp[col & (MDIM - 1)];
  371.          ir = *s++ & 0xFF;
  372.          ig = *s++ & 0xFF;
  373.          ib = *s++ & 0xFF;
  374.      if (ir == ig && ig == ib) {
  375.         g = m * ig + gd * d;
  376.             if (g < 0) g = 0; else if (g > 1) g = 1;
  377.         r = b = g;
  378.         }
  379.      else {
  380.             r = m * ir + d;  if (r < 0) r = 0;  else if (r > 1) r = 1;
  381.             g = m * ig + d;  if (g < 0) g = 0;  else if (g > 1) g = 1;
  382.             b = m * ib + d;  if (b < 0) b = 0;  else if (b > 1) b = 1;
  383.         }
  384.          *t++ = *(rgbkey(p, r, g, b));
  385.          }
  386.       }
  387.  
  388.    RetAlcString(rv, t - rv);
  389.    }
  390.  
  391.  
  392.  
  393. /*************************  internal functions  *************************/
  394.  
  395.  
  396.  
  397. /*
  398.  *  ppmalc(w, h, max) -- allocate new ppm image and initialize header
  399.  *
  400.  *  If allocation fails, the address in the returned descriptor is NULL.
  401.  */
  402. static descriptor ppmalc(int w, int h, int max)
  403.    {
  404.    char buf[32];
  405.    descriptor d;
  406.  
  407.    sprintf(buf, "P6\n%d %d\n%d\n", w, h, max);
  408.    d.dword = strlen(buf) + 3 * w * h;
  409.    d.vword = (word)alcstr(NULL, d.dword);
  410.    if (d.vword != 0)
  411.       strcpy((void *)d.vword, buf);
  412.    return d;
  413.    }
  414.  
  415.  
  416.  
  417. /*  ppmcrack(d) -- crack PPM header, setting max=0 on error  */
  418.  
  419. static ppminfo ppmcrack(descriptor d)
  420.    {
  421.    int n;
  422.    char *s;
  423.    ppminfo info;
  424.    static ppminfo zeroes;
  425.  
  426.    s = StringAddr(d);
  427.    if (sscanf(s, "P6 %d %d %n", &info.w, &info.h, &n) < 2)
  428.       return zeroes;            /* not a raw PPM file */
  429.  
  430.    /* can't scanf for "max" because it consumes too much trailing whitespace */
  431.    info.max = 0;
  432.    for (s += n; isspace(*s); s++)
  433.       ;
  434.    while (isdigit(*s))
  435.       info.max = 10 * info.max + *s++ - '0';
  436.    if (info.max == 0 || info.max > 255)
  437.       return zeroes;            /* illegal max value for raw PPM */
  438.  
  439.    /* now consume exactly one more whitespace character */
  440.    if (isspace(*s))
  441.       s++;
  442.  
  443.    info.npixels = (long)info.w * (long)info.h;
  444.    info.nbytes =  3 * info.npixels;
  445.    if (s + info.nbytes > StringAddr(d) + StringLen(d))
  446.       return zeroes;            /* file was truncated */
  447.  
  448.    info.data = s;
  449.    return info;
  450.    }
  451.  
  452.  
  453.  
  454. /*
  455.  *  ppmrows(hdr, nbr, func, arg) -- extend rows and call driver function  
  456.  *
  457.  *  Calls func(a, w, i, arg) for each row of the PPM image identified by hdr,
  458.  *  where
  459.  *    a is a pointer to a pointer to the first byte of the row (see below)
  460.  *    w is the width of a row, in pixels
  461.  *    i is the row number
  462.  *    arg is passed along from the call to ppmrows
  463.  *
  464.  *  When nbr > 0, this indicates that func() needs to read up to nbr pixels
  465.  *  above, below, left, and/or right of each source pixel; ppmrows copies
  466.  *  and extends the rows to make this easy.  The argument "a" passed to func
  467.  *  is a pointer to the center of an array of row pointers that extends by
  468.  *  nbr rows in each direction.  That is, a[0] points to the current row;
  469.  *  a[-1] points to the previous row, a[1] to the next row, and so on.
  470.  *
  471.  *  Each row is extended by nbr additional pixels in each direction by the
  472.  *  duplication of the first and last pixels.  The pointers in the array "a"
  473.  *  skip past the initial duplicates.  Thus a[0][0] is the first byte
  474.  *  (the red byte) of the first pixel, a[0][-3] is its duplicate, and
  475.  *  a[0][3] is the first byte of the second pixel of the row.
  476.  *
  477.  *  The idea behind all this complication is to make it easy to perform
  478.  *  neighborhood operations.  See any caller of ppmrows for an example.
  479.  *
  480.  *  If ppmrows cannot allocate memory, it returns error code 305.
  481.  *  If func returns nonzero, ppmrows returns that value immediately.
  482.  *  Otherwise, ppmrows returns zero.
  483.  */
  484.  
  485. static int ppmrows(ppminfo hdr, int nbr, int (*func) (), long arg)
  486.    {
  487.    char **a, *s;
  488.    void *buf;
  489.    int i, rv, np, row, rowlen;
  490.  
  491.    /* process nbr=0 without any copying */
  492.    if (nbr <= 0) {
  493.       s = hdr.data;
  494.       for (row = 0; row < hdr.h; row++) {
  495.      rv = func(&s, hdr.w, row, arg);
  496.      if (rv != 0)
  497.         return rv;
  498.      s += 3 * hdr.w;
  499.      }
  500.       return 0;
  501.       }
  502.  
  503.    /* allocate memory for pointers and data */
  504.    np = 2 * nbr + 1;            /* number of pointers */
  505.    rowlen = 3 * (nbr + hdr.w + nbr);    /* length of one extended row */
  506.    a = buf = malloc(np * sizeof(char *) + np * rowlen);
  507.    if (buf == NULL)
  508.       return 305;
  509.  
  510.    /* set pointers to row buffers */
  511.    s = (char *)buf + np * sizeof(char *) + 3 * nbr;
  512.    for (i = 0; i < np; i++) {
  513.       *a++ = s;
  514.       s += rowlen;
  515.       }
  516.    a -= nbr + 1;            /* point to center row */
  517.  
  518.    /* initialize buffers */
  519.    for (i = -nbr; i < 0; i++)        /* duplicates of first row */
  520.       rowextend(a[i], hdr.data, hdr.w, nbr);
  521.    for (i = 0; i <= nbr; i++)        /* first nbr+1 rows */
  522.       rowextend(a[i], hdr.data + 3 * i * hdr.w, hdr.w, nbr);
  523.  
  524.    /* iterate through rows */
  525.    for (row = 0; row < hdr.h; row++) {
  526.  
  527.       /* call function for this row */
  528.       rv = func(a, hdr.w, row, arg);
  529.       if (rv != 0) {
  530.      free(buf);
  531.      return rv;
  532.      }
  533.  
  534.       /* rotate row pointers */
  535.       s = a[-nbr];
  536.       for (i = -nbr; i < nbr; i++)
  537.      a[i] = a[i+1];
  538.       a[nbr] = s;
  539.  
  540.       /* replace oldest with new row */
  541.       if (row + nbr < hdr.h)
  542.      rowextend(s, hdr.data + 3 * (row + nbr) * hdr.w, hdr.w, nbr);
  543.       else
  544.      rowextend(s, hdr.data + 3 * (hdr.h - 1) * hdr.w, hdr.w, nbr);
  545.  
  546.       }
  547.  
  548.    free(buf);
  549.    return 0;
  550.    }
  551.  
  552.  
  553.  
  554. /*
  555.  *  rowextend(dst, src, w, nbr) -- extend row on both ends
  556.  *
  557.  *  Copy w bytes from src to dst, extending both ends by nbr copies of
  558.  *  the first/last 3-byte pixel.  w is the row width in pixels.
  559.  *  Returns unextended dst pointer.
  560.  */
  561. static char *rowextend(char *dst, char *src, int w, int nbr)
  562.    {
  563.    char *s1, *s2, *d1, *d2;
  564.  
  565.    memcpy(dst, src, 3 * w);
  566.    d1 = dst;
  567.    d2 = dst + 3 * w;
  568.    s1 = d1 + 3;
  569.    s2 = d2 - 3;
  570.    nbr *= 3;
  571.    while (nbr--) {
  572.       *--d1 = *--s1;
  573.       *d2++ = *s2++;
  574.       }
  575.    return dst;
  576.    }
  577.