/*
 * filter.c
 * Copyright (C) 2003 Florian Schulze <crow@icculus.org>
 *
 * This file is part of Jump 'n Bump.
 *
 * Jump 'n Bump is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Jump 'n Bump is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/*
 The following scaling filter is called advancedmame2x.
 The implementation found here was possible because of the great ideas of
 Lucas Pope.
 */

typedef unsigned char byte;
static int scale2x_inited = 0;
static byte lookup_map[4 * 16];

void init_scale2x(void)
{
	int i;

	if (scale2x_inited)
		return;

	//-------------------------------------------------------------------------
	// scale2x takes the following source:
	// A B C
	// D E F
	// G H I
	//
	// and doubles the size of E to produce:
	// E0 E1
	// E2 E3
	//
	//  E0 = D == B && B != F && D != H ? D : E;
	//  E1 = B == F && B != D && F != H ? F : E;
	//  E2 = D == H && D != B && H != F ? D : E;
	//  E3 = H == F && D != H && B != F ? F : E;
	//
	// to make this comparison regimen faster, we encode source color
	// equivalency into a single byte with the getCode() macro
	//
	// #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 )

	// encode the scale2x conditionals into a lookup code
	for (i = 0; i < 16; i++) {
		//  E0 = D == B && B != F && D != H ? D : E; // 10-0 => 1000 or 1010 => 8 or A
		lookup_map[0 * 16 + i] = (i == 0x8 || i == 0xA) ? 0 : 1;
		//  E1 = B == F && B != D && F != H ? F : E; // 0-01 => 0101 or 0001 => 5 or 1
		lookup_map[1 * 16 + i] = (i == 0x5 || i == 0x1) ? 2 : 1;
		//  E2 = D == H && D != B && H != F ? D : E; // 010- => 0101 or 0100 => 5 or 4
		lookup_map[2 * 16 + i] = (i == 0x4 || i == 0x5) ? 0 : 1;
		//  E3 = H == F && D != H && B != F ? F : E; // -010 => 1010 or 0010 => A or 2
		lookup_map[3 * 16 + i] = (i == 0xA || i == 0x2) ? 2 : 1;
	}
}

void do_scale2x(unsigned char *src,
				int src_width,
				int src_height,
				unsigned char *dst)
{
	int x;
	int y;
	int dst_width = src_width * 2;
	int code;
	byte rowColors[3];
	byte *e0;
	byte *e1;
	byte *e2;
	byte *e3;

	if (!scale2x_inited)
		init_scale2x();

	// special top case - b is always unknown
	{
		byte *d;
		byte *e;
		byte *f;
		byte *h;

		e0 = &dst[0];
		e1 = &dst[1];
		e2 = &dst[dst_width];
		e3 = &dst[dst_width + 1];
		e = &src[0];
		f = &src[1];
		h = &src[src_width];

		// special left case - d is unknown
		rowColors[0] = *e;
		rowColors[1] = *e;
		rowColors[2] = *f;
		code = ((*f == *h) << 1);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
		e++;
		f++;
		h++;
		d = &src[src_width]; // (src_width - 1) + 1
		e0 += 2;
		e1 += 2;
		e2 += 2;
		e3 += 2;

		// normal case
		for (x = 1; x < (src_width - 1); x++) {
			rowColors[0] = *d;
			rowColors[1] = *e;
			rowColors[2] = *f;
			code = ((*f == *h) << 1 | (*h == *d) << 2);
			*e0 = rowColors[lookup_map[0 * 16 + code]];
			*e1 = rowColors[lookup_map[1 * 16 + code]];
			*e2 = rowColors[lookup_map[2 * 16 + code]];
			*e3 = rowColors[lookup_map[3 * 16 + code]];
			d++;
			e++;
			f++;
			h++;
			e0 += 2;
			e1 += 2;
			e2 += 2;
			e3 += 2;
		}

		// special right case - f is unknown
		rowColors[0] = *d;
		rowColors[1] = *e;
		rowColors[2] = *e;
		code = ((*h == *d) << 2);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
	}

	// top and bottom always known
	for (y = 1; y < (src_height - 1); y++) {
		byte *b;
		byte *d;
		byte *e;
		byte *f;
		byte *h;

		e0 = &dst[y * dst_width * 2];
		e1 = &dst[y * dst_width * 2 + 1];
		e2 = &dst[y * dst_width * 2 + dst_width];
		e3 = &dst[y * dst_width * 2 + dst_width + 1];
		b = &src[y * src_width - src_width];
		e = &src[y * src_width];
		f = &src[y * src_width + 1];
		h = &src[y * src_width + src_width];

		// special left case - d is unknown
		rowColors[0] = *e;
		rowColors[1] = *e;
		rowColors[2] = *f;
		code = ((*b == *f) << 0 | (*f == *h) << 1);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
		b++;
		e++;
		f++;
		h++;
		d = &src[y * src_width]; // (y * src_width - 1) + 1
		e0 += 2;
		e1 += 2;
		e2 += 2;
		e3 += 2;

		// normal case
		for (x = 1; x < (src_width - 1); x++) {
			rowColors[0] = *d;
			rowColors[1] = *e;
			rowColors[2] = *f;
			code = ((*b == *f) << 0 | (*f == *h) << 1 | (*h == *d) << 2 | (*d == *b) << 3);
			*e0 = rowColors[lookup_map[0 * 16 + code]];
			*e1 = rowColors[lookup_map[1 * 16 + code]];
			*e2 = rowColors[lookup_map[2 * 16 + code]];
			*e3 = rowColors[lookup_map[3 * 16 + code]];
			b++;
			d++;
			e++;
			f++;
			h++;
			e0 += 2;
			e1 += 2;
			e2 += 2;
			e3 += 2;
		}

		// special right case - f is unknown
		rowColors[0] = *d;
		rowColors[1] = *e;
		rowColors[2] = *e;
		code = ((*h == *d) << 2 | (*d == *b) << 3);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
	}

	// special bottom case - h is always unknown
	{
		byte *b;
		byte *d;
		byte *e;
		byte *f;

		e0 = &dst[y * dst_width * 2];
		e1 = &dst[y * dst_width * 2 + 1];
		e2 = &dst[y * dst_width * 2 + dst_width];
		e3 = &dst[y * dst_width * 2 + dst_width + 1];
		b = &src[y * src_width - src_width];
		e = &src[y * src_width];
		f = &src[y * src_width + 1];

		// special left case - d is unknown
		rowColors[0] = *e;
		rowColors[1] = *e;
		rowColors[2] = *f;
		code = ((*b == *f) << 0);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
		b++;
		e++;
		f++;
		d = &src[y * src_width]; // (y * src_width - 1) + 1
		e0 += 2;
		e1 += 2;
		e2 += 2;
		e3 += 2;

		// normal case
		for (x = 1; x < (src_width - 1); x++) {
			rowColors[0] = *d;
			rowColors[1] = *e;
			rowColors[2] = *f;
			code = ((*b == *f) << 0 | (*d == *b) << 3);
			*e0 = rowColors[lookup_map[0 * 16 + code]];
			*e1 = rowColors[lookup_map[1 * 16 + code]];
			*e2 = rowColors[lookup_map[2 * 16 + code]];
			*e3 = rowColors[lookup_map[3 * 16 + code]];
			b++;
			d++;
			e++;
			f++;
			e0 += 2;
			e1 += 2;
			e2 += 2;
			e3 += 2;
		}

		// special right case - f is unknown
		rowColors[0] = *d;
		rowColors[1] = *e;
		rowColors[2] = *e;
		code = ((*d == *b) << 3);
		*e0 = rowColors[lookup_map[0 * 16 + code]];
		*e1 = rowColors[lookup_map[1 * 16 + code]];
		*e2 = rowColors[lookup_map[2 * 16 + code]];
		*e3 = rowColors[lookup_map[3 * 16 + code]];
	}
}
