This is Posterizer.m in view mode; [Download] [Up]
#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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.