/*
	Edge Enhancement	coded by T.Ogihara
*/

#import "../ImageOpr.h"
#import <appkit/Application.h>
#import <appkit/publicWraps.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Control.h>
#import <appkit/Panel.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <streams/streams.h>
#import "../TVController.h"
#import "../ToyWin.h"
#import "../ToyView.h"
#import "../common.h"
#import "../getpixel.h"
#import "Posterizer.h"
#import "imageOperation.h"


@implementation ImageOpr (Edge)

static const char enhanceTab[5][5] = {
	{0, 1, 1, 1, 0},
	{1, 2, 3, 2, 1},
	{1, 3, 0, 3, 1},
	{1, 2, 3, 2, 1},
	{0, 1, 1, 1, 0}
};
static const char embossTab[5][5] = {
	{0,  0,  0,  0,  0},
	{0, -2, -1,  0,  0},
	{0, -1,  0,  1,  0},
	{0,  0,  1,  2,  0},
	{0,  0,  0,  0,  0}
};


static int edge(int op, commonInfo *cinf,
	float factor, float bright, unsigned char **planes)
{
	int i, j, x, y, ptr;
	int cnum, pnum;
	int elm[MAXPLANE];
	int alp, err;
	const char (*weight)[5];

	cnum = pnum = cinf->numcolors;
	if (cinf->alpha) alp = pnum++;
	else alp = 0;
	weight = (op == Enhance) ? enhanceTab: embossTab;

	err = allocImage(planes, cinf->width, cinf->height + 3, 8, pnum);
	if (err) return err;

	for (y = 0; y < cinf->height; y++) {
		ptr = cinf->width * (y + 3);
		for (x = 0; x < cinf->width; x++) {
			getPixel(&elm[0], &elm[1], &elm[2], &elm[3]);
			for (i = 0; i < cnum; i++)
				planes[i][ptr] = elm[i];
			if (alp) planes[alp][ptr] = elm[3];
			ptr++;
		}
	}

	for (y = 0; y < cinf->height; y++) {
		int curp, pw, n, v, w, totalw, totalv;
		int xlow, xhigh, ylow, yhigh;

		ylow = (y > 2) ? -2 : -y;
		if ((yhigh = cinf->height - 1 - y) > 2) yhigh = 2;
		ptr = cinf->width * (y + 3);
		curp = cinf->width * y;
		for (x = 0; x < cinf->width; x++, ptr++, curp++) {
			if (alp) {
				planes[alp][curp] = w = planes[alp][ptr];
				if (w == AlphaTransp) {
					for (n = 0; n < cnum; n++)
						planes[n][curp] = 255;
					continue;
				}
			}
			xlow = (x > 2) ? -2 : -x;
			if ((xhigh = cinf->width - 1 - x) > 2) xhigh = 2;
			for (n = 0; n < cnum; n++) {
				totalv = 0;
				totalw = 0;
				for (i = ylow; i < yhigh; i++) {
				    pw = cinf->width * (y + 3 + i);
				    for (j = xlow; j < xhigh; j++) {
					totalw += w = weight[i+2][j+2];
					if (w)
					    totalv += planes[n][pw+x+j] * w;
				    }
				}
				if (op == Enhance) {
					w = planes[n][ptr];
					v = totalv / totalw - w;
					w -= (int)(factor * v + 0.5);
				}else {
					w = (int)(factor * totalv);
					v = (planes[n][ptr] - 128) * bright;
					w += v + 128;
				}
				planes[n][curp] = (w < 0) ? 0
						: ((w > 255) ? 255 : w);
			}
		}
	}

	return 0;
}


/* Local Method */
- (int)doEnhance:(int)op filename:(const char *)fn map:(unsigned char **)map
	parent:(ToyWin *)parent
	info:(commonInfo *)cinf with:(float)degree : (float)bright
{
	ToyWin	*tw;
	int	err = 0;
	id	text;
	commonInfo *newinf = NULL;
	unsigned char *working[MAXPLANE];

	working[0] = NULL;
	tw = NULL;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL)
		goto ErrEXIT;
	*newinf = *cinf;
	newinf->bits = 8;
	newinf->isplanar = YES;
	newinf->xbytes = newinf->width;
	newinf->palsteps = 0;
	newinf->palette = NULL;
	text = [theController messageDisplay:
		(op == Enhance) ? "Enhancing..." :
		((op == Emboss) ? "Embossing..." : "Posterizing...")];
	tw = [[ToyWin alloc] init:parent by:op];
	[tw initLocateWindow:fn width:newinf->width height:newinf->height];
	if (op == Posterize) {
		id pos = [Posterizer alloc];
		if (pos != nil && [pos initWith:cinf newmap:working]) {
			[pos setMessageText: text];
			[pos posterize:map with:degree : bright];
		}else
			err = Err_MEMORY;
		if (pos) [pos free];
	}else {
		resetPixel(map, 0);
		err = edge(op, cinf, degree, bright, working);
	}
	[theController messageDisplay:NULL];
	if (err) goto ErrEXIT;
	[tw makeComment: newinf from: cinf];
	if ([tw drawView:working info: newinf] == nil)
		goto ErrEXIT;
	[theController newWindow: tw];

	return 0;

ErrEXIT:
	if (working[0]) free((void *)working[0]);
	if (newinf) free((void *)newinf);
	if (tw) [tw free];
	return Err_MEMORY;
}

/* Local Method */
- enhanceOrEmboss:(int)op and:(float)degree :(float)bright
{
	ToyWin	*tw;
	ToyView	*tv = NULL;
	NXImageRep	*rep;
	commonInfo	*cinf;
	unsigned char	*map[MAXPLANE];
	char	fn[MAXFILENAMELEN];
	const char *fnam, *opstr;
	int	err;

	if (degree == 0.0 || (tw = [self keyParentWindow: op]) == nil) {
		NXBeep();
		return self;
	}
	tv = [tw toyView];
	fnam = [tw filename];
	cinf = [tv commonInfo];
	if (![self checkCommonInfo:cinf filename:fnam]
				|| cinf->width >= MAXWidth - 4)
		return self;

	rep = [[tv image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	if ((err = initGetPixel(cinf)) != 0) {
		errAlert(fnam, err);
		return self;
	}
	opstr = (op == Enhance) ? "Enhance":
		((op == Emboss) ? "Emboss" : "Posterize");
	sprintf(fn, "%s(%s)", fnam, opstr);
	if ((err = [self doEnhance:op filename:fn map:map
			parent:tw info:cinf with:degree :bright]) != 0)
		errAlert(fnam, err);
	return self;
}

- enhanceWith:(float)degree
{
	[self enhanceOrEmboss:Enhance and:degree : 0.0];
	return self;
}

- embossWith:(float)degree and:(float)bright
{
	[self enhanceOrEmboss:Emboss and:degree : bright];
	return self;
}

- posterizeWith:(float)diff and:(float)ctrl
{
	[self enhanceOrEmboss:Posterize and:diff : ctrl];
	return self;
}

@end
