home *** CD-ROM | disk | FTP | other *** search
- #import "Posterizer.h"
- #import <appkit/TextField.h>
- #import <stdio.h>
- #import <stdlib.h>
- #import <string.h>
- #import "../common.h"
- #import "../getpixel.h"
- #import "imageOperation.h"
-
- #define CellMAX(n) ((n) >> 2)
- #define DiffHUGE (256 * 256 * 3)
- #define AvrMAX 1000
- #define AlphaMark 255
-
- typedef int area_t; /* 32 bits */
- typedef unsigned char brite_t;
-
- typedef struct _area_cell {
- long rgb[3];
- long count;
- long alias;
- } area_cell;
-
-
- @implementation Posterizer
-
- static commonInfo *cinf;
- static int cnum;
- static int cell_max;
- static unsigned char *planes[MAXPLANE]; /* faster than **planes */
- /* work memory */
- static area_t *area;
- static brite_t *brite;
- static area_cell *acell;
-
-
- static int alink(int idx)
- {
- int i, x;
- area_cell *ptr;
-
- for (x = idx; x < cell_max && acell[x].count == 0; x = acell[x].alias)
- ;
- for (i = idx; i != x; ) {
- i = (ptr = &acell[i])->alias;
- ptr->alias = x;
- }
- return x;
- }
-
- - initWith:(commonInfo *)info newmap:(unsigned char **)newmap
- {
- long allpix;
- int i, pnum;
-
- cinf = info;
- area = NULL;
- brite = NULL;
- acell = NULL;
- pnum = cnum = cinf->numcolors;
- if (cinf->alpha) pnum++;
- if (allocImage(newmap, cinf->width, cinf->height, 8, pnum))
- return nil;
- for (i = 0; i < MAXPLANE; i++)
- planes[i] = newmap[i];
- allpix = cinf->width * cinf->height;
- cell_max = CellMAX(allpix);
- acell = (area_cell *)malloc(cell_max * sizeof(area_cell));
- area = (area_t *)malloc(allpix * sizeof(area_t));
- brite = (brite_t *)malloc(allpix * sizeof(brite_t));
- if (area == NULL || acell == NULL || brite == NULL)
- return nil;
- msgtext = nil;
- return self;
- }
-
- - free
- {
- if (area) {
- free((void *)area);
- area = NULL;
- }
- if (acell) {
- free((void *)acell);
- acell = NULL;
- }
- if (brite) {
- free((void *)brite);
- brite = NULL;
- }
- [super free];
- return nil;
- }
-
- - setMessageText: text
- {
- msgtext = text;
- return self;
- }
-
- static int distance(int r1, int g1, int b1, int r2, int g2, int b2)
- {
- int r = r1 - r2;
- int g = g1 - g2;
- int b = b1 - b2;
-
- return ((r * r + g * g + b * b) / 3);
- }
-
- static void smooth(int brdiff)
- {
- int tim, i, j, x, y, idx;
- int cnt, val, sum, d;
- int index[8];
- index[0] = - cinf->width - 1;
- index[1] = - cinf->width;
- index[2] = - cinf->width + 1;
- index[3] = -1;
- index[4] = 1;
- index[5] = cinf->width - 1;
- index[6] = cinf->width;
- index[7] = cinf->width + 1;
-
- for (tim = 0; tim < 2; tim++)
- for (y = 1; y < cinf->height - 1; y++) {
- idx = y * cinf->width + 1;
- for (x = 1; x < cinf->width - 1; x++, idx++) {
- cnt = 1;
- sum = val = brite[idx];
- if (val == AlphaMark)
- continue;
- for (i = 0; i < 8; i++) {
- if ((j = brite[idx + index[i]]) == AlphaMark)
- continue;
- d = j - val;
- if (d > - brdiff && d < brdiff)
- sum += j, cnt++;
- }
- if (cnt > 1)
- brite[idx] = sum / cnt;
- }
- }
- }
-
- static int neighbor(int nx, int idx, int phase)
- {
- int v, n, ap;
- int va, vb;
- long *lp;
- area_cell *aptr;
-
- if (cinf->alpha &&
- (planes[cnum][nx] == AlphaTransp
- || planes[cnum][idx] == AlphaTransp))
- if (phase == 1 && area[nx] == area[idx])
- return DiffHUGE;
- if (cnum > 1) {
- va = distance(planes[0][nx], planes[1][nx], planes[2][nx],
- planes[0][idx], planes[1][idx], planes[2][idx]);
- }else {
- v = (int)planes[0][nx] - (int)planes[0][idx];
- va = v * v;
- }
-
- if ((ap = area[nx]) >= cell_max) /* Alpha */
- return DiffHUGE;
- aptr = &acell[alink(ap)];
- lp = aptr->rgb;
- if (phase == 0 && (n = aptr->count) > 1) {
- if (cnum > 1) {
- vb = distance(lp[0] / n, lp[1] / n, lp[2] / n,
- planes[0][idx], planes[1][idx], planes[2][idx]);
- }else {
- v = lp[0] / n - (int)planes[0][idx];
- vb = v * v;
- }
- }else {
- if (cnum > 1) {
- vb = distance(lp[0], lp[1], lp[2],
- planes[0][idx], planes[1][idx], planes[2][idx]);
- }else {
- v = lp[0] - (int)planes[0][idx];
- vb = v * v;
- }
- }
-
- return (vb > va) ? vb : va;
- }
-
- /* Local Method */
- - (BOOL) segment: (int)brdiff : (int)diffc
- {
- int i, j, x, y, base, up, rp;
- int acp, idx, alp, scandir;
- int elm[MAXPLANE];
- area_cell *aptr, *bptr;
- long *lp;
- long diffcolor, difx, dify;
- unsigned char br[256];
-
- for (i = 0, j = 0; i < 256; ) {
- br[i] = j;
- if (++i > j) j += brdiff;
- }
- diffcolor = diffc * diffc;
- alp = (cinf->alpha) ? cnum : 0;
- acp = 0;
- idx = 0;
- for (y = 0; y < cinf->height; y++) {
- for (x = 0; x < cinf->width; x++, idx++) {
- getPixel(&elm[0], &elm[1], &elm[2], &elm[3]);
- for (i = 0; i < cnum; i++)
- planes[i][idx] = elm[i];
- if (alp) {
- planes[alp][idx] = elm[ALPHA];
- if (elm[ALPHA] == AlphaTransp) {
- brite[idx] = AlphaMark; /* 255 */
- continue;
- }
- }
- j = (cnum == 1) ? elm[0] : Bright255(elm[0], elm[1], elm[2]);
- brite[idx] = (j >= 255) ? 254 : j;
- }
- }
- smooth(brdiff);
-
- idx = 0;
- for (y = 0; y < cinf->height; y++)
- for (x = 0; x < cinf->width; x++, idx++)
- brite[idx] = br[brite[idx]];
-
- scandir = 1;
- for (y = 0; y < cinf->height; y++) {
- base = y * cinf->width;
- for (x = (scandir > 0) ? 0 : (cinf->width - 1);
- x >= 0 && x < cinf->width; x += scandir) {
- idx = base + x;
- if (alp && planes[alp][idx] == AlphaTransp) {
- area[idx] = cell_max; /* mark as ALPHA */
- continue;
- }
- rp = ((j = x - scandir) < 0 || j >= cinf->width)
- ? -1 : (idx - scandir);
- up = (y == 0) ? -1 : (idx - cinf->width);
- difx = (rp < 0 || brite[rp] != brite[idx])
- ? DiffHUGE : neighbor(rp, idx, 0);
- dify = (up < 0 || brite[up] != brite[idx])
- ? DiffHUGE : neighbor(up, idx, 0);
- if (difx > diffcolor && dify > diffcolor) {
- /* isolated pixel */
- aptr = &acell[area[idx] = acp];
- lp = aptr->rgb;
- for (i = 0; i < cnum; i++)
- lp[i] = planes[i][idx];
- aptr->count = 1;
- if (++acp >= cell_max) /* Too Many Colors */
- return NO;
- }else {
- if (difx <= diffcolor && dify <= diffcolor
- && (i = alink(area[rp])) != (j = alink(area[up]))
- && neighbor(rp, up, 0) <= diffcolor) {
- aptr = &acell[i];
- bptr = &acell[j];
- bptr->count += aptr->count;
- aptr->count = 0;
- aptr->alias = j;
- for (i = 0; i < cnum; i++)
- bptr->rgb[i] += aptr->rgb[i];
- }else
- j = alink(area[(difx < dify)? rp : up]);
- area[idx] = j;
- aptr = &acell[j];
- if (aptr->count < AvrMAX) {
- lp = aptr->rgb;
- for (i = 0; i < cnum; i++)
- lp[i] += planes[i][idx];
- aptr->count++;
- }
- }
- } /* x */
- scandir = (scandir > 0) ? -1 : 1;
- }
-
- for (j = 0; j < acp; j++) {
- long n;
- if ((n = (aptr = &acell[j])->count) > 1) {
- lp = aptr->rgb;
- for (i = 0; i < cnum; i++)
- lp[i] /= n;
- }
- }
- return YES;
- }
-
-
- - posterize:(unsigned char **)map with:(float)diff : (float)ctrl
- {
- int i, x, y, idx;
- long *lp;
- int diffc, brdiff;
- area_cell *aptr;
- float w;
-
- w = 0.25 + (diff * 0.75);
- for (w *= w; ; w += 0.05) {
- brdiff = w * 64;
- diffc = brdiff * (0.1 + (ctrl * 0.9));
- if (diffc < 4) diffc = 4;
- if (msgtext) {
- char msgbuf[80];
- sprintf(msgbuf, "Posterizing: %d:%d", brdiff, diffc);
- [msgtext setStringValue: msgbuf];
- NXPing();
- }
- resetPixel(map, 0);
- if ([self segment: brdiff : diffc]) break;
- }
- if (msgtext) {
- [msgtext setStringValue: "Painting..."];
- NXPing();
- }
-
- idx = 0;
- for (y = 0; y < cinf->height; y++) {
- for (x = 0; x < cinf->width; x++, idx++) {
- aptr = &acell[alink(area[idx])];
- if (aptr->count > 1) {
- lp = aptr->rgb;
- for (i = 0; i < cnum; i++)
- planes[i][idx] = lp[i];
- }
- }
- }
- return self;
- }
-
- @end
-