/*
	Bilateral v1.0
	by Sp00kyFox, 2014

Bilateral Filter, calculates a weighted mean of surrounding pixels based on color and spatial distance.
This can be used to smooth color transitions or blend dithering to some extent while preserving sharp edges.
Increasing the radius leads to more pixel lookups and therefore to a lower shader performance.

*/


#pragma parameter RAD   "BILATERAL Radius"        2.00 0.00 12.0 0.25
#pragma parameter CLR   "BILATERAL Color Thresh"  0.15 0.01  1.0 0.01
#pragma parameter CWGHT "BILATERAL Central Wght"  0.25 0.00  2.0 0.05

#ifdef PARAMETER_UNIFORM
	uniform float RAD, CLR, CWGHT;
#else
	#define RAD   2.00
	#define CLR   0.15
	#define CWGHT 0.25
#endif


#define TEX(dx,dy) tex2D(decal, VAR.texCoord+float2((dx),(dy))*VAR.t1)

static float4 unit4  = float4(1.0);

static int   steps = ceil(RAD);
static float clr   = -CLR * CLR;
static float sigma = RAD * RAD / 2.0;
static float cwght = 1.0 + CWGHT * max(1.0, 2.87029746*sigma + 0.43165242*RAD - 0.25219746);

static float domain[13] = {1.0, exp( -1.0/sigma), exp( -4.0/sigma), exp( -9.0/sigma), exp( -16.0/sigma), exp( -25.0/sigma), exp( -36.0/sigma),
				exp(-49.0/sigma), exp(-64.0/sigma), exp(-81.0/sigma), exp(-100.0/sigma), exp(-121.0/sigma), exp(-144.0/sigma)};

float dist2(float3 pt1, float3 pt2)
{
	float3 v = pt1 - pt2;
	return dot(v,v);
}

float4 weight(int i, int j, float3 org, float4x3 A)
{
	return domain[i] * domain[j] * exp(float4(dist2(org,A[0]), dist2(org,A[1]), dist2(org,A[2]), dist2(org,A[3]))/clr);
}


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

struct out_vertex {
	float4 position : POSITION;
	float4 color    : COLOR;
	float2 texCoord : TEXCOORD0;
	float2 t1;
};

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

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

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

	OUT.texCoord = texCoord;
	OUT.t1       = 1.0/IN.texture_size;

	return OUT;
}


/*    FRAGMENT SHADER    */
float3 main_fragment(in out_vertex VAR, uniform sampler2D decal : TEXUNIT0, uniform input IN) : COLOR
{
	float4x3 A, B;
	float4 wghtA, wghtB;
	float3 org = TEX(0,0).rgb, result = cwght*org;
	float  norm = cwght;
	

	for(int x=1; x<=steps; x++){
		
		A = float4x3(TEX( x, 0).rgb, TEX(-x, 0).rgb, TEX( 0, x).rgb, TEX( 0,-x).rgb);
		B = float4x3(TEX( x, x).rgb, TEX( x,-x).rgb, TEX(-x, x).rgb, TEX(-x,-x).rgb);

		wghtA = weight(x, 0, org, A); wghtB = weight(x, x, org, B);	

		result += mul(wghtA, A)     + mul(wghtB, B);
		norm   += dot(wghtA, unit4) + dot(wghtB, unit4);
		
		for(int y=1; y<x; y++){
					
			A = float4x3(TEX( x, y).rgb, TEX( x,-y).rgb, TEX(-x, y).rgb, TEX(-x,-y).rgb);
			B = float4x3(TEX( y, x).rgb, TEX( y,-x).rgb, TEX(-y, x).rgb, TEX(-y,-x).rgb);

			wghtA = weight(x, y, org, A); wghtB = weight(y, x, org, B);	

			result += mul(wghtA, A)     + mul(wghtB, B);
			norm   += dot(wghtA, unit4) + dot(wghtB, unit4);
		}
	}

	return result/norm;
}
