/* COMPATIBILITY
	- Cg compilers
*/

/*
	SABR v1.0 Shader
	Joshua Street
	
	Portions of this algorithm were taken from Hyllian's 5xBR v3.7c
	shader.
	
	This program 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.

	This program 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
	

const static float4 A45 = float4( 1.0, -1.0, -1.0,  1.0);
const static float4 B45 = float4( 1.0,  1.0, -1.0, -1.0);
const static float4 C45 = float4( 1.5,  0.5, -0.5,  0.5);
const static float4 A15 = float4( 1.0, -1.0, -1.0,  1.0);
const static float4 B15 = float4( 0.5,  2.0, -0.5, -2.0);
const static float4 C15 = float4( 1.0,  1.0, -0.5,  0.0);
const static float4 A60 = float4( 1.0, -1.0, -1.0,  1.0);
const static float4 B60 = float4( 2.0,  0.5, -2.0, -0.5);
const static float4 C60 = float4( 2.0,  0.0, -1.0,  0.5);

const static float4 S45 = float4(0.2, 0.2, 0.2, 0.2);
const static float4 S15 = float4(0.1, 0.2, 0.1, 0.2);
const static float4 S60 = float4(0.2, 0.1, 0.2, 0.1);

const static float4 M45 = float4(0.4, 0.4, 0.4, 0.4);
const static float4 M15 = float4(0.2, 0.4, 0.2, 0.4);
const static float4 M60 = float4(0.4, 0.2, 0.4, 0.2);
const static float4 shift = float4(0.2);

const static float coef = 2.0;
const static float threshold = 20.0;
const static float lum_weight = 48.0;
const static float3 lum = float3(0.2126, 0.7152, 0.0722);
const static float3 lum_weighted = lum * lum_weight;

float4 lum_to(float3 v0, float3 v1, float3 v2, float3 v3) {
	return float4(
			dot(lum_weighted, v0),
			dot(lum_weighted, v1),
			dot(lum_weighted, v2),
			dot(lum_weighted, v3));
}

float lum_df(float A, float B) {
	return abs(A - B);
}

float4 lum_df(float4 A, float4 B) {
	return abs(A - B);
}

bool4 lum_eq(float4 A, float4 B) {
	return lessThan(lum_df(A, B), float4(threshold));
}

float4 lum_wd(float4 a, float4 b, float4 c, float4 d, float4 e, float4 f, float4 g, float4 h) {
	return lum_df(a, b) + lum_df(a, c) + lum_df(d, e) + lum_df(d, f) + 4.0 * lum_df(g, h);
}

struct input {
	half2 video_size;
	float2 texture_size;
	half2 output_size;
	float frame_count;
	float frame_direction;
	float frame_rotation;
};

struct out_vertex {
	half4 position : POSITION;
	float2 texCoord : TEXCOORD0;
	half4 t1 : TEXCOORD1;
};

/* VERTEX SHADER */
out_vertex main_vertex (
		half4 position   : POSITION,
		float2 texCoord : TEXCOORD0,
		uniform half4x4 modelViewProj,
		uniform input IN) {
	out_vertex OUT = (out_vertex)0;
	OUT.position = mul(modelViewProj, position);
	
	half2 ps = float2(1.0 / IN.texture_size.x, 1.0 / IN.texture_size.y);
	half dx = ps.x;
	half dy = ps.y;
	
	OUT.texCoord = texCoord;
	OUT.t1.xy = half2(dx, 0);
	OUT.t1.zw = half2(0, dy);
	
	return OUT;
}

/* FRAGMENT SHADER */
float4 main_fragment(
		in out_vertex VAR,
		uniform sampler2D decal : TEXUNIT0,
		uniform input IN) : COLOR {
	
	float2 fp = frac(VAR.texCoord * IN.texture_size);
	
	float2 dx = VAR.t1.xy;
	float2 dy = VAR.t1.zw;
	
	float3 P0  = tex2D(decal, VAR.texCoord - 2.0 * dx - 2.0 * dy).rgb;
	float3 P1  = tex2D(decal, VAR.texCoord -       dx - 2.0 * dy).rgb;
	float3 P2  = tex2D(decal, VAR.texCoord            - 2.0 * dy).rgb;
	float3 P3  = tex2D(decal, VAR.texCoord +       dx - 2.0 * dy).rgb;
	float3 P4  = tex2D(decal, VAR.texCoord + 2.0 * dx - 2.0 * dy).rgb;
	float3 P5  = tex2D(decal, VAR.texCoord - 2.0 * dx -       dy).rgb;
	float3 P6  = tex2D(decal, VAR.texCoord -       dx -       dy).rgb;
	float3 P7  = tex2D(decal, VAR.texCoord            -       dy).rgb;
	float3 P8  = tex2D(decal, VAR.texCoord +       dx -       dy).rgb;
	float3 P9  = tex2D(decal, VAR.texCoord + 2.0 * dx -       dy).rgb;
	float3 P10 = tex2D(decal, VAR.texCoord - 2.0 * dx           ).rgb;
	float3 P11 = tex2D(decal, VAR.texCoord -       dx           ).rgb;
	float3 P12 = tex2D(decal, VAR.texCoord                      ).rgb;
	float3 P13 = tex2D(decal, VAR.texCoord +       dx           ).rgb;
	float3 P14 = tex2D(decal, VAR.texCoord + 2.0 * dx           ).rgb;
	float3 P15 = tex2D(decal, VAR.texCoord - 2.0 * dx +       dy).rgb;
	float3 P16 = tex2D(decal, VAR.texCoord -       dx +       dy).rgb;
	float3 P17 = tex2D(decal, VAR.texCoord            +       dy).rgb;
	float3 P18 = tex2D(decal, VAR.texCoord +       dx +       dy).rgb;
	float3 P19 = tex2D(decal, VAR.texCoord + 2.0 * dx +       dy).rgb;
	float3 P20 = tex2D(decal, VAR.texCoord - 2.0 * dx + 2.0 * dy).rgb;
	float3 P21 = tex2D(decal, VAR.texCoord -       dx + 2.0 * dy).rgb;
	float3 P22 = tex2D(decal, VAR.texCoord            + 2.0 * dy).rgb;
	float3 P23 = tex2D(decal, VAR.texCoord +       dx + 2.0 * dy).rgb;
	float3 P24 = tex2D(decal, VAR.texCoord + 2.0 * dx + 2.0 * dy).rgb;
	
	float4 p7  = lum_to(P7,  P11, P17, P13);
	float4 p8  = lum_to(P8,  P6,  P16, P18);
	float4 p11 = lum_to(P11, P17, P13, P7 );
	float4 p12 = lum_to(P12, P12, P12, P12);
	float4 p13 = lum_to(P13, P7,  P11, P17);
	float4 p14 = lum_to(P14, P2,  P10, P22);
	float4 p16 = lum_to(P16, P18, P8,  P6 );
	float4 p17 = lum_to(P17, P13, P7,  P11);
	float4 p18 = lum_to(P18, P8,  P6,  P16);
	float4 p19 = lum_to(P19, P3,  P5,  P21);
	float4 p22 = lum_to(P22, P14, P2,  P10);
	float4 p23 = lum_to(P23, P9,  P1,  P15);
	
	float4 t45   = float4(A45 * fp.y + B45 * fp.x);
	float4 t15   = float4(A15 * fp.y + B15 * fp.x);
	float4 t60   = float4(A60 * fp.y + B60 * fp.x);
	
	bool4 fx45 = greaterThan(t45, C45 - S45);
	bool4 fx15 = greaterThan(t15, C15 - S15);
	bool4 fx60 = greaterThan(t60, C60 - S60);
	bool4 fxrn = greaterThan(t45, C45);
	
	float4 ma45 = smoothstep(C45 - M45, C45 + M45, t45);
	float4 ma15 = smoothstep(C15 - M15, C15 + M15, t15);
	float4 ma60 = smoothstep(C60 - M60, C60 + M60, t60);
	float4 marn = smoothstep(C45 - M45 + shift, C45 + M45 + shift, t45);
	
	float4 e45 = lum_wd(p12, p8, p16, p18, p22, p14, p17, p13);
	float4 econt = lum_wd(p17, p11, p23, p13, p7, p19, p12, p18);
	
	bool4 r45_1   = p12 != p13 && p12 != p17;
	bool4 r45_2   = !lum_eq(p13, p7) && !lum_eq(p13, p8);
	bool4 r45_3   = !lum_eq(p17, p11) && !lum_eq(p17, p16);
	bool4 r45_4_1 = !lum_eq(p13, p14) && !lum_eq(p13, p19);
	bool4 r45_4_2 = !lum_eq(p17, p22) && !lum_eq(p17, p23);
	bool4 r45_4   = lum_eq(p12, p18) && (r45_4_1 || r45_4_2);
	bool4 r45_5   = lum_eq(p12, p16) || lum_eq(p12, p8);
	bool4 r45     = r45_1 && (r45_2 || r45_3 || r45_4 || r45_5);
	
	float4 e15 = lum_df(p13, p16);
	bool4 r15 = p12 != p16 && p11 != p16;
	
	float4 e60 = lum_df(p8, p17);
	bool4 r60 = p12 != p8 && p7 != p8;
	
	bool4 edr45 = e45 < econt && r45;
	bool4 edr15 = edr45 && coef * e15 <= e60 && r15;
	bool4 edr60 = edr45 && coef * e60 <= e15 && r60;
	bool4 edrrn = e45 <= econt;
	
	bool4 px = lum_df(p12, p13) <= lum_df(p12, p17);
	
	float3 c;
	float3 res = P12;
	if (any(edr45) || any(edrrn)) {
		c = px.x ? P13 : P17;
		res = edr15.x && fx15.x ? lerp(res, c, ma15.x) : edr60.x && fx60.x ? lerp(res, c, ma60.x) : edr45.x && fx45.x ? lerp(res, c, ma45.x) : edrrn.x && fxrn.x ? lerp(res, c, marn.x) : res;
		c = px.y ? P7 : P13;
		res = edr15.y && fx15.y ? lerp(res, c, ma15.y) : edr60.y && fx60.y ? lerp(res, c, ma60.y) : edr45.y && fx45.y ? lerp(res, c, ma45.y) : edrrn.y && fxrn.y ? lerp(res, c, marn.y) : res;
		c = px.z ? P11 : P7;
		res = edr15.z && fx15.z ? lerp(res, c, ma15.z) : edr60.z && fx60.z ? lerp(res, c, ma60.z) : edr45.z && fx45.z ? lerp(res, c, ma45.z) : edrrn.z && fxrn.z ? lerp(res, c, marn.z) : res;
		c = px.w ? P17 : P11;
		res = edr15.w && fx15.w ? lerp(res, c, ma15.w) : edr60.w && fx60.w ? lerp(res, c, ma60.w) : edr45.w && fx45.w ? lerp(res, c, ma45.w) : edrrn.w && fxrn.w ? lerp(res, c, marn.w) : res;
	}
	
	return float4(res.r, res.g, res.b, 1.0);
}
