/*
   AA & DDT Sharp 2D+3D + NTSC colors shader + CRT-Lottes ShadowMask, coded by 2016 guest(r)

   ( Special regards to Hyllian's 3D detection work.)
   ( Another nice version of CRT-Lottes at: https://github.com/Asmodean-/PsxFX/blob/master/PsxFX/gpuPeteOGL2.slf#L2652 ) 	

   AA Shader 4.o
   Copyright (C) 2014 guest(r) - guest.r@gmail.com

   Hyllian's DDT Sharp Shader
   Copyright (C) 2011-2016 Hyllian/Jararaca - sergiogdb@gmail.com

   SimoneT  NTSC TV colors emulation 1.0 Shader
   Copyright (C) 2016 SimoneT - simone1tarditi@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.

*/


const float NTSC      = 0.50;          // NTSC Color emulation strength, 0.0 -> OFF, 1.0 -> FULL
const float threshold = 0.02;          // 2D-3D algorithm threshold (3D -> 0.0....0.05 -> 2D)
const float offset    = 0.325;         // AA offset (0.25 - sharp, 0.325 - middle, 0.50 - soft. Use greater offset for windowed gameplay)

#define shadowMask 3     // 1, 2, 3 or 4
#define maskDark 0.65
#define maskLight 1.25


uniform sampler2D OGL2Texture;
uniform vec4 OGL2Size;
uniform vec4 OGL2Param;


const vec3 dt = vec3(1.0,1.0,1.0);


const   vec2 OGLSize    = vec2( 1024.0, 512.0);
const   vec2 OGLInvSize = vec2( 0.0009765625, 0.001953125); 
const   vec2 dx         = vec2( 0.0009765625, 0.0);
const   vec2 dy         = vec2( 0.0, 0.001953125 );


// AA Shader 4.o code

vec4 yx = vec4(1.0/4096,1.0/2048,-1.0/4096,-1.0/2048)*offset;

vec3 AA402x (vec2 texcoord) {

	vec3 s00 = texture2D(OGL2Texture, texcoord + yx.zw).xyz; 
	vec3 s20 = texture2D(OGL2Texture, texcoord + yx.xw).xyz; 
	vec3 s22 = texture2D(OGL2Texture, texcoord + yx.xy).xyz; 
	vec3 s02 = texture2D(OGL2Texture, texcoord + yx.zy).xyz; 

	float m1=dot(abs(s00-s22),dt)+0.001;
	float m2=dot(abs(s02-s20),dt)+0.001;

	return .5*(m2*(s00+s22)+m1*(s02+s20))/(m1+m2);
}

vec3 AA40 (sampler2D OGL2Texture, vec2 texcoord) {
	
	// Calculating texel coordinates
	vec2 size     = vec2(4096.0,2048.0)/offset;
	vec2 inv_size = vec2(1.0/4096.0,1.0/2048.0)*offset;
	
	vec2 OGL2Pos = texcoord * size;
	vec2 fp = fract(OGL2Pos);
	vec2 dx = vec2(inv_size.x,0.0);
	vec2 dy = vec2(0.0, inv_size.y);
	vec2 g1 = vec2(inv_size.x,inv_size.y);
	vec2 g2 = vec2(-inv_size.x,inv_size.y);
	
	vec2 pC4 = floor(OGL2Pos) * inv_size + 0.5*inv_size;	
	
	// Reading the texels
	vec3 C0 = AA402x(pC4 - g1); 
	vec3 C1 = AA402x(pC4 - dy);
	vec3 C2 = AA402x(pC4 - g2);
	vec3 C3 = AA402x(pC4 - dx);
	vec3 C4 = AA402x(pC4     );
	vec3 C5 = AA402x(pC4 + dx);
	vec3 C6 = AA402x(pC4 + g2);
	vec3 C7 = AA402x(pC4 + dy);
	vec3 C8 = AA402x(pC4 + g1);
	
	vec3 ul, ur, dl, dr;
	float m1, m2;
	
	m1 = dot(abs(C0-C4),dt)+0.001;
	m2 = dot(abs(C1-C3),dt)+0.001;
	ul = (m2*(C0+C4)+m1*(C1+C3))/(m1+m2);  
	
	m1 = dot(abs(C1-C5),dt)+0.001;
	m2 = dot(abs(C2-C4),dt)+0.001;
	ur = (m2*(C1+C5)+m1*(C2+C4))/(m1+m2);
	
	m1 = dot(abs(C3-C7),dt)+0.001;
	m2 = dot(abs(C6-C4),dt)+0.001;
	dl = (m2*(C3+C7)+m1*(C6+C4))/(m1+m2);
	
	m1 = dot(abs(C4-C8),dt)+0.001;
	m2 = dot(abs(C5-C7),dt)+0.001;
	dr = (m2*(C4+C8)+m1*(C5+C7))/(m1+m2);
	
	vec3 c11 = 0.5*((dr*fp.x+dl*(1-fp.x))*fp.y+(ur*fp.x+ul*(1-fp.x))*(1-fp.y));

	return c11; 
}


// DDT Sharp code

#define WP1  1.0
#define WP2  1.0
#define WP3 -1.0

const vec3 Y = vec3(.2126, .7152, .0722);

float luma(vec3 color)
{
  return dot(color, Y);
}

vec3 bilinear(float p, float q, vec3 A, vec3 B, vec3 C, vec3 D)
{
	return ((1-p)*(1-q)*A + p*(1-q)*B + (1-p)*q*C + p*q*D);
}

vec3 DDTSharp (sampler2D OGL2Texture, vec2 texcoord)
{	
	
	vec2 pos = fract(texcoord*OGLSize)-vec2(0.5, 0.5); // pos = pixel position
	vec2 dir = sign(pos); // dir = pixel direction

	vec2 fp  = fract(texcoord*OGLSize);
	vec2 texcoord1 = texcoord-fp*OGLInvSize + 0.5*OGLInvSize;

	vec2 g1 = dir*dx;
	vec2 g2 = dir*dy;

//    A1 B1
// A0 A  B  B2
// C0 C  D  D2
//    C3 D3

	vec3 A  = texture2D(OGL2Texture, texcoord1       ).xyz;
	vec3 B  = texture2D(OGL2Texture, texcoord1 +g1   ).xyz;
	vec3 C  = texture2D(OGL2Texture, texcoord1    +g2).xyz;
	vec3 D  = texture2D(OGL2Texture, texcoord1 +g1+g2).xyz;

	vec3 A1 = texture2D(OGL2Texture, texcoord1    -g2).xyz;
	vec3 B1 = texture2D(OGL2Texture, texcoord1 +g1-g2).xyz;
	vec3 A0 = texture2D(OGL2Texture, texcoord1 -g1   ).xyz;
	vec3 C0 = texture2D(OGL2Texture, texcoord1 -g1+g2).xyz;

	vec3 B2 = texture2D(OGL2Texture, texcoord1 +2*g1     ).xyz;
	vec3 D2 = texture2D(OGL2Texture, texcoord1 +2*g1+  g2).xyz;
	vec3 C3 = texture2D(OGL2Texture, texcoord1      +2*g2).xyz;
	vec3 D3 = texture2D(OGL2Texture, texcoord1   +g1+2*g2).xyz;

	float a  = luma(A);
	float b  = luma(B);
	float c  = luma(C);
	float d  = luma(D);

	float a1 = luma(A1);
	float b1 = luma(B1);
	float a0 = luma(A0);
	float c0 = luma(C0);

	float b2 = luma(B2);
	float d2 = luma(D2);
	float c3 = luma(C3);
	float d3 = luma(D3);

	float p = abs(pos.x);
	float q = abs(pos.y);

	float k = distance(pos,g1);
	float l = distance(pos,g2);

	float wd1 = WP1*abs(a-d) + WP2*(abs(b-a1) + abs(b-d2) + abs(c-a0) + abs(c-d3)) + WP3*(abs(a1-d2) + abs(a0-d3));
	float wd2 = WP1*abs(b-c) + WP2*(abs(a-b1) + abs(a-c0) + abs(d-b2) + abs(d-c3)) + WP3*(abs(b1-c0) + abs(b2-c3));

	if ( wd1 < wd2 )
	{
		if (k < l)
		{
			C = A + D - B;
		}
		else
		{
			B = A + D - C;
		}
	}
	else if (wd1 > wd2)
	{
		D = B + C - A;
	}

	vec3 color = clamp (bilinear(p, q, A, B, C, D), 0.0, 1.0);

	return color;
} 


// conversion from NTSC RGB Reference White D65 ( color space used by NA/Japan TV's ) to XYZ

vec3 ntscrgb_2_xyz( vec3 c ) {

    //Inverse Companding
    vec3 v = vec3( pow( c.r, 2.2 ), pow( c.g, 2.2 ), pow( c.b, 2.2  ) ); 
    return v *
        mat3(  0.6068909,  0.1735011,  0.2003480,
 	       0.2989164,  0.5865990,  0.1144845,
 	       0.0000000,  0.0660957,  1.1162243 );
}

// conversion from XYZ to sRGB Reference White D65 ( color space used by windows ) 

vec3 xyz_2_srgb( vec3 c ) {

    vec3 v =  c *         
	mat3(  3.2404542, -1.5371385, -0.4985314,
	      -0.9692660,  1.8760108,  0.0415560,
 	       0.0556434, -0.2040259,  1.0572252 );

    //Companding
    v.r = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;
    v.g = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;
    v.b = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;
    return v;
}

// NTSC RGB to sRGB

vec3 ntscrgb_2_srgb( vec3 c ) { 
     return xyz_2_srgb( ntscrgb_2_xyz( c ) ); 
}



float diff2D (vec2 coord)
{
	vec3 c00 = texture2D(OGL2Texture, coord + 0.25*(-dx-dy)).xyz; 
	vec3 c20 = texture2D(OGL2Texture, coord + 0.25*( dx-dy)).xyz; 
	vec3 c02 = texture2D(OGL2Texture, coord + 0.25*(-dx+dy)).xyz; 
	vec3 c22 = texture2D(OGL2Texture, coord + 0.25*( dx+dy)).xyz;

	vec3 mn  = min(min(c00,c20),min(c02,c22)); 
	vec3 mx  = max(max(c00,c20),max(c02,c22));

	return dot(mx-mn,dt);
}


// CRT-Lottes code, Only ShadowMask code is used ATM

// Shadow mask 
vec3 Mask(vec2 pos){
  vec3 mask=vec3(maskDark,maskDark,maskDark);

  // Very compressed TV style shadow mask.
  if (shadowMask == 1) {
    float line=maskLight;
    float odd=0.0;
    if(fract(pos.x/6.0)<0.5)odd=1.0;
    if(fract((pos.y+odd)/2.0)<0.5)line=maskDark;  
    pos.x=fract(pos.x/3.0);
   
    if(pos.x<0.333)mask.r=maskLight;
    else if(pos.x<0.666)mask.g=maskLight;
    else mask.b=maskLight;
    mask*=line;  
  } 

  // Aperture-grille.
  else if (shadowMask == 2) {
    pos.x=fract(pos.x/3.0);

    if(pos.x<0.333)mask.r=maskLight;
    else if(pos.x<0.666)mask.g=maskLight;
    else mask.b=maskLight;
  } 

  // Stretched VGA style shadow mask (same as prior shaders).
  else if (shadowMask == 3) {
    pos.x+=pos.y*3.0;
    pos.x=fract(pos.x/6.0);

    if(pos.x<0.333)mask.r=maskLight;
    else if(pos.x<0.666)mask.g=maskLight;
    else mask.b=maskLight;
  }

  // VGA style shadow mask.
  else if (shadowMask == 4) {
    pos.xy=floor(pos.xy*vec2(1.0,0.5));
    pos.x+=pos.y*3.0;
    pos.x=fract(pos.x/6.0);

    if(pos.x<0.333)mask.r=maskLight;
    else if(pos.x<0.666)mask.g=maskLight;
    else mask.b=maskLight;
  }

  return mask;
  }    


void main()
{

	vec2 fp  = fract(gl_TexCoord[0].xy*OGLSize);
	vec2 texcoord = gl_TexCoord[0].xy-fp*OGLInvSize + 0.5*OGLInvSize;
	vec2 locator  = sign(fp-vec2(0.5000000001,0.5000000001));
	float maxdiff = max(max(diff2D(texcoord+locator*dx),diff2D(texcoord+locator*dy)),max(diff2D(texcoord),diff2D(texcoord+locator*(dx+dy))));

	vec3 color;

	if (maxdiff <= threshold) { color = DDTSharp(OGL2Texture, gl_TexCoord[0].xy+vec2(0.00000001)).xyz;} 
        else color = AA40(OGL2Texture, gl_TexCoord[0].xy).xyz;

	color = mix(color, ntscrgb_2_srgb( color ), NTSC);

	// Only Shadowmask feature supported for now
	color*= Mask(gl_FragCoord.xy); // Special SimoneT CRT-Lottes Shadowmask fix
 
	gl_FragColor.xyz = color;
}
