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.