home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume5 / xldimage / part02 / reduce.c next >
Encoding:
C/C++ Source or Header  |  1989-11-13  |  5.5 KB  |  195 lines

  1. /* reduce.c:
  2.  *
  3.  * reduce an image's colormap usage to a set number of colors.
  4.  *
  5.  * jim frost 07.06.89
  6.  *
  7.  * Copyright 1989 Jim Frost.  See included file "copyright.h" for complete
  8.  * copyright information.
  9.  */
  10.  
  11. #include "copyright.h"
  12. #include "image.h"
  13.  
  14. #define DIST(A, B) ((A) < (B) ? (B) - (A) : (A) - (B))
  15.  
  16. /* find the distance between two colors.  we loose some accuracy here because
  17.  * a triple squared short may not fit in a long.  we use a table lookup
  18.  * to help speed this up; it's an O(exp(n,2)) algorithm.
  19.  */
  20.  
  21. static unsigned int  squareInit= 0;
  22. static unsigned long squareTable[32768];
  23.  
  24. static void initSquareTable()
  25. { unsigned long a;
  26.  
  27.   for (a= 0; a < 32768; a++)
  28.     squareTable[a]= a * a;
  29.   squareInit= 1;
  30. }
  31.  
  32. static unsigned long colorDistance(rgb, a, b)
  33.      RGBMap *rgb;
  34.      Pixel   a, b;
  35. {
  36.   return(squareTable[DIST(*(rgb->red + a), *(rgb->red + b)) >> 1] +
  37.      squareTable[DIST(*(rgb->green + a), *(rgb->green + b)) >> 1] +
  38.      squareTable[DIST(*(rgb->blue + a), *(rgb->blue + b)) >> 1]);
  39. }
  40.  
  41. Pixel bestColor(rgb, color, rdist)
  42.      RGBMap        *rgb;
  43.      Pixel          color;
  44.      unsigned long *rdist;
  45. { Pixel         qcolor, bcolor;
  46.   unsigned long qdist, bdist;
  47.  
  48.   bdist= 0xffffffff;
  49.   for (qcolor= color + 1; qcolor < rgb->used; qcolor++)
  50.     if ((qdist= colorDistance(rgb, color, qcolor)) && (qdist < bdist)) {
  51.       bdist= qdist;
  52.       bcolor= qcolor;
  53.     }
  54.   *rdist= bdist;
  55.   return(bcolor);
  56. }
  57.  
  58. void reduceRGBMap(rgb, n, verbose)
  59.      RGBMap *rgb;
  60.      int     verbose;
  61. { unsigned int   numcolors;
  62.   Pixel          a, b;
  63.   Pixel          lowextreme, highextreme; /* intensity extremes */
  64.   unsigned long  lowintensity, highintensity, myintensity;
  65.   Pixel          bcolor1; /* closest colors */
  66.   Pixel          bcolor2;
  67.   unsigned long  bdist;
  68.   Pixel         *best;    /* array holding best match for each color */
  69.   unsigned long *dists;   /* array holding distances of best matches */
  70.   Pixel         *same;    /* array holding identical pixel lists */
  71.   Intensity      newred, newgreen, newblue;
  72.  
  73.   if (!squareInit)     /* only do multiplies once */
  74.     initSquareTable();
  75.  
  76.   if (verbose) {
  77.     printf("  Reducing colormap to %d colors...", n);
  78.     fflush(stdout);
  79.   }
  80.  
  81.   best= (Pixel *)lmalloc(sizeof(Pixel) * rgb->used);
  82.   same= (Pixel *)lmalloc(sizeof(Pixel) * rgb->used);
  83.   dists= (unsigned long *)lmalloc(sizeof(unsigned long) * rgb->used);
  84.  
  85.   /* find out how many unique colors we have by subtracting identical ones
  86.    * and build table of identicals.  find extreme intensities while we're
  87.    * at it.
  88.    */
  89.  
  90.   lowextreme= highextreme= rgb->used - 1;
  91.   lowintensity= highintensity= *(rgb->red + lowextreme) +
  92.      *(rgb->green + lowextreme) + *(rgb->blue + lowextreme);
  93.   for (a= 0; a < rgb->used; a++)
  94.     *(same + a)= a;
  95.   for (numcolors= rgb->used, a= 0; a < rgb->used - 1; a++) {
  96.     if (*(same + a) == a) {
  97.       myintensity= *(rgb->red + a) + *(rgb->green + a) + *(rgb->blue + a);
  98.       if (myintensity < lowintensity) {
  99.     lowintensity= myintensity;
  100.     lowextreme= a;
  101.       }
  102.       if (myintensity > highintensity) {
  103.     highintensity= myintensity;
  104.     highextreme= a;
  105.       }
  106.       for (b= a + 1; b < rgb->used; b++) {
  107.     if ((*(rgb->red + a) == *(rgb->red + b)) &&
  108.         (*(rgb->green + a) == *(rgb->green + b)) &&
  109.         (*(rgb->blue + a) == *(rgb->blue + b))) {
  110.       numcolors--;
  111.       *(same + b)= a;
  112.     }
  113.       }
  114.     }
  115.   }
  116.  
  117.   for (a= 0; a < rgb->used - 1; a++) /* build table of "bests" */
  118.     *(best + a)= bestColor(rgb, a, dists + a);
  119.  
  120.   /* find the two closest colors in the colormap and average them, thus
  121.    * reducing the size of the colormap by one.  continue until we fit.
  122.    * this is simplistic but effective.
  123.    */
  124.  
  125.   while (numcolors-- > n) {
  126.     bdist= 0xffffffff; /* a really big number */
  127.     for (a= 0; a < rgb->used - 1; a++)
  128.       if ((*(same + a) == a) && (*(dists + a) < bdist)) {
  129.     bdist= *(dists + a);
  130.     bcolor1= a;
  131.       }
  132.  
  133.     bcolor2= *(same + *(best + bcolor1));
  134.  
  135.     /* calculate new rgb values.  we average the colors unless one of them
  136.      * is an extreme.
  137.      */
  138.  
  139.     if ((bcolor1 == lowextreme) || (bcolor1 == highextreme)) {
  140.       newred= *(rgb->red + bcolor1);
  141.       newgreen= *(rgb->green + bcolor1);
  142.       newblue= *(rgb->blue + bcolor1);
  143.     }
  144.     else if ((bcolor2 == lowextreme) || (bcolor2 == highextreme)) {
  145.       newred= *(rgb->red + bcolor2);
  146.       newgreen= *(rgb->green + bcolor2);
  147.       newblue= *(rgb->blue + bcolor2);
  148.     }
  149.     else {
  150.       newred= ((unsigned int)(*(rgb->red + bcolor1)) +
  151.            (unsigned int)(*(rgb->red + bcolor2))) >> 1;
  152.       newgreen= ((unsigned int)(*(rgb->green + bcolor1)) +
  153.          (unsigned int)(*(rgb->green + bcolor2))) >> 1;
  154.       newblue= ((unsigned int)(*(rgb->blue + bcolor1)) +
  155.         (unsigned int)(*(rgb->blue + bcolor2))) >> 1;
  156.     }
  157.  
  158.     for (a= 0; a < rgb->used; a++)
  159.       if ((*(same + a) == bcolor1) || (*(same + a) == bcolor2)) {
  160.     *(same + a)= bcolor1;
  161.         *(rgb->red + a)= newred;
  162.         *(rgb->green + a)= newgreen;
  163.         *(rgb->blue + a)= newblue;
  164.       }
  165.     
  166.     for (a= 0; a < rgb->used - 1; a++)
  167.       if ((*(best + a) == bcolor1) || (*(same + a) == bcolor1) ||
  168.       (*(same + *(best + a)) == bcolor1))
  169.     *(best + a)= bestColor(rgb, a, dists + a);
  170.   }
  171.  
  172.   lfree(best);
  173.   lfree(dists);
  174.   lfree(same);
  175.  
  176.   if (verbose)
  177.     printf("done\n");
  178. }
  179.  
  180. void reduce(image, n, verbose)
  181.      Image        *image;
  182.      unsigned int  n, verbose;
  183. { char buf[BUFSIZ];
  184.  
  185.   goodImage(image);
  186.   if (! RGBP(image)) /* we're AT&T */
  187.     return;
  188.   compress(image, verbose);
  189.   reduceRGBMap(&(image->rgb), n, verbose);
  190.   compress(image, verbose);
  191.   sprintf(buf, "%s (%d colors)", image->title, image->rgb.used);
  192.   lfree(image->title);
  193.   image->title= dupString(buf);
  194. }
  195.