/*
   Hyllian's 5xBR v3.8a (rounded) Shader
   
   Copyright (C) 2011/2012 Hyllian/Jararaca - sergiogdb@gmail.com

   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.


   Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
*/

const static float coef           = 2.0;
const static float4 eq_threshold  = float4(15.0);
const static half y_weight        = 48.0;
const static half u_weight        = 7.0;
const static half v_weight        = 6.0;
const static half3x3 yuv          = half3x3(0.299, 0.587, 0.114, -0.169, -0.331, 0.499, 0.499, -0.418, -0.0813);
const static half3x3 yuv_weighted = half3x3(y_weight*yuv[0], u_weight*yuv[1], v_weight*yuv[2]);
const static float4 delta       = float4(0.2);

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


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

bool4 eq(float4 A, float4 B)
{
	return (df(A, B) < float4(15.0));
}

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



struct input
{
	half2 video_size;
	float2 texture_size;
	half2 output_size;
};


struct out_vertex {
	half4 position : POSITION;
	half4 color    : COLOR;
	float2 texCoord : TEXCOORD0;
	float4 t1       : TEXCOORD1;
	float4 t2       : TEXCOORD2;
	float4 t3       : TEXCOORD3;
	float4 t4       : TEXCOORD4;
	float4 t5       : TEXCOORD5;
	float4 t6       : TEXCOORD6;
	float4 t7       : TEXCOORD7;
};

/*    VERTEX_SHADER    */
out_vertex main_vertex
(
	half4 position	: POSITION,
	half4 color	: COLOR,
	float2 texCoord : TEXCOORD0,

   	uniform half4x4 modelViewProj,
	uniform input IN
)
{
	out_vertex OUT;

	OUT.position = mul(modelViewProj, position);
	OUT.color = color;

	float2 ps = float2(1.0/IN.texture_size.x, 1.0/IN.texture_size.y);
	float dx = ps.x;
	float dy = ps.y;

	//    A1 B1 C1
	// A0  A  B  C C4
	// D0  D  E  F F4
	// G0  G  H  I I4
	//    G5 H5 I5

	OUT.texCoord = texCoord;
	OUT.t1 = texCoord.xxxy + half4( -dx, 0, dx,-2.0*dy); // A1 B1 C1
	OUT.t2 = texCoord.xxxy + half4( -dx, 0, dx,    -dy); //  A  B  C
	OUT.t3 = texCoord.xxxy + half4( -dx, 0, dx,      0); //  D  E  F
	OUT.t4 = texCoord.xxxy + half4( -dx, 0, dx,     dy); //  G  H  I
	OUT.t5 = texCoord.xxxy + half4( -dx, 0, dx, 2.0*dy); // G5 H5 I5
	OUT.t6 = texCoord.xyyy + half4( 2.0*dx, 2.0*dy, 0,  dy); // A0 D0 G0
	OUT.t7 = texCoord.xyyy + half4( 2.0*dx,-dy, 0,  dy); // C4 F4 I4

	return OUT;
}

/*    FRAGMENT SHADER    */
half4 main_fragment(in out_vertex VAR, uniform sampler2D decal : TEXUNIT0, uniform input IN) : COLOR
{
	float2 fp = frac(VAR.texCoord*IN.texture_size);
	half3 HH, HV, HD;

	half3 b  = tex2D(decal, VAR.t2.xw).rgb;
	half3 c  = tex2D(decal, VAR.t2.yw).rgb;
	half3 d  = tex2D(decal, VAR.t2.zw).rgb;
	half3 e = tex2D(decal, VAR.t7.xy).rgb;

	half3 f  = tex2D(decal, VAR.t3.xw).rgb;
	half3 g  = tex2D(decal, VAR.t3.yw).rgb;
	half3 h  = tex2D(decal, VAR.t3.zw).rgb;
	half3 o = tex2D(decal, VAR.t7.xz).rgb;

	half3 p  = tex2D(decal, VAR.t4.xw).rgb;
	half3 q  = tex2D(decal, VAR.t4.yw).rgb;
	half3 r  = tex2D(decal, VAR.t4.zw).rgb;
	half3 s = tex2D(decal, VAR.t7.xw).rgb;

	half3 t = tex2D(decal, VAR.t5.xw).rgb;
	half3 u = tex2D(decal, VAR.t5.yw).rgb;
	half3 v = tex2D(decal, VAR.t5.zw).rgb;
	half3 w = tex2D(decal, VAR.t6.xy).rgb;

	float4 l1 = mul( half4x3(b, c, d, e), yuv_weighted[0] );
	float4 l2 = mul( half4x3(f, g, h, o), yuv_weighted[0] );
	float4 l3 = mul( half4x3(p, q, r, s), yuv_weighted[0] );
	float4 l4 = mul( half4x3(t, u, v, w), yuv_weighted[0] );

	bool horizHH_edge = (dfY(l2.x + l2.y, l2.z + l2.w) < dfY(l1.y + l1.z, l3.y + l3.z));
	bool vertHH_edge  = (dfY(l2.x + l2.y, l2.z + l2.w) > dfY(l1.y + l1.z, l3.y + l3.z));
	bool vertHV_edge  = (dfY(l1.y + l2.y, l3.y + l4.y) < dfY(l2.x + l3.x, l2.z + l3.z));
	bool horizHV_edge = (dfY(l1.y + l2.y, l3.y + l4.y) > dfY(l2.x + l3.x, l2.z + l3.z));
	bool hqHD_edge = (dfY(l1.x + l2.y, l3.z + l4.w) > dfY(l1.w + l2.z, l3.y + l4.x));
	bool grHD_edge = (dfY(l1.x + l2.y, l3.z + l4.w) < dfY(l1.w + l2.z, l3.y + l4.x));

	bool edge_qd = (dfY(l1.y, l3.z) > dfY(l1.z, l3.y));
	bool edge_rc = (dfY(l1.y, l3.z) < dfY(l1.z, l3.y));
	bool edge_fr = (dfY(l3.x, l2.z) > dfY(l2.x, l3.z));
	bool edge_ph = (dfY(l3.x, l2.z) < dfY(l2.x, l3.z));

	if (horizHH_edge)
	{
			HH = (g + h)/2.0;
	}
	else if (vertHH_edge)
	{
		if (edge_qd)
		{
			HH = (d + q)/2;
		}
		else if (edge_rc)
		{
			HH = (r + c)/2;
		}
		else
		{
			HH = (g + h)/2.0;
		}
	}
	else
	{
		HH = (g + h)/2.0;
	}


	if (vertHV_edge)
	{
		HV= (g + q)/2;
	}
	else if (horizHV_edge)
	{
		if (edge_ph)
		{
			HV = (h + p)/2;
		}
		else if (edge_fr)
		{
			HV = (r + f)/2;
		}
		else
		{
			HV = (g + q)/2.0;
		}
	}
	else
	{
		HV = (g + q)/2.0;
	}



	if (hqHD_edge)
	{
		HD= (h + q)/2;
	}
	else if (grHD_edge)
	{
		HD= (g + r)/2;
	}
	else
	{
		HD = (g + h + q + r)/4.0;
	}


	half3 res;

	res = (fp.x < 0.50) ? (fp.y < 0.50 ? g : HV) : (fp.y < 0.50 ? HH: HD);


	return half4(res, 1.0);
}


