sampler texture0 : register(s0);

float4x4 WorldViewProjectionTransform  : WORLDVIEWPROJ;
float2 fTextureSize;

struct VS_OUTPUT 
{
   float4 Position   : POSITION0;
   float2 TexCoord   : TEXCOORD0;
};

VS_OUTPUT vertexMain(
				float4 vPosition : POSITION,
				float2 texCoord0 : TEXCOORD0)
{
	VS_OUTPUT Output; 

	Output.Position = mul(vPosition, WorldViewProjectionTransform);
	Output.TexCoord = texCoord0;   

	return Output;
}

float nv12rgb_getYValue(float2 texCoord)
{
	float4 map = float4(2, 1, 0, 3);
	float fTargetX = 0.f;
	float2 coord = texCoord;
	coord.x /= 4.f;
	
	// corrd could now point to 4 different pixels
	// so we could not have a fraction of 0.0, 0.25, 0.5, 0.75 here
	// and have to decide which pixel to choose for Y
	// therefore we multiply it by four and get an index to access the pixel
	// but we have to map this value, to the real positions in the texture memory
	// because they are not in the original sequence (0 -> 2, 1 -> 1, 0 -> 2, 3 -> 3)
	float fFraction = modf(coord.x, fTargetX);
	fFraction *= 4.f;

	// get the pixel
	coord.x = fTargetX + 0.5f;
	coord.y += 0.5f;
	coord /= fTextureSize;
	float4 raw = tex2D(texture0, coord);
	return raw[map[fFraction]];
}

float3 yv12rgb_getYUVValue(float2 texCoord)
{
	float4 map = float4(2, 1, 0, 3);
	
	float fTargetX = 0.f;
	float2 coord = texCoord;
	coord.x /= 4.f;
	
	// corrd could now point to 4 different pixels in the Y-Plane
	// so we could not have a fraction of 0.0, 0.25, 0.5, 0.75 here
	// and have to decide which pixel to choose for Y
	// therefore we multiply it by four and get an index to access the pixel
	// but we have to map this value, to the real positions in the texture memory
	// because they are not in the original sequence (0 -> 2, 1 -> 1, 0 -> 2, 3 -> 3)
	//
	// for the uv plane, we have 8 target values in one source pixel, because of YUV420 format
	float fFraction = modf(coord.x, fTargetX);
	fFraction *= 4.f;

	coord.x = fTargetX + 0.5f;
	coord.y += 0.5f;
	coord /= fTextureSize;
	float4 raw = tex2D(texture0, coord);

	float yValue = raw[map[fFraction]];

	coord = texCoord;
	coord.x /= 8.f;
	coord.y /= 2.f;
	
	fFraction = modf(coord.x, fTargetX);
	coord.x = fTargetX + 0.5f;
	coord.y += 0.5f;

	fFraction *= 4.f;

	float2 uCoord = (coord + float2(fTextureSize[0] / 2.f, fTextureSize[1] / 2.f)) / fTextureSize;
	float2 vCoord = (coord + float2(fTextureSize[0] / 2.f, 0.f)) / fTextureSize;
	float4 rawU = tex2D(texture0, uCoord);
	float4 rawV = tex2D(texture0, vCoord);
	return float3(yValue, rawU[map[fFraction]], rawV[map[fFraction]]);
}


float2 nv12rgb_getUVValue(float2 texCoord)
{
	float fTargetX = texCoord.x / 4.f;
	float fFraction = modf(fTargetX, fTargetX);
	float fFirstPixel = step(0.5f, fFraction);
	float fFirstPixelInv = 1.f - fFirstPixel;

	float fTargetY = texCoord.y / 2.f;
	modf(fTargetY, fTargetY);

	float2 coord;
	coord.x = fTargetX + 0.5f + fTextureSize[0] / 2.f;
	coord.y = fTargetY + 0.5f;
	coord /= fTextureSize;
	float4 raw = tex2D(texture0, coord);

	float2 result;
	result[0] = raw[2] * fFirstPixelInv + raw[0] * fFirstPixel;
	result[1] = raw[1] * fFirstPixelInv + raw[3] * fFirstPixel;
	return result;
}

float4 yuv2rgb(float _y, float _u, float _v)
{
	float y = _y - 16.f/255.f; 
	float u = _u - 128.f/255.f; 
	float v = _v - 128.f/255.f; 
	
	float4 result; 
	result.a = 1.f; 
	result.b = saturate(y + 1.770f * u); 
	result.g = saturate(y - 0.344f * u - 0.714 * v);
	result.r = saturate(y + 1.403f * v);
	return result;
}

float4 nv12rgbMain(VS_OUTPUT In) : COLOR0
{
	float rawYValue = nv12rgb_getYValue(In.TexCoord);
	float2 rawUVValue = nv12rgb_getUVValue(In.TexCoord);
	return yuv2rgb(rawYValue, rawUVValue[0], rawUVValue[1]);
}

float4 yv12rgbMain(VS_OUTPUT In) : COLOR0
{
	float3 raw = yv12rgb_getYUVValue(In.TexCoord);
	return yuv2rgb(raw[0], raw[1], raw[2]);
}

/**
 * converts a YUY2 image to RGB
 */
float4 yuy2rgbMain(VS_OUTPUT In) : COLOR0
{
   // we have folowing pixelstuff in the sourcetexture
   // y0 u y1 v.. so we have to pick the right pixel from
   // the source texture.. and then decide which Y we
   // have to use for the final pixel calculations
   
   // first find the right source pixel
   float2 coord = In.TexCoord; 
   float targetX = 0.f;
   coord.x /= 2.f;
   
   // if firstSecondsPixel >= 0.5 then we have to use the 
   // Y1 later instead of Y0
   float fSecondPixel = modf(coord.x, targetX);
   fSecondPixel = step(0.5f, fSecondPixel);
   float fSecondPixelInv = 1.f - fSecondPixel;

   coord.x = targetX + 0.5;
   coord.y += 0.5;
   coord /= fTextureSize; 
   float4 raw = tex2D(texture0, coord);
   float rawYValue = raw.b * fSecondPixelInv + raw.r * fSecondPixel; 

   return yuv2rgb(rawYValue, raw.g, raw.a);
   //return yuv2rgb(rawYValue, raw.a, raw.g);
}

float rgb2Y(float4 rgb) {
	return 0.299f * rgb.r + 0.587f * rgb.g + 0.144f * rgb.b;
}

float rgb2U(float4 rgb) {
	return -0.149f * rgb.r - 0.298f * rgb.g + 0.436f * rgb.b + 0.5;
}

float rgb2V(float4 rgb) {
	return 0.615f * rgb.r - 0.515f * rgb.g - 0.100f * rgb.b + 0.5;
}

float4 rgb2yv12Main(VS_OUTPUT In) : COLOR0
{
	float2 fTextureSizeHalf = fTextureSize / 2;
	if (In.TexCoord.x <= fTextureSize.x / 4) {
		// YPlane
		float2 sourcePixelCoord = float2(In.TexCoord.x * 4, In.TexCoord.y);
		float4 result;
		result.b = rgb2Y(tex2D(texture0, sourcePixelCoord / fTextureSize));
		result.g = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(1, 0)) / fTextureSize));
		result.r = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(2, 0)) / fTextureSize));
		result.a = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(3, 0)) / fTextureSize));
		return result;
	}
	else if (In.TexCoord.x <= fTextureSizeHalf.x) {
		if (In.TexCoord.y < fTextureSizeHalf.y) {
			// u plane
			// pick the middle of 4 pixels for interpolation of the U-V values. It's this simple
			// in the shaders \o/
			//float2 sourcePixelCoord = float2((In.TexCoord.x - fTextureSizeHalf.x / 2) * 8 + 0.5f, In.TexCoord.y * 2 + 0.5f);
			float2 sourcePixelCoord = float2((In.TexCoord.x - fTextureSizeHalf.x / 2) * 8, In.TexCoord.y * 2);
			float4 result;
			result.b = rgb2U(tex2D(texture0, sourcePixelCoord / fTextureSize));
			result.g = rgb2U(tex2D(texture0, (sourcePixelCoord + float2(2, 0)) / fTextureSize));
			result.r = rgb2U(tex2D(texture0, (sourcePixelCoord + float2(4, 0)) / fTextureSize));
			result.a = rgb2U(tex2D(texture0, (sourcePixelCoord + float2(6, 0)) / fTextureSize));
			return result;
		} else {
			// v plane
			//float2 sourcePixelCoord = float2((In.TexCoord.x - fTextureSizeHalf.x / 2) * 8 + 0.5f, (In.TexCoord.y - fTextureSizeHalf.y) * 2 + 0.5f);
			float2 sourcePixelCoord = float2((In.TexCoord.x - fTextureSizeHalf.x / 2) * 8, (In.TexCoord.y - fTextureSizeHalf.y) * 2);
			float4 result;
			result.b = rgb2V(tex2D(texture0, sourcePixelCoord / fTextureSize));
			result.g = rgb2V(tex2D(texture0, (sourcePixelCoord + float2(2, 0)) / fTextureSize));
			result.r = rgb2V(tex2D(texture0, (sourcePixelCoord + float2(4, 0)) / fTextureSize));
			result.a = rgb2V(tex2D(texture0, (sourcePixelCoord + float2(6, 0)) / fTextureSize));
			return result;
		}
	} else {
		return float4(0.f, 0.f, 0.f, 0.f);
	}
}

float4 rgb2nv12Main(VS_OUTPUT In) : COLOR0
{
	float2 fTextureSizeHalf = fTextureSize / 2;
	float2 fTextureSizeQuater = fTextureSize / 4;
	if (In.TexCoord.x <= fTextureSizeQuater.x) {
		// YPlane
		float2 sourcePixelCoord = float2(In.TexCoord.x * 4, In.TexCoord.y);
		float4 result;
		result.b = rgb2Y(tex2D(texture0, sourcePixelCoord / fTextureSize));
		result.g = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(1, 0)) / fTextureSize));
		result.r = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(2, 0)) / fTextureSize));
		result.a = rgb2Y(tex2D(texture0, (sourcePixelCoord + float2(3, 0)) / fTextureSize));
		return result;
	}
	else if (In.TexCoord.x <= fTextureSizeQuater.x * 3) {
		if (In.TexCoord.y <= fTextureSizeHalf.y) {
			// uv plane
			// pick the middle of 4 pixels for interpolation of the U-V values. It's this simple
			// in the shaders \o/
			float2 sourcePixelCoord = float2((In.TexCoord.x - fTextureSizeQuater.x) * 4 + 0.5f, In.TexCoord.y * 2 + 0.5f);
			float4 p1 = tex2D(texture0, sourcePixelCoord / fTextureSize);
			float4 p2 = tex2D(texture0, (sourcePixelCoord + float2(2, 0)) / fTextureSize);

			float4 result;
			result.b = rgb2U(p1);
			result.g = rgb2V(p1);
			result.r = rgb2U(p2);
			result.a = rgb2V(p2);
			return result;
		} else {
			return float4(0.f, 0.f, 0.f, 0.f);
		}
	} else {
		return float4(0.f, 0.f, 0.f, 0.f);
	}
}