
/* Generated by Interface Builder */

#import <math.h>
#import <stdio.h>
#import <appkit/Button.h>
#import <appkit/Matrix.h>
#import <appkit/NXBitmapImageRep.h>
#import "GraphicApp.h"
#import "PictureDocument.h"
#import "NXBitmapImageRepControl.h"
#import "ImageControl.h"
#import "Conversions.h"

@implementation Conversions

+ new
{
	self = [super new];
	
	[NXApp setConversionPanel: self];
	state = 0;
	
	return self;
}

- setTypeMatrix:anObject
{
	typeMatrix = anObject;
	if (state < 1) state++;
	else [[NXApp currentDoc] windowDidBecomeMain: self];
	return self;
}

- setAlphaToggle:anObject
{
	alphaToggle = anObject;
	if (state < 1) state++;
	else [[NXApp currentDoc] windowDidBecomeMain: self];
	return self;
}

- to1Bit
{
	id		newImage;
	id		imageCon = [[NXApp imageControl] getImageControl: currentImage];
	
	newImage = [imageCon ditherImage];
	
	if (inverted) {
		unsigned char		*data;
		int				x;
		int				count;
		
		fprintf(stderr, "Inverting Image\n");
		count = ([newImage pixelsWide] / 8 + (([newImage pixelsWide] % 8) != 0 ? 1 : 0))
					* [newImage pixelsHigh];
		data = [newImage data];
		for (x = 0; x < count; x++) {
			data[x] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];
	
	return self;
}

- to2Bit
{
	id 				newImage;
	id				imageCon = [[NXApp imageControl] getImageControl: currentImage];
	unsigned char		*planes[5];
	GetPixelNextFunc	nextPixel;
	ToGrayFunc		toGray;
	Pixel			*workPixel;
	int				i, j, byte, shift;
	double			scale;
	BOOL			oAlpha = [currentImage hasAlpha];
	int				width, height;
	
	newImage = [NXBitmapImageRep alloc];
	[newImage 	initData: 		NULL
				pixelsWide: 		[currentImage pixelsWide]
				pixelsHigh: 		[currentImage pixelsHigh]
				bitsPerSample:	2
				samplesPerPixel:	alpha ? 2 : 1
				hasAlpha:		alpha
				isPlanar:			YES
				colorSpace:		NX_OneIsWhiteColorSpace
				bytesPerRow:		0
				bitsPerPixel:		0];

	width = [newImage pixelsWide];
	height = [newImage pixelsHigh];

	[newImage getDataPlanes: planes];
	nextPixel = [imageCon getNextFunction];
	toGray = [imageCon getToGrayFunction];
	scale = 3.0 / (pow(2.0, (double)[currentImage bitsPerSample]) - 1.0);

	[imageCon resetNext];
	
	byte = 0;
	planes[0][byte] = 0;
	if (alpha) planes[1][byte] = 0;
	for (i = 0; i < height; i++) {
		shift = 6;
		for (j = 0; j < width; j++) {
			workPixel = toGray(nextPixel());
			planes[0][byte] |= ((int)(workPixel->values[0] * scale) & 0x03) << shift;
			if (alpha) 
				if (oAlpha) 
					planes[1][byte]|=((int)(workPixel->values[1]*scale)&0x03)<<shift;
				else
					planes[1][byte] |= 0x03 << shift;
			shift -= 2;
			if (shift < 0) {
				shift = 6;
				byte++;
				planes[0][byte] = 0;
				if (alpha) planes[1][byte] = 0;
			}
		}
		if (shift != 6) {
			shift = 6;
			byte++;
			planes[0][byte] = 0;
			if (alpha) planes[1][byte] = 0;
		}
	}
	
	if (inverted) {
		for (i = 0; i < [newImage bytesPerPlane]; i++) {
			planes[0][i] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];

	return self;
}

- to4Bit
{
	id 				newImage;
	id				imageCon = [[NXApp imageControl] getImageControl: currentImage];
	unsigned char		*planes[5];
	GetPixelNextFunc	nextPixel;
	ToGrayFunc		toGray;
	Pixel			*workPixel;
	int				i, j, byte, shift;
	double			scale;
	BOOL			oAlpha = [currentImage hasAlpha];
	int				width, height;
	
	newImage = [NXBitmapImageRep alloc];
	[newImage 	initData: 		NULL
				pixelsWide: 		[currentImage pixelsWide]
				pixelsHigh: 		[currentImage pixelsHigh]
				bitsPerSample:	4
				samplesPerPixel:	alpha ? 2 : 1
				hasAlpha:		alpha
				isPlanar:			YES
				colorSpace:		NX_OneIsWhiteColorSpace
				bytesPerRow:		0
				bitsPerPixel:		0];

	width = [newImage pixelsWide];
	height = [newImage pixelsHigh];

	[newImage getDataPlanes: planes];
	nextPixel = [imageCon getNextFunction];
	toGray = [imageCon getToGrayFunction];
	scale = 15.0 / (pow(2.0, (double)[currentImage bitsPerSample]) - 1.0);

	[imageCon resetNext];
	
	byte = 0;
	planes[0][byte] = 0;
	if (alpha) planes[1][byte] = 0;
	for (i = 0; i < height; i++) {
		shift = 4;
		for (j = 0; j < width; j++) {
			workPixel = toGray(nextPixel());
			planes[0][byte] |= ((int)(workPixel->values[0] * scale) & 0x0F) << shift;
			if (alpha) 
				if (oAlpha) 
					planes[1][byte]|=((int)(workPixel->values[1]*scale)&0x0F)<<shift;
				else
					planes[1][byte] |= 0x07 << shift;
			shift -= 4;
			if (shift < 0) {
				shift = 4;
				byte++;
				planes[0][byte] = 0;
				if (alpha) planes[1][byte] = 0;
			}
		}
		if (shift != 4) {
			shift = 4;
			byte++;
			planes[0][byte] = 0;
			if (alpha) planes[1][byte] = 0;
		}
	}
	
	if (inverted) {
		for (i = 0; i < [newImage bytesPerPlane]; i++) {
			planes[0][i] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];

	return self;
}

- to8Bit
{
	id 				newImage;
	id				imageCon = [[NXApp imageControl] getImageControl: currentImage];
	unsigned char		*planes[5];
	GetPixelNextFunc	nextPixel;
	ToGrayFunc		toGray;
	Pixel			*workPixel;
	int				i;
	double			scale;
	BOOL			oAlpha = [currentImage hasAlpha];
	int				width, height;
	
	newImage = [NXBitmapImageRep alloc];
	[newImage 	initData: 		NULL
				pixelsWide: 		[currentImage pixelsWide]
				pixelsHigh: 		[currentImage pixelsHigh]
				bitsPerSample:	8
				samplesPerPixel:	alpha ? 2 : 1
				hasAlpha:		alpha
				isPlanar:			YES
				colorSpace:		NX_OneIsWhiteColorSpace
				bytesPerRow:		0
				bitsPerPixel:		0];

	width = [newImage pixelsWide];
	height = [newImage pixelsHigh];

	[newImage getDataPlanes: planes];
	nextPixel = [imageCon getNextFunction];
	toGray = [imageCon getToGrayFunction];
	scale = 255.0 / (pow(2.0, (double)[currentImage bitsPerSample]) - 1.0);

	[imageCon resetNext];
	
	for (i = 0; i < width * height; i++) {
		workPixel = toGray(nextPixel());
		planes[0][i] = (int)(workPixel->values[0] * scale);
		if (alpha) 
			if (oAlpha) 
				planes[1][i] = (int)(workPixel->values[1] * scale);
			else
				planes[1][i] = 0xFF;
	}
	
	if (inverted) {
		for (i = 0; i < [newImage bytesPerPlane]; i++) {
			planes[0][i] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];

	return self;
}

- to12Bit
{
	id 				newImage;
	id				imageCon = [[NXApp imageControl] getImageControl: currentImage];
	unsigned char		*planes[5];
	GetPixelNextFunc	nextPixel;
	ToRGBFunc		toRGB;
	Pixel			*workPixel;
	int				i, j, byte, shift;
	double			scale;
	BOOL			oAlpha = [currentImage hasAlpha];
	int				width, height;
	
	newImage = [NXBitmapImageRep alloc];
	[newImage 	initData: 		NULL
				pixelsWide: 		[currentImage pixelsWide]
				pixelsHigh: 		[currentImage pixelsHigh]
				bitsPerSample:	4
				samplesPerPixel:	alpha ? 4 : 3
				hasAlpha:		alpha
				isPlanar:			YES
				colorSpace:		NX_RGBColorSpace
				bytesPerRow:		0
				bitsPerPixel:		0];

	width = [newImage pixelsWide];
	height = [newImage pixelsHigh];

	[newImage getDataPlanes: planes];
	nextPixel = [imageCon getNextFunction];
	toRGB = [imageCon getToRGBFunction];
	scale = 15.0 / (pow(2.0, (double)[currentImage bitsPerSample]) - 1.0);

	[imageCon resetNext];
	
	byte = 0;
	planes[0][byte] = 0; planes[1][byte] = 0; planes[2][byte] = 0;
	if (alpha) planes[3][byte] = 0;
	for (i = 0; i < height; i++) {
		shift = 4;
		for (j = 0; j < width; j++) {
			workPixel = toRGB(nextPixel());
			planes[0][byte] |= ((int)(workPixel->values[0] * scale) & 0x0F) << shift;
			planes[1][byte] |= ((int)(workPixel->values[1] * scale) & 0x0F) << shift;
			planes[2][byte] |= ((int)(workPixel->values[2] * scale) & 0x0F) << shift;
			if (alpha) 
				if (oAlpha) 
					planes[3][byte]|=((int)(workPixel->values[3]*scale)&0x0F)<<shift;
				else
					planes[3][byte] |= 0x07 << shift;
			shift -= 4;
			if (shift < 0) {
				shift = 4;
				byte++;
				planes[0][byte] = 0; planes[1][byte] = 0; planes[2][byte] = 0;
				if (alpha) planes[4][byte] = 0;
			}
		}
		if (shift != 4) {
			shift = 4;
			byte++;
			planes[0][byte] = 0; planes[1][byte] = 0; planes[2][byte] = 0;
			if (alpha) planes[4][byte] = 0;
		}
	}
	
	if (inverted) {
		for (i = 0; i < [newImage bytesPerPlane]; i++) {
			planes[0][i] ^= 0xFF;
			planes[1][i] ^= 0xFF;
			planes[2][i] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];

	return self;
}

- to24Bit
{
	id 				newImage;
	id				imageCon = [[NXApp imageControl] getImageControl: currentImage];
	unsigned char		*planes[5];
	GetPixelNextFunc	nextPixel;
	ToRGBFunc		toRGB;
	Pixel			*workPixel;
	int				i;
	double			scale;
	BOOL			oAlpha = [currentImage hasAlpha];
	int				width, height;
	
	newImage = [NXBitmapImageRep alloc];
	[newImage 	initData: 		NULL
				pixelsWide: 		[currentImage pixelsWide]
				pixelsHigh: 		[currentImage pixelsHigh]
				bitsPerSample:	8
				samplesPerPixel:	alpha ? 4 : 3
				hasAlpha:		alpha
				isPlanar:			YES
				colorSpace:		NX_RGBColorSpace
				bytesPerRow:		0
				bitsPerPixel:		0];

	width = [newImage pixelsWide];
	height = [newImage pixelsHigh];

	[newImage getDataPlanes: planes];
	nextPixel = [imageCon getNextFunction];
	toRGB = [imageCon getToRGBFunction];
	scale = 255.0 / (pow(2.0, (double)[currentImage bitsPerSample]) - 1.0);

	[imageCon resetNext];
	
	for (i = 0; i < width * height; i++) {
		workPixel = toRGB(nextPixel());
		planes[0][i] = (int)(workPixel->values[0] * scale);
		planes[1][i] = (int)(workPixel->values[1] * scale);
		planes[2][i] = (int)(workPixel->values[2] * scale);
		if (alpha) 
			if (oAlpha) 
				planes[3][i] = (int)(workPixel->values[1] * scale);
			else
				planes[3][i] = 0xFF;
	}
	
	if (inverted) {
		for (i = 0; i < [newImage bytesPerPlane]; i++) {
			planes[0][i] ^= 0xFF;
			planes[1][i] ^= 0xFF;
			planes[2][i] ^= 0xFF;
		}
	}
		
	[[NXApp currentDoc] replaceImageWith: newImage];
	[[NXApp currentDoc] windowDidBecomeMain: self];

	return self;
}

- setSPP:	 (int)spp withBPS: (int)bps inColorSpace: (int)cs andAlpha: (BOOL)alph
{
	int		b = bps * spp;
	
	if (alph) {
		[alphaToggle setState: 1];
		switch (b) {
			case 2:
				[typeMatrix selectCellAt: 0 : 0]; break;
			case 4:
				[typeMatrix selectCellAt: 0 : 1]; break;
			case 8:
				[typeMatrix selectCellAt: 0 : 2]; break;
			case 16:
				if (cs == NX_RGBColorSpace) [typeMatrix selectCellAt: 1 : 1];
				else [typeMatrix selectCellAt: 1 : 0];
				break;
			case 32:
				[typeMatrix selectCellAt: 1 : 2]; break;
		}
	} else {
		[alphaToggle setState: 0];
		switch (b) {
			case 1:
				[typeMatrix selectCellAt: 0 : 0]; break;
			case 2:
				[typeMatrix selectCellAt: 0 : 1]; break;
			case 4:
				[typeMatrix selectCellAt: 0 : 2]; break;
			case 8:
				[typeMatrix selectCellAt: 1 : 0]; break;
			case 12:
				[typeMatrix selectCellAt: 1 : 1]; break;
			case 24:
				[typeMatrix selectCellAt: 1 : 2]; break;
		}
	}
	
	return self;
}

- applyConversion:sender
{
	bits = [typeMatrix selectedRow] * 3 + [typeMatrix selectedCol];
	alpha = [alphaToggle state];
	inverted = [invertToggle state];
	currentImage = [[NXApp currentDoc] getImage];

	if (!currentImage) {
		NXRunAlertPanel("Alert", 
						"There's no active document to apply a conversion to.",
						"Continue", NULL, NULL);
		return self;
	}

	if (!currentImage) {
		fprintf(stderr, "No image to convert\n");
		return self;
	}
	
	switch (bits) {
		case 0:
			[self to1Bit];
			break;
		case 1:
			[self to2Bit];
			break;
		case 2:
			[self to4Bit];
			break;
		case 3:
			[self to8Bit];
			break;
		case 4:
			[self to12Bit];
			break;
		case 5:
			[self to24Bit];
			break;
	}
	
	return self;
}


@end
