home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / fbm / part07 / fbham.c next >
Encoding:
C/C++ Source or Header  |  1989-06-08  |  14.4 KB  |  573 lines

  1. /*****************************************************************
  2.  * fbham.c: FBM Library 0.94 (Beta test) 20-May-89  Michael Mauldin
  3.  *
  4.  * fbham.c: Write a 24bit RGB file as an Amiga HAM IFF file
  5.  *
  6.  * USAGE
  7.  *     % fbham < image1 > image2
  8.  *
  9.  * EDITLOG
  10.  *    LastEditDate = Sat May 20 19:15:08 1989 - Michael Mauldin
  11.  *    LastFileName = /usr2/mlm/src/misc/fbm/fbham.c
  12.  *
  13.  * HISTORY
  14.  * 20-May-89  Michael Mauldin (mlm) at Carnegie Mellon University
  15.  *    Beta release (version 0.94) mlm@cs.cmu.edu
  16.  *
  17.  * 20-Apr-89  C. Harald Koch (chk) at DCIEM Toronto.
  18.  *     Created.  chk@ben.dciem.dnd.ca
  19.  *
  20.  *================================================================
  21.  * based on ray2.c from DBW_Render, Copyright 1987 David B. Wecker
  22.  *
  23.  * From: chk@dretor.dciem.dnd.ca (C. Harald Koch)
  24.  * Subject: fbham.c - convert a 24bit FBM file to an IFF file using HAM mode
  25.  * To: Michael.Mauldin@nl.cs.cmu.edu (Michael Maudlin)
  26.  * Date: Mon, 1 May 89 17:21:16 EDT
  27.  * X-Mailer: ELM [version 2.2 PL0]
  28.  * 
  29.  * This is the source to my program to convert from a 24bit FBM file to Amiga
  30.  * HAM mode. It is based on Dave Wecker's RAY2 program, which converts the
  31.  * output of his raytracer to HAM mode. His code uses the Amiga graphics
  32.  * library to plot the pixels in a framebuffer for encoding; this version will
  33.  * run standalone.
  34.  * 
  35.  * There may be bugs, although it works well for me here. It is probably not in
  36.  * the format you want for FBM source; Please go ahead and modify it. There is
  37.  * probably room for some command line options for various things (such as the
  38.  * verbose output, run length encoding, etc) which I haven't needed and so
  39.  * haven't added.
  40.  * 
  41.  * I will send you the IRIS programs when I get them. Enjoy!
  42.  *    -chk
  43.  *****************************************************************/
  44.  
  45. #include <stdio.h>
  46. #include <math.h>
  47. #include "fbm.h"
  48.  
  49. # define USAGE "Usage: fbham < image > image"
  50.  
  51. #ifndef lint
  52. static char    *fbmid = 
  53. "$FBM fbham.c <0.93> 03-May-89  (C) 1989 by C. Harald Koch$";
  54. #endif
  55.  
  56. #define DEPTH        6        /* max depth of Amiga HAM image */
  57.  
  58. #define BMHDsize    20L        /* chunk sizes */
  59. #define CMAPsize    96L
  60. #define CAMGsize    4L
  61. #define BODYsize    ((long)(16000L))
  62. #define FORMsize    (BODYsize+CAMGsize+CMAPsize+BMHDsize+36L)
  63.  
  64. unsigned char *planes[DEPTH];    /* bitplane pointers */
  65.  
  66. static int      comp = 1;    /* compress image? */
  67. static int      depth = DEPTH;    /* depth of image being created */
  68. static int      colors = 16;    /* number of colors in color lookup table */
  69. static int      colorstats[4096][2];    /* color usage statistics */
  70. static int    row_size;    /* number of bytes in IFF row of data */
  71.  
  72. /* temp variables for writing IFF file */
  73. char            str[40];
  74. long            lng, pos1, pos2;
  75. short           wrd;
  76. unsigned char   byt;
  77. unsigned char  *dest, destbuf[BUFSIZ];
  78.  
  79.  
  80. main(argc, argv)
  81. char           *argv[];
  82. {
  83.     FBM             input;
  84.  
  85.     /* Clear the memory pointers so alloc_fbm won't be confused */
  86.     input.cm = input.bm = (unsigned char *) NULL;
  87.  
  88.     /* Read the image and rotate it */
  89.     if (!read_bitmap(&input, argc > 0 ? argv[1] : (char *) NULL))
  90.     {
  91.     exit(1);
  92.     }
  93.  
  94.     /* slight sanity checks. could be better. */
  95.     if (input.hdr.physbits != 8 || input.hdr.planes != 3) {
  96.     fprintf(stderr, "input file must be 24 bit RGB data\n");
  97.     exit(1);
  98.     }
  99.  
  100.     /* convert to HAM */
  101.     if (!fbm2ham(&input, stdout)) {
  102.     exit(1);
  103.     }
  104.  
  105.     exit(0);
  106. }
  107.  
  108.  
  109. /************************ run length encoding from Amiga RKM *****************/
  110. #define DUMP        0   /* list of different bytes */
  111. #define RUN        1   /* single run of bytes */
  112. #define MinRun        3   /* shortest allowed run */
  113. #define MaxRun        128 /* longest run (length is signed char) */
  114. #define    MaxDat        128 /* longest block of unencoded data */
  115. #define GetByte()    (*source++)
  116. #define PutByte(c)    { *dest++ = (c); ++PutSize; }
  117. #define OutDump(nn)    dest = PutDump(dest,nn);
  118. #define OutRun(nn,cc)    dest = PutRun(dest,nn,cc);
  119.  
  120. int             PutSize;
  121. char            buf[256];
  122.  
  123. unsigned char  *
  124. PutDump(dest, nn)
  125. unsigned char  *dest;
  126. int             nn;
  127. {
  128.     int             i;
  129.  
  130.     PutByte(nn - 1);
  131.     for (i = 0; i < nn; i++)
  132.     PutByte(buf[i]);
  133.     return (dest);
  134. }
  135.  
  136. unsigned char  *
  137. PutRun(dest, nn, cc)
  138. unsigned char  *dest;
  139. int             nn, cc;
  140. {
  141.     PutByte(-(nn - 1));
  142.     PutByte(cc);
  143.     return (dest);
  144. }
  145.  
  146. /* PackRow - pack a row of data using Amiga IFF RLE */
  147. int 
  148. PackRow(pSource, pDest, RowSize)
  149. unsigned char **pSource, **pDest;
  150. int             RowSize;
  151. {
  152.     unsigned char  *source, *dest;
  153.     char            c, lastc = '\000';
  154.     int             mode = DUMP, nbuf = 0,    /* number of chars in buf */
  155.                     rstart = 0;    /* buf index current run starts */
  156.  
  157.     source = *pSource;
  158.     dest = *pDest;
  159.  
  160.     PutSize = 0;
  161.     buf[0] = lastc = c = GetByte();
  162.     nbuf = 1;
  163.     RowSize--;
  164.  
  165.     for (; RowSize; --RowSize) {
  166.     buf[nbuf++] = c = GetByte();
  167.  
  168.     switch (mode) {
  169.     case DUMP:
  170.         if (nbuf > MaxDat) {
  171.         OutDump(nbuf - 1);
  172.         buf[0] = c;
  173.         nbuf = 1;
  174.         rstart = 0;
  175.         break;
  176.         }
  177.         if (c == lastc) {
  178.         if (nbuf - rstart >= MinRun) {
  179.             if (rstart > 0)
  180.             OutDump(rstart);
  181.             mode = RUN;
  182.         }
  183.         else if (rstart == 0)
  184.             mode = RUN;
  185.         }
  186.         else
  187.         rstart = nbuf - 1;
  188.         break;
  189.  
  190.     case RUN:
  191.         if ((c != lastc) || (nbuf - rstart > MaxRun)) {
  192.         OutRun((nbuf - 1) - rstart, lastc);
  193.         buf[0] = c;
  194.         nbuf = 1;
  195.         rstart = 0;
  196.         mode = DUMP;
  197.         }
  198.         break;
  199.     }
  200.     lastc = c;
  201.     }
  202.     switch (mode) {
  203.     case DUMP:
  204.     OutDump(nbuf);
  205.     break;
  206.     case RUN:
  207.     OutRun(nbuf - rstart, lastc);
  208.     break;
  209.     }
  210.  
  211.     *pSource = source;
  212.     *pDest = dest;
  213.  
  214.     return (PutSize);
  215. }
  216. /******************* end of RKM RL encoding routines **********************/
  217.  
  218.  
  219. /* build_histogram - count frequency of 12bit colors in an FBM image */
  220. build_histogram(image)
  221. FBM *image;
  222. {
  223.     int i, j, t0, t1, gap, val, used;
  224.     unsigned char *rp, *gp, *bp;
  225.     int diff;
  226.  
  227.     /* initialize color statistics. */
  228.     for (i = 0; i < 4096; i++) {
  229.     colorstats[i][0] = i;
  230.     colorstats[i][1] = 0;
  231.     }
  232.  
  233.     /* obtain pointers to the beginning of each plane (Red, Green, Blue) */
  234.     rp = image->bm;
  235.     gp = rp + image->hdr.plnlen;
  236.     bp = gp + image->hdr.plnlen;
  237.  
  238.     /* count the number of occurences of each color in the image */
  239.     for (i = 0 ; i < image->hdr.plnlen ; i++) {
  240.     val = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
  241.     val %= 4096;
  242.     if (colorstats[val][1] < 32767) {
  243.         colorstats[val][1]++;
  244.     }
  245.     }
  246.  
  247.     /* sort the color stats in order of decreasing usage */
  248.     for (gap = 2048; gap > 0; gap /= 2) {
  249.     for (i = gap; i < 4096; i++) {
  250.         for (j = i - gap; j >= 0 &&
  251.             colorstats[j][1] < colorstats[j + gap][1]; j -= gap) {
  252.         t0 = colorstats[j][0];
  253.         t1 = colorstats[j][1];
  254.         colorstats[j][0] = colorstats[j + gap][0];
  255.         colorstats[j][1] = colorstats[j + gap][1];
  256.         colorstats[j + gap][0] = t0;
  257.         colorstats[j + gap][1] = t1;
  258.         }
  259.     }
  260.     }
  261.  
  262.     /* count the number of colors actually used in the image */
  263.     for (used = 0; used < 4096 && colorstats[used][1] > 0; used++);
  264.     fprintf(stderr, "Used %d colors out of a possible 4096\n", used);
  265. }
  266.  
  267. /*-
  268.  * plot_pixel - plot a color in an Amiga style bitmap (one plane per bit)
  269.  *
  270.  * Description:
  271.  *    A somewhat optimized routine to set/reset one bit in each plane
  272.  *    at the specified (x,y) coordinates. plane i gets the bit in
  273.  *    position i of color. a replacement for the Amiga WritePixel call.
  274. -*/
  275. plot_pixel(planes, x, y, color)
  276. unsigned char *planes[];
  277. int x, y;
  278. register int color;
  279. {
  280.     register int bit;
  281.     register unsigned char shifted_bit = 1 << (7-(x%8));
  282.     register int array_offset = y * row_size + x/8;
  283.     register int i;
  284.  
  285.     for (i = 0; color && i < depth; i++) {
  286.     bit = color & 1;
  287.     color >>= 1;
  288.  
  289.     if (bit)    *(planes[i] + array_offset) |= shifted_bit;
  290.     }
  291. }
  292.  
  293.  
  294. /*-
  295.  * fbm2ham - write an FBM image in HAM IFF format on the given file pointer.
  296.  *
  297. -*/
  298. fbm2ham(image, ofil)
  299. FBM *image;
  300. FILE *ofil;
  301. {
  302.     int             i, j, k, gap, t0, t1, prgb, crgb, cpix, ppix, maxdis, used;
  303.     int            c1, c2, diff;
  304.     unsigned char *rp, *gp, *bp;
  305.  
  306.     fprintf(stderr, "Building a color histogram:\n");
  307.     build_histogram(image);
  308.  
  309.     /* convert the image to an Amiga HAM bitplane based image */
  310.     cpix = 0;
  311.     crgb = colorstats[0][1];
  312.  
  313.     rp = image->bm;
  314.     gp = rp + image->hdr.plnlen;
  315.     bp = gp + image->hdr.plnlen;
  316.  
  317.     row_size = ((image->hdr.cols + 15) / 16) * 2;
  318.     diff = image->hdr.rowlen - image->hdr.cols;
  319.  
  320.     for (i = 0; i < depth ; i++) {
  321.     planes[i] = (unsigned char *)malloc(row_size * image->hdr.rows);
  322.     }
  323.  
  324.     for (i = 0; i < image->hdr.rows; i ++) {
  325.     /* verbose output because this program is slow even on a Sun */
  326.     if (i%50 == 0) { fprintf(stderr, "...%d", i); fflush(stderr); }
  327.  
  328.     /* step through current row, converting FBM pixel to nearest equivalent
  329.      * HAM pixel */
  330.     for (j = 0; j < image->hdr.cols; j++) {
  331.         prgb = crgb;
  332.         crgb = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
  333.         crgb %= 4096;
  334.         ppix = cpix;
  335.  
  336.         /* start of scan line is ALWAYS an absolute color */
  337.         if (j == 0)
  338.         cpix = getcolor(ppix, &crgb, -1);
  339.         else
  340.         cpix = getcolor(ppix, &crgb, prgb);
  341.  
  342.         /* plot the computed pixel */
  343.         plot_pixel(planes, j, i, cpix);
  344.     }
  345.     rp += diff;
  346.     gp += diff;
  347.     bp += diff;
  348.     }
  349.     fprintf(stderr, "\n");
  350.  
  351.     /* Now we write the planes[] array we just created in ILBM format */
  352.     sprintf(str, "FORM");
  353.     fwrite(str, 1, 4, ofil);
  354.     pos1 = ftell(ofil);
  355.     lng = FORMsize;
  356.     fwrite(&lng, 4, 1, ofil);
  357.     sprintf(str, "ILBM");
  358.     fwrite(str, 1, 4, ofil);
  359.  
  360.     sprintf(str, "BMHD");
  361.     fwrite(str, 1, 4, ofil);
  362.     lng = BMHDsize;
  363.     fwrite(&lng, 4, 1, ofil);
  364.     wrd = image->hdr.cols;
  365.     fwrite(&wrd, 2, 1, ofil);    /* width */
  366.     wrd = image->hdr.rows;
  367.     fwrite(&wrd, 2, 1, ofil);    /* height */
  368.     wrd = 0;
  369.     fwrite(&wrd, 2, 1, ofil);    /* top */
  370.     wrd = 0;
  371.     fwrite(&wrd, 2, 1, ofil);    /* left */
  372.     byt = depth;
  373.     fwrite(&byt, 1, 1, ofil);    /* Depth */
  374.     byt = 0;
  375.     fwrite(&byt, 1, 1, ofil);    /* mask */
  376.     byt = comp;
  377.     fwrite(&byt, 1, 1, ofil);    /* compress */
  378.     byt = 0;
  379.     fwrite(&byt, 1, 1, ofil);    /* pad */
  380.     wrd = 0;
  381.     fwrite(&wrd, 2, 1, ofil);    /* transparency */
  382.     byt = 10;
  383.     fwrite(&byt, 1, 1, ofil);    /* aspect x */
  384.     byt = 11;
  385.     fwrite(&byt, 1, 1, ofil);    /* aspect y */
  386.     wrd = image->hdr.cols;
  387.     fwrite(&wrd, 2, 1, ofil);    /* page width */
  388.     wrd = image->hdr.rows;
  389.     fwrite(&wrd, 2, 1, ofil);    /* page height */
  390.  
  391.     /* CAMG chunk for displaying files on the Amiga. should include at least
  392.      * the HAM flag; I also add lace if the picture is large enough. Perhaps
  393.      * this should be an option. -CHK */
  394.     sprintf(str, "CAMG");
  395.     fwrite(str, 1, 4, ofil);
  396.     lng = CAMGsize;
  397.     fwrite(&lng, 4, 1, ofil);
  398.     if (image->hdr.rows > 200)
  399.     lng = 0x804L;        /* HAM | LACE */
  400.     else
  401.     lng = 0x800L;        /* HAM */
  402.     fwrite(&lng, 4, 1, ofil);
  403.  
  404.     /* write out the color lookup table */
  405.     sprintf(str, "CMAP");
  406.     fwrite(str, 1, 4, ofil);
  407.     lng = CMAPsize;
  408.     fwrite(&lng, 4, 1, ofil);
  409.     for (i = 0; i < 32; i++) {
  410.     str[0] = (colorstats[i][0] >> 4) & 0xF0;
  411.     str[1] = (colorstats[i][0]) & 0xF0;
  412.     str[2] = (colorstats[i][0] << 4) & 0xF0;
  413.     fwrite(str, 1, 3, ofil);
  414.     }
  415.  
  416.     sprintf(str, "BODY");
  417.     fwrite(str, 1, 4, ofil);
  418.     pos2 = ftell(ofil);
  419.     lng = BODYsize;
  420.     fwrite(&lng, 4, 1, ofil);
  421.  
  422.     lng = 0L;
  423.  
  424.     for (i = 0; i < image->hdr.rows; i ++) {
  425.     for (j = 0; j < depth; j++) {
  426.         if (comp) {
  427.         dest = destbuf;
  428.         wrd = PackRow(&planes[j], &dest, row_size);
  429.         lng += (long) wrd;
  430.         fwrite(destbuf, 1, wrd, ofil);
  431.         }
  432.         else {
  433.         fwrite(planes[j], 1, row_size, ofil);
  434.         planes[j] += row_size;
  435.         }
  436.     }
  437.     }
  438.     if (comp) {
  439.     fseek(ofil, (long) pos2, 0);
  440.     fwrite(&lng, 4, 1, ofil);
  441.     lng -= BODYsize;
  442.     lng += FORMsize;
  443.     fseek(ofil, (long) pos1, 0);
  444.     fwrite(&lng, 4, 1, ofil);
  445.     }
  446.  
  447.     return 1;
  448. }
  449.  
  450. /* get the next encoding for a pixel, based on the previous pixel and the
  451.  * current 12 bit RGB value */
  452.  
  453. #define BPP        4        /* Bits per pixel */
  454. #define MAXGRAY        (1 << BPP)
  455. #define    ABS(x)        ((x) < 0 ? -(x) : (x))
  456.  
  457. /*-
  458.  *    first, check to see if pixel is the same color. if so, return
  459.  *    check if only one of R, G, B have changed. if so, return new pixel.
  460.  *    find closest entry in colormap. if exact match, return it.
  461.  *    modify one of R, G, B (whichever difference is largest)
  462.  *    compare previous calculation to best match in color table. return
  463.  *        whichever is a better match.
  464. -*/
  465. getcolor(ppix, crgb, prgb)
  466. int             ppix, *crgb, prgb;
  467. {
  468.     int             i, j, val, cr, cg, cb, pr, pg, pb, nr, ng, nb, best, dist, nrgb;
  469.  
  470.     /* if same color, then do a NOOP (same as previous pixel) */
  471.     if (*crgb == prgb)
  472.     return (ppix);
  473.  
  474.     /* set up for comparisons */
  475.     cb = *crgb & (MAXGRAY - 1);
  476.     cg = (*crgb >> BPP) & (MAXGRAY - 1);
  477.     cr = (*crgb >> (BPP * 2)) & (MAXGRAY - 1);
  478.  
  479.     pb = prgb & (MAXGRAY - 1);
  480.     pg = (prgb >> BPP) & (MAXGRAY - 1);
  481.     pr = (prgb >> (BPP * 2)) & (MAXGRAY - 1);
  482.  
  483.     /* see if only one plane changed, if so, use HAM encoding */
  484.     if (prgb != -1) {
  485.     if (pr == cr && pg == cg)
  486.         return (cb + 0x10);
  487.     if (pr == cr && pb == cb)
  488.         return (cg + 0x30);
  489.     if (pg == cg && pb == cb)
  490.         return (cr + 0x20);
  491.     }
  492.  
  493.     /* else look for an exact match in the color table (or minimal distance) */
  494.     for (i = 0; i < colors; i++) {
  495.     if (colorstats[i][0] == *crgb)
  496.         return (i);
  497.     if (i == 0) {
  498.         best = 0;
  499.         dist = distance(colorstats[i][0], *crgb);
  500.     }
  501.     else if ((j = distance(colorstats[i][0], *crgb)) < dist) {
  502.         best = i;
  503.         dist = j;
  504.     }
  505.     }
  506.  
  507.     /* do a forced absolute */
  508.     if (prgb == -1) {
  509.     *crgb = colorstats[best][0];
  510.     return (best);
  511.     }
  512.  
  513.     /* find which color is off the most from previous */
  514.     i = 0;
  515.     val = ABS(cr - pr);
  516.     if (ABS(cg - pg) > val) {
  517.     i = 1;
  518.     val = ABS(cg - pg);
  519.     }
  520.     if (ABS(cb - pb) > val) {
  521.     i = 2;
  522.     val = ABS(cb - pb);
  523.     }
  524.  
  525.     nr = pr;
  526.     ng = pg;
  527.     nb = pb;
  528.     switch (i) {
  529.     case 0:
  530.     nr = cr;
  531.     val = nr + 0x20;
  532.     break;
  533.     case 1:
  534.     ng = cg;
  535.     val = ng + 0x30;
  536.     break;
  537.     case 2:
  538.     nb = cb;
  539.     val = nb + 0x10;
  540.     break;
  541.     }
  542.     nrgb = (nr << (2 * BPP)) + (ng << BPP) + nb;
  543.  
  544.     /* now pick the best */
  545.     if (distance(nrgb, *crgb) < dist) {
  546.  
  547.     /* do a best relative */
  548.     *crgb = nrgb;
  549.     return (val);
  550.     }
  551.  
  552.     /* do a best absolute */
  553.     *crgb = colorstats[best][0];
  554.     return (best);
  555. }
  556.  
  557. /* calculate distance between two colors in 3D space */
  558. distance(argb, brgb)
  559. {
  560.     int             b, g, r;
  561.  
  562.     /* set up for comparisons */
  563.     b = argb & (MAXGRAY - 1);
  564.     g = (argb >> BPP) & (MAXGRAY - 1);
  565.     r = (argb >> (BPP * 2)) & (MAXGRAY - 1);
  566.  
  567.     b -= brgb & (MAXGRAY - 1);
  568.     g -= (brgb >> BPP) & (MAXGRAY - 1);
  569.     r -= (brgb >> (BPP * 2)) & (MAXGRAY - 1);
  570.  
  571.     return (r * r + g * g + b * b);
  572. }
  573.