home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #2 / amigaacscoverdisc1998-021998.iso / games / doom / adoom / src / amiga_median.c < prev    next >
C/C++ Source or Header  |  1997-12-29  |  7KB  |  240 lines

  1. /*****************************************************************************
  2.  
  3.         Flick FLI-format Animation Viewer v1.2          19 Feb 1994
  4.         --------------------------------------
  5.  
  6.  
  7. This program plays FLI/FLC-format bitmapped animation files on any ECS
  8. or AGA Amiga running OS2.04 or higher.  FLI/FLC-format files are
  9. produced by Autodesk Animator and Autodesk 3D Studio on a PC, as well
  10. as by other programs.
  11.  
  12. The files in this archive may be distributed anywhere provided they are
  13. unmodified and are not sold for profit.
  14.  
  15. Ownership and copyright of all files remains with the author:
  16.  
  17.     Peter McGavin, 86 Totara Crescent, Lower Hutt, New Zealand.
  18.     e-mail: peterm@maths.grace.cri.nz
  19.  
  20. *****************************************************************************/
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25.  
  26. #include <exec/types.h>
  27.  
  28. #include "i_system.h"
  29.  
  30. #define NBITS 4            /* for each r, g, b */
  31. #define NSIDE (1<<NBITS)
  32. #define NPALETTE 256      /* # of palette entries */
  33.  
  34. typedef enum rgb_type {R, G, B};
  35.  
  36. struct box_type {                  /* Defines a parallelopiped */
  37.   UBYTE start[3];
  38.   UBYTE end[3];
  39. };
  40.  
  41. struct node_type {
  42.   struct box_type box;        /* corners of the rectangular box */
  43.   UWORD n;            /* total number of pixels in box */
  44.   enum rgb_type long_primary;    /* longest direction (red, green or blue) */
  45.   ULONG badness;        /* spread of pixels within box */
  46.   UWORD primaryhist[3][NSIDE];    /* 3 histograms (one for each of R,G,B) */
  47.   UBYTE palette[3];        /* centre of gravity */
  48. };
  49.  
  50.  
  51. static UWORD hist[NSIDE][NSIDE][NSIDE];    /* 3D histogram (cube) */
  52.  
  53. static void update_entry (struct node_type *node)
  54. /* given a rectangular box of given dimensions (in node), calculate and
  55.    fill in all the other parts of the node. */
  56. {
  57.   UWORD r, g, b, i, mean;
  58.   ULONG c, wsum, bad;
  59.   LONG signed_diff;
  60.   enum rgb_type primary;
  61.  
  62.   /* build histogram for each primary */
  63.   memset (node->primaryhist, 0, 3 * NSIDE * sizeof(UWORD));
  64.   node->n = 0;
  65.   for (r = node->box.start[R]; r <= node->box.end[R]; r++)
  66.     for (g = node->box.start[G]; g <= node->box.end[G]; g++)
  67.       for (b = node->box.start[B]; b <= node->box.end[B]; b++) {
  68.         c = hist[r][g][b];
  69.         node->primaryhist[R][r] += c;
  70.         node->primaryhist[G][g] += c;
  71.         node->primaryhist[B][b] += c;
  72.         node->n += c;
  73.       }
  74.  
  75.   if (node->n == 0)
  76.     I_Error ("Empty box!  This should never happen.");
  77.  
  78.   /* shrink the box */
  79.   for (primary = R; primary <= B; primary++) {
  80.     i = node->box.start[primary];
  81.     while (node->primaryhist[primary][i] == 0)
  82.       i++;
  83.     node->box.start[primary] = i;
  84.     i = node->box.end[primary];
  85.     while (node->primaryhist[primary][i] == 0)
  86.       i--;
  87.     node->box.end[primary] = i;
  88.   }
  89.  
  90.   /* compute the badness & choose the primary with the greatest badness */
  91.   node->badness = 0;
  92.   for (primary = R; primary <= B; primary++) {
  93.     wsum = 0;
  94.     for (i = node->box.start[primary]; i <= node->box.end[primary]; i++)
  95.       wsum += (node->primaryhist[primary][i] * (ULONG)i);
  96.     mean = ((wsum << 1) + node->n) / (node->n << 1); /* round(wsum/node->n) */
  97.     node->palette[primary] = (UBYTE)(mean << 4);
  98.     bad = 0;
  99.     for (i = node->box.start[primary]; i <= node->box.end[primary]; i++) {
  100.       signed_diff = ((WORD)mean) - ((WORD)(i));
  101.       bad += node->primaryhist[primary][i] * (ULONG)(signed_diff * signed_diff);
  102.     }
  103.     if (bad >= node->badness) {
  104.       node->badness = bad;
  105.       node->long_primary = primary;
  106.     }
  107.   }
  108. }
  109.  
  110.  
  111. static void cut (struct node_type *node0, struct node_type *node1)
  112. /* cut node0 into 2 pieces in the node0->long_primary direction and store
  113.    the 2 pieces back into node0 and node1 */
  114. {
  115.   ULONG sum, goal;
  116.   UWORD split;
  117.  
  118.   if (node0->badness == 0)
  119.     I_Error ("Badness == 0!  This should never happen");
  120.  
  121.   /* find the median */
  122.   goal = node0->n >> 1;    /* how many we want on each side of the cut */
  123.   sum = 0;
  124.   split = node0->box.start[node0->long_primary];
  125.   while (sum <= goal)
  126.     sum += node0->primaryhist[node0->long_primary][split++];
  127.  
  128.   /* go back a bit if necessary to get a better balance */
  129.   if ((sum - goal) >
  130.       (goal - (sum - node0->primaryhist[node0->long_primary][split - 1])))
  131.     sum -= node0->primaryhist[node0->long_primary][--split];
  132.  
  133.   /* copy box */
  134.   node1->box = node0->box;
  135.  
  136.   /* set new dimensions of boxes */
  137.   node0->box.end[node0->long_primary] = split - 1;
  138.   node1->box.start[node0->long_primary] = split;
  139.  
  140.   /* update the nodes */
  141.   update_entry (node0);
  142.   update_entry (node1);
  143. }
  144.  
  145.  
  146. static struct node_type node[32];          /* node list */
  147.  
  148. void median_cut (UBYTE *palette, ULONG *table, UBYTE *xlate)
  149. {
  150.   UWORD idx, this_idx, worst_idx, c, r, g, b, best_idx, max_nodes;
  151.   WORD dr, dg, db;
  152.   ULONG max_badness, max_n, dist, best_dist, lr, lg, lb, *l;
  153.   enum rgb_type primary;
  154.   UBYTE *p;
  155.  
  156.   max_nodes = 32;    /* 32 colours for EHB */
  157.  
  158.   /* build histogram from palette (unlike conventional median split
  159.      algorithm which uses pixels from image, but that would be too slow) */
  160.   memset (hist, 0, NSIDE * NSIDE * NSIDE * sizeof(UWORD));
  161.   p = palette;
  162.   for (c = 0; c < NPALETTE; c++) {
  163.     r = *p++ >> 4;
  164.     g = *p++ >> 4;
  165.     b = *p++ >> 4;
  166.     if (r < 8 && g < 8 && b < 8)    /* EHB if possible */
  167.       hist[r << 1][g << 1][b << 1]++;
  168.     else
  169.       hist[r][g][b]++;
  170.   }
  171.  
  172.   /* set up an initial box containing the entire colour cube */
  173.   for (primary = R; primary <= B; primary++) {
  174.     node[0].box.start[primary] = 0;
  175.     node[0].box.end[primary] = NSIDE - 1;
  176.   }
  177.   update_entry (&node[0]);
  178.  
  179.   /* locate and subdivide the worst box until there are not enough
  180.      palette entries for more boxes or all the boxes are too small to
  181.      subdivide further */
  182.   for (this_idx = 1; this_idx < max_nodes; this_idx++) {
  183.     max_badness = 0;
  184.     max_n = 0;
  185.     for (idx = 0; idx < this_idx; idx++)
  186.       if (node[idx].badness > max_badness ||
  187.           (node[idx].badness == max_badness && node[idx].n > max_n)) {
  188.         max_badness = node[idx].badness;
  189.         max_n = node[idx].n;
  190.         worst_idx = idx;
  191.       }
  192.     if (max_badness == 0)
  193.       break;
  194.     cut (&node[worst_idx], &node[this_idx]);
  195.   }
  196.  
  197.   /* fill the output table */
  198.   l = table;
  199.   for (idx = 0; idx < this_idx; idx++) {
  200.     lr = node[idx].palette[R];
  201.     lr += (lr<<8);
  202.     lr += (lr<<16);
  203.     *l++ = lr;
  204.     lg = node[idx].palette[G];
  205.     lg += (lg<<8);
  206.     lg += (lg<<16);
  207.     *l++ = lg;
  208.     lb = node[idx].palette[B];
  209.     lb += (lb<<8);
  210.     lb += (lb<<16);
  211.     *l++ = lb;
  212.   }
  213.  
  214.   /* calculate pixel translation table */
  215.   p = palette;
  216.   for (c = 0; c < NPALETTE; c++) {
  217.     r = *p++;
  218.     g = *p++;
  219.     b = *p++;
  220.     best_dist = 32760;
  221.     for (idx = 0; idx < this_idx; idx++) {
  222.       dr = ((WORD)r) - (WORD)node[idx].palette[R];
  223.       dg = ((WORD)g) - (WORD)node[idx].palette[G];
  224.       db = ((WORD)b) - (WORD)node[idx].palette[B];
  225.       if ((dist = dr * dr + dg * dg + db * db) < best_dist) {
  226.         best_dist = dist;
  227.         best_idx = idx;
  228.       }
  229.       dr = ((WORD)r) - (WORD)(node[idx].palette[R] >> 1);
  230.       dg = ((WORD)g) - (WORD)(node[idx].palette[G] >> 1);
  231.       db = ((WORD)b) - (WORD)(node[idx].palette[B] >> 1);
  232.       if ((dist = dr * dr + dg * dg + db * db) < best_dist) {
  233.         best_dist = dist;
  234.         best_idx = idx + 32;    /* EHB colour entry */
  235.       }
  236.     }
  237.     xlate[c] = best_idx;
  238.   }
  239. }
  240.