home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Graphics / ToyViewer-2.6a / src / ImageOpr.bproj / Posterizer.m < prev    next >
Encoding:
Text File  |  1996-12-07  |  7.2 KB  |  338 lines

  1. #import  "Posterizer.h"
  2. #import  <appkit/TextField.h>
  3. #import  <stdio.h>
  4. #import  <stdlib.h>
  5. #import  <string.h>
  6. #import "../common.h"
  7. #import "../getpixel.h"
  8. #import "imageOperation.h"
  9.  
  10. #define  CellMAX(n)    ((n) >> 2)
  11. #define  DiffHUGE    (256 * 256 * 3)
  12. #define  AvrMAX        1000
  13. #define  AlphaMark    255
  14.  
  15. typedef  int    area_t;            /* 32 bits */
  16. typedef  unsigned char    brite_t;
  17.  
  18. typedef struct _area_cell {
  19.     long    rgb[3];
  20.     long    count;
  21.     long    alias;
  22. } area_cell;
  23.  
  24.  
  25. @implementation Posterizer
  26.  
  27. static commonInfo *cinf;
  28. static int cnum;
  29. static int cell_max;
  30. static unsigned char *planes[MAXPLANE];    /* faster than **planes */
  31. /* work memory */
  32. static area_t *area;
  33. static brite_t *brite;
  34. static area_cell *acell;
  35.  
  36.  
  37. static int alink(int idx)
  38. {
  39.     int    i, x;
  40.     area_cell    *ptr;
  41.  
  42.     for (x = idx; x < cell_max && acell[x].count == 0; x = acell[x].alias)
  43.         ;
  44.     for (i = idx; i != x; ) {
  45.         i = (ptr = &acell[i])->alias;
  46.         ptr->alias = x;
  47.     }
  48.     return x;
  49. }
  50.  
  51. - initWith:(commonInfo *)info newmap:(unsigned char **)newmap
  52. {
  53.     long    allpix;
  54.     int    i, pnum;
  55.  
  56.     cinf = info;
  57.     area = NULL;
  58.     brite = NULL;
  59.     acell = NULL;
  60.     pnum = cnum = cinf->numcolors;
  61.     if (cinf->alpha) pnum++;
  62.     if (allocImage(newmap, cinf->width, cinf->height, 8, pnum))
  63.         return nil;
  64.     for (i = 0; i < MAXPLANE; i++)
  65.         planes[i] = newmap[i];
  66.     allpix = cinf->width * cinf->height;
  67.     cell_max = CellMAX(allpix);
  68.     acell = (area_cell *)malloc(cell_max * sizeof(area_cell));
  69.     area = (area_t *)malloc(allpix * sizeof(area_t));
  70.     brite = (brite_t *)malloc(allpix * sizeof(brite_t));
  71.     if (area == NULL || acell == NULL || brite == NULL)
  72.         return nil;
  73.     msgtext = nil;
  74.     return self;
  75. }
  76.  
  77. - free
  78. {
  79.     if (area) {
  80.         free((void *)area);
  81.         area = NULL;
  82.     }
  83.     if (acell) {
  84.         free((void *)acell);
  85.         acell = NULL;
  86.     }
  87.     if (brite) {
  88.         free((void *)brite);
  89.         brite = NULL;
  90.     }
  91.     [super free];
  92.     return nil;
  93. }
  94.  
  95. - setMessageText: text
  96. {
  97.     msgtext = text;
  98.     return self;
  99. }
  100.  
  101. static int distance(int r1, int g1, int b1, int r2, int g2, int b2)
  102. {
  103.     int    r = r1 - r2;
  104.     int    g = g1 - g2;
  105.     int    b = b1 - b2;
  106.  
  107.     return ((r * r + g * g + b * b) / 3);
  108. }
  109.  
  110. static void smooth(int brdiff)
  111. {
  112.     int    tim, i, j, x, y, idx;
  113.     int    cnt, val, sum, d;
  114.     int    index[8];
  115.     index[0] = - cinf->width - 1;
  116.     index[1] = - cinf->width;
  117.     index[2] = - cinf->width + 1;
  118.     index[3] = -1;
  119.     index[4] = 1;
  120.     index[5] = cinf->width - 1;
  121.     index[6] = cinf->width;
  122.     index[7] = cinf->width + 1;
  123.  
  124.     for (tim = 0; tim < 2; tim++)
  125.         for (y = 1; y < cinf->height - 1; y++) {
  126.             idx = y * cinf->width + 1;
  127.             for (x = 1; x < cinf->width - 1; x++, idx++) {
  128.             cnt = 1;
  129.             sum = val = brite[idx];
  130.             if (val == AlphaMark)
  131.                 continue;
  132.             for (i = 0; i < 8; i++) {
  133.                 if ((j = brite[idx + index[i]]) == AlphaMark)
  134.                     continue;
  135.                 d = j - val;
  136.                 if (d > - brdiff && d < brdiff)
  137.                     sum += j, cnt++;
  138.             }
  139.             if (cnt > 1)
  140.                 brite[idx] = sum / cnt;
  141.             }
  142.         }
  143. }
  144.  
  145. static int neighbor(int nx, int idx, int phase)
  146. {
  147.     int    v, n, ap;
  148.     int    va, vb;
  149.     long    *lp;
  150.     area_cell *aptr;
  151.  
  152.     if (cinf->alpha &&
  153.         (planes[cnum][nx] == AlphaTransp
  154.             || planes[cnum][idx] == AlphaTransp))
  155.     if (phase == 1 && area[nx] == area[idx])
  156.         return DiffHUGE;
  157.     if (cnum > 1) {
  158.         va = distance(planes[0][nx], planes[1][nx], planes[2][nx],
  159.             planes[0][idx], planes[1][idx], planes[2][idx]);
  160.     }else {
  161.         v = (int)planes[0][nx] - (int)planes[0][idx];
  162.         va = v * v;
  163.     }
  164.  
  165.     if ((ap = area[nx]) >= cell_max) /* Alpha */
  166.         return DiffHUGE;
  167.     aptr = &acell[alink(ap)];
  168.     lp = aptr->rgb;
  169.     if (phase == 0 && (n = aptr->count) > 1) {
  170.         if (cnum > 1) {
  171.         vb = distance(lp[0] / n, lp[1] / n, lp[2] / n,
  172.             planes[0][idx], planes[1][idx], planes[2][idx]);
  173.         }else {
  174.         v = lp[0] / n - (int)planes[0][idx];
  175.         vb = v * v;
  176.         }
  177.     }else {
  178.         if (cnum > 1) {
  179.         vb = distance(lp[0], lp[1], lp[2],
  180.             planes[0][idx], planes[1][idx], planes[2][idx]);
  181.         }else {
  182.         v = lp[0] - (int)planes[0][idx];
  183.         vb = v * v;
  184.         }
  185.     }
  186.  
  187.     return (vb > va) ? vb : va;
  188. }
  189.  
  190. /* Local Method */
  191. - (BOOL) segment: (int)brdiff : (int)diffc
  192. {
  193.     int    i, j, x, y, base, up, rp;
  194.     int    acp, idx, alp, scandir;
  195.     int    elm[MAXPLANE];
  196.     area_cell *aptr, *bptr;
  197.     long    *lp;
  198.     long    diffcolor, difx, dify;
  199.     unsigned char br[256];
  200.  
  201.     for (i = 0, j = 0; i < 256; ) {
  202.         br[i] = j;
  203.         if (++i > j) j += brdiff;
  204.     }
  205.     diffcolor = diffc * diffc;
  206.     alp = (cinf->alpha) ? cnum : 0;
  207.     acp = 0;
  208.     idx = 0;
  209.     for (y = 0; y < cinf->height; y++) {
  210.         for (x = 0; x < cinf->width; x++, idx++) {
  211.         getPixel(&elm[0], &elm[1], &elm[2], &elm[3]);
  212.         for (i = 0; i < cnum; i++)
  213.             planes[i][idx] = elm[i];
  214.         if (alp) {
  215.             planes[alp][idx] = elm[ALPHA];
  216.             if (elm[ALPHA] == AlphaTransp) {
  217.                 brite[idx] = AlphaMark;    /* 255 */
  218.                 continue;
  219.             }
  220.         }
  221.         j = (cnum == 1) ? elm[0] : Bright255(elm[0], elm[1], elm[2]);
  222.         brite[idx] = (j >= 255) ? 254 : j;
  223.         }
  224.     }
  225.     smooth(brdiff);
  226.  
  227.     idx = 0;
  228.     for (y = 0; y < cinf->height; y++)
  229.         for (x = 0; x < cinf->width; x++, idx++)
  230.         brite[idx] = br[brite[idx]];
  231.  
  232.     scandir = 1;
  233.     for (y = 0; y < cinf->height; y++) {
  234.         base = y * cinf->width;
  235.         for (x = (scandir > 0) ? 0 : (cinf->width - 1);
  236.             x >= 0 && x < cinf->width; x += scandir) {
  237.         idx = base + x;
  238.         if (alp && planes[alp][idx] == AlphaTransp) {
  239.             area[idx] = cell_max;    /* mark as ALPHA */
  240.             continue;
  241.         }
  242.         rp = ((j = x - scandir) < 0 || j >= cinf->width)
  243.             ? -1 : (idx - scandir);
  244.         up = (y == 0) ? -1 : (idx - cinf->width);
  245.         difx = (rp < 0 || brite[rp] != brite[idx])
  246.             ? DiffHUGE : neighbor(rp, idx, 0);
  247.         dify = (up < 0 || brite[up] != brite[idx])
  248.             ? DiffHUGE : neighbor(up, idx, 0);
  249.         if (difx > diffcolor && dify > diffcolor) {
  250.             /* isolated pixel */
  251.             aptr = &acell[area[idx] = acp];
  252.             lp = aptr->rgb;
  253.             for (i = 0; i < cnum; i++)
  254.                 lp[i] = planes[i][idx];
  255.             aptr->count = 1;
  256.             if (++acp >= cell_max) /* Too Many Colors */
  257.                 return NO;
  258.         }else {
  259.             if (difx <= diffcolor && dify <= diffcolor
  260.             && (i = alink(area[rp])) != (j = alink(area[up]))
  261.                 && neighbor(rp, up, 0) <= diffcolor) {
  262.                 aptr = &acell[i];
  263.                 bptr = &acell[j];
  264.                 bptr->count += aptr->count;
  265.                 aptr->count = 0;
  266.                 aptr->alias = j;
  267.                 for (i = 0; i < cnum; i++)
  268.                     bptr->rgb[i] += aptr->rgb[i];
  269.             }else
  270.                 j = alink(area[(difx < dify)? rp : up]);
  271.             area[idx] = j;
  272.             aptr = &acell[j];
  273.             if (aptr->count < AvrMAX) {
  274.                 lp = aptr->rgb;
  275.                 for (i = 0; i < cnum; i++)
  276.                     lp[i] += planes[i][idx];
  277.                 aptr->count++;
  278.             }
  279.         }
  280.         } /* x */
  281.         scandir = (scandir > 0) ? -1 : 1;
  282.     }
  283.  
  284.     for (j = 0; j < acp; j++) {
  285.         long n;
  286.         if ((n = (aptr = &acell[j])->count) > 1) {
  287.             lp = aptr->rgb;
  288.             for (i = 0; i < cnum; i++)
  289.                 lp[i] /= n;
  290.         }
  291.     }
  292.     return YES;
  293. }
  294.  
  295.  
  296. - posterize:(unsigned char **)map with:(float)diff : (float)ctrl
  297. {
  298.     int    i, x, y, idx;
  299.     long    *lp;
  300.     int    diffc, brdiff;
  301.     area_cell *aptr;
  302.     float    w;
  303.  
  304.     w = 0.25 + (diff * 0.75);
  305.     for (w *= w; ; w += 0.05) {
  306.         brdiff = w * 64;
  307.         diffc = brdiff * (0.1 + (ctrl * 0.9));
  308.         if (diffc < 4) diffc = 4;
  309.         if (msgtext) {
  310.             char msgbuf[80];
  311.             sprintf(msgbuf, "Posterizing: %d:%d", brdiff, diffc);
  312.             [msgtext setStringValue: msgbuf];
  313.             NXPing();
  314.         }
  315.         resetPixel(map, 0);
  316.         if ([self segment: brdiff : diffc]) break;
  317.     }
  318.     if (msgtext) {
  319.         [msgtext setStringValue: "Painting..."];
  320.         NXPing();
  321.     }
  322.  
  323.     idx = 0;
  324.     for (y = 0; y < cinf->height; y++) {
  325.         for (x = 0; x < cinf->width; x++, idx++) {
  326.         aptr = &acell[alink(area[idx])];
  327.         if (aptr->count > 1) {
  328.             lp = aptr->rgb;
  329.             for (i = 0; i < cnum; i++)
  330.                 planes[i][idx] = lp[i];
  331.         }
  332.         }
  333.     }
  334.     return self;
  335. }
  336.  
  337. @end
  338.