/*
   AA & xBR 2D+3D FXAA Hybrid shader + NTSC TV colors emulation, coded by 2018 guest(r) 
   ( Special regards to Hyllian's 3D detection work.)
   ( FXAA ported form Asmodean's PsxFX 2.0 shader, visit https://github.com/Asmodean-/PsxFX to get it.)

   Hyllian's xBR Level 2 shader w. accuracy feature - added by guest.r
   Copyright (C) 2011/2014 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.

*/


// variables (you can edit them)

const vec3  c_ch = vec3(1.0,1.0,1.0);  //  rgb color channel intensity
const float    a = 1.00 ;              //  saturation  usable values between 0.0...2.0
const float    b = 1.00 ;              //  brightness 
const float    c = 0.00 ;              //  contrast    usable values between 0.0...1.0

const float xbrthreshold  = 0.020;     // xBR algorithm threshold (3D -> 0.0....0.05 -> 2D)


#define FxaaSubpixMax 8.00             //[0.50 to 4.00] Amount of subpixel anti-aliasing. Higher values for a stronger effect, & edge coverage.
#define FxaaQuality 4                  //[1|2|3|4] Overall Fxaa quality preset (pixel coverage). 1: Low, 2: Medium, 3: High, 4: Ultra.



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


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


// FXAA code

//Average relative luminance
const vec3 lumCoeff = vec3(0.2126729, 0.7151522, 0.0721750);
float AvgLuminance(vec3 color)
{
    return sqrt(dot(color * color, lumCoeff));
}


#if FxaaQuality == 4
#define FxaaEdgeThreshold 1.0/8.0
#define FxaaEdgeThresholdMin 1.0/128.0
#elif FxaaQuality == 3
#define FxaaEdgeThreshold 0.125
#define FxaaEdgeThresholdMin 0.0312
#elif FxaaQuality == 2
#define FxaaEdgeThreshold 0.166
#define FxaaEdgeThresholdMin 0.0625
#elif FxaaQuality == 1
#define FxaaEdgeThreshold 0.250
#define FxaaEdgeThresholdMin 0.0833
#endif

const float FXAA_SPAN_MAX = FxaaSubpixMax;
const float FXAA_REDUCE_MUL = FxaaEdgeThreshold;
const float FXAA_REDUCE_MIN = FxaaEdgeThresholdMin;

vec2 NWoffset = vec2(-OGL2InvSize.x, -OGL2InvSize.y);
vec2 NEoffset = vec2(+OGL2InvSize.x, -OGL2InvSize.y);

vec3 FXAA(vec2 texcoord)
{
    vec3 rgbNW = texture2D(OGL2Texture, texcoord + NWoffset).xyz;
    vec3 rgbNE = texture2D(OGL2Texture, texcoord + NEoffset).xyz;
    vec3 rgbSW = texture2D(OGL2Texture, texcoord - NEoffset).xyz;
    vec3 rgbSE = texture2D(OGL2Texture, texcoord - NWoffset).xyz;
    vec3 rgbM  = texture2D(OGL2Texture, texcoord).xyz;

    float lumaNW = AvgLuminance(rgbNW);
    float lumaNE = AvgLuminance(rgbNE);
    float lumaSW = AvgLuminance(rgbSW);
    float lumaSE = AvgLuminance(rgbSE);
    float lumaM  = AvgLuminance(rgbM);

    float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
    float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));

    vec2 dir;
    dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
    dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));

    float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
    float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
    
    dir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
    dir * rcpDirMin)) * OGL2InvSize.xy;

    vec3 rgbA = (1.0/2.0) * (
        texture2D(OGL2Texture, texcoord + dir * (1.0/3.0 - 0.5)).xyz +
        texture2D(OGL2Texture, texcoord + dir * (2.0/3.0 - 0.5)).xyz);
    
    vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
        texture2D(OGL2Texture, texcoord + dir * (0.0/3.0 - 0.5)).xyz +
        texture2D(OGL2Texture, texcoord + dir * (3.0/3.0 - 0.5)).xyz);
    
    float lumaB = AvgLuminance(rgbB);

    if((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA;
    else return rgbB; 
}


// Hyllian's xBR Level 2 shader code

// Uncomment just one of the three params below to choose the corner detection
//#define CORNER_A
//#define CORNER_B
#define CORNER_C
//#define CORNER_D

#ifndef CORNER_A
  #define SMOOTH_TIPS
#endif

#define XBR_SCALE 3.0
#define XBR_Y_WEIGHT 48.0
#define XBR_EQ_THRESHOLD 15.0
#define XBR_LV2_COEFFICIENT 2.0

#define lv2_cf XBR_LV2_COEFFICIENT
 
const   float coef          = 2.0;
const   vec3  rgbw          = vec3(14.352, 28.176, 5.472);
const   vec4  eq_threshold  = vec4(15.0, 15.0, 15.0, 15.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 );
const   vec2 x2         = vec2( 0.001953125 , 0.0);
const   vec2 y2         = vec2( 0.0 , 0.00390625 );
const   vec4 xy         = vec4( 0.0009765625, 0.001953125,-0.0009765625,-0.001953125);  
const   vec4 zw         = vec4( 0.001953125 , 0.001953125,-0.001953125 ,-0.001953125);  
const   vec4 wz         = vec4( 0.0009765625, 0.00390625 ,-0.0009765625,-0.00390625 );  

const  vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
const  vec4 Bo = vec4( 1.0,  1.0, -1.0,-1.0 );
const  vec4 Co = vec4( 1.5,  0.5, -0.5, 0.5 );
const  vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
const  vec4 Bx = vec4( 0.5,  2.0, -0.5,-2.0 );
const  vec4 Cx = vec4( 1.0,  1.0, -0.5, 0.0 );
const  vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
const  vec4 By = vec4( 2.0,  0.5, -2.0,-0.5 );
const  vec4 Cy = vec4( 2.0,  0.0, -1.0, 0.5 );
const  vec4 Ci = vec4(0.25, 0.25, 0.25, 0.25);


// Difference between vector components.
vec4 df(vec4 A, vec4 B)
{
    return vec4(abs(A-B));
}

// Compare two vectors and return their components are different.
vec4 diff(vec4 A, vec4 B)
{
    return vec4(notEqual(A, B));
}

// Determine if two vector components are equal based on a threshold.
vec4 eq(vec4 A, vec4 B)
{
    return (step(df(A, B), vec4(XBR_EQ_THRESHOLD)));
}

// Determine if two vector components are NOT equal based on a threshold.
vec4 neq(vec4 A, vec4 B)
{
    return (vec4(1.0, 1.0, 1.0, 1.0) - eq(A, B));
}

// Weighted distance.
vec4 wd(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h)
{
    return (df(a,b) + df(a,c) + df(d,e) + df(d,f) + 4.0*df(g,h));
}

float c_df(vec3 c1, vec3 c2) 
{
      vec3 df = abs(c1 - c2);
      return df.r + df.g + df.b;
}



vec3 xBR (vec2 texcoord) {

    vec4 edri, edr, edr_l, edr_u, px; // px = pixel, edr = edge detection rule
    vec4 irlv0, irlv1, irlv2l, irlv2u;
    vec4 fx, fx_l, fx_u; // inequations of straight lines.

    vec2 fp  = fract(texcoord*OGLSize);
    vec2 TexCoord_0 = texcoord-fp*OGLInvSize + 0.5*OGLInvSize;
	
// AutoScale code

#ifndef XBR_SCALE	
    vec2 video_size = vec2(1024.0, 512.0)*OGL2Size.zw;
    vec2 tcoord = vec2(gl_TexCoord[1].x,1.0-gl_TexCoord[1].y);
    vec2 output_size = gl_FragCoord.xy/tcoord;
    vec2 scale = output_size/video_size;
    float XBR_SCALE = scale.x*0.8;
#endif

    vec4 delta   = vec4(1.0/XBR_SCALE, 1.0/XBR_SCALE, 1.0/XBR_SCALE, 1.0/XBR_SCALE);
    vec4 delta_l = vec4(0.5/XBR_SCALE, 1.0/XBR_SCALE, 0.5/XBR_SCALE, 1.0/XBR_SCALE);
    vec4 delta_u = delta_l.yxwz;
	
    vec3 A  = texture2D(OGL2Texture, TexCoord_0 + xy.zw ).xyz;
    vec3 B  = texture2D(OGL2Texture, TexCoord_0     -dy ).xyz;
    vec3 C  = texture2D(OGL2Texture, TexCoord_0 + xy.xw ).xyz;
    vec3 D  = texture2D(OGL2Texture, TexCoord_0 - dx    ).xyz;
    vec3 E  = texture2D(OGL2Texture, TexCoord_0         ).xyz;
    vec3 F  = texture2D(OGL2Texture, TexCoord_0 + dx    ).xyz;
    vec3 G  = texture2D(OGL2Texture, TexCoord_0 + xy.zy ).xyz;
    vec3 H  = texture2D(OGL2Texture, TexCoord_0     +dy ).xyz;
    vec3 I  = texture2D(OGL2Texture, TexCoord_0 + xy.xy ).xyz;
    vec3 A1 = texture2D(OGL2Texture, TexCoord_0 + wz.zw ).xyz;
    vec3 C1 = texture2D(OGL2Texture, TexCoord_0 + wz.xw ).xyz;
    vec3 A0 = texture2D(OGL2Texture, TexCoord_0 + zw.zw ).xyz;
    vec3 G0 = texture2D(OGL2Texture, TexCoord_0 + zw.zy ).xyz;
    vec3 C4 = texture2D(OGL2Texture, TexCoord_0 + zw.xw ).xyz;
    vec3 I4 = texture2D(OGL2Texture, TexCoord_0 + zw.xy ).xyz;
    vec3 G5 = texture2D(OGL2Texture, TexCoord_0 + wz.zy ).xyz;
    vec3 I5 = texture2D(OGL2Texture, TexCoord_0 + wz.xy ).xyz;
    vec3 B1 = texture2D(OGL2Texture, TexCoord_0 - y2    ).xyz;
    vec3 D0 = texture2D(OGL2Texture, TexCoord_0 - x2    ).xyz;
    vec3 H5 = texture2D(OGL2Texture, TexCoord_0 + y2    ).xyz;
    vec3 F4 = texture2D(OGL2Texture, TexCoord_0 + x2    ).xyz;

    vec4 b  = vec4(dot(B ,rgbw), dot(D ,rgbw), dot(H ,rgbw), dot(F ,rgbw));
    vec4 c  = vec4(dot(C ,rgbw), dot(A ,rgbw), dot(G ,rgbw), dot(I ,rgbw));
    vec4 d  = b.yzwx;
    vec4 e  = vec4(dot(E,rgbw));
    vec4 f  = b.wxyz;
    vec4 g  = c.zwxy;
    vec4 h  = b.zwxy;
    vec4 i  = c.wxyz;
    vec4 i4 = vec4(dot(I4,rgbw), dot(C1,rgbw), dot(A0,rgbw), dot(G5,rgbw));
    vec4 i5 = vec4(dot(I5,rgbw), dot(C4,rgbw), dot(A1,rgbw), dot(G0,rgbw));
    vec4 h5 = vec4(dot(H5,rgbw), dot(F4,rgbw), dot(B1,rgbw), dot(D0,rgbw));
    vec4 f4 = h5.yzwx;
    
    // These inequations define the line below which interpolation occurs.
    fx   = (Ao*fp.y+Bo*fp.x); 
    fx_l = (Ax*fp.y+Bx*fp.x);
    fx_u = (Ay*fp.y+By*fp.x);

    irlv1 = irlv0 = diff(e,f) * diff(e,h);

#ifdef CORNER_B
    irlv1      = (irlv0 * ( neq(f,b) * neq(h,d) + eq(e,i) * neq(f,i4) * neq(h,i5) + eq(e,g) + eq(e,c) ) );
#endif
#ifdef CORNER_D
    vec4 c1 = i4.yzwx;
    vec4 g0 = i5.wxyz;
    irlv1     = (irlv0  *  ( neq(f,b) * neq(h,d) + eq(e,i) * neq(f,i4) * neq(h,i5) + eq(e,g) + eq(e,c) ) * (diff(f,f4) * diff(f,i) + diff(h,h5) * diff(h,i) + diff(h,g) + diff(f,c) + eq(b,c1) * eq(d,g0)));
#endif
#ifdef CORNER_C
    irlv1     = (irlv0  * ( neq(f,b) * neq(f,c) + neq(h,d) * neq(h,g) + eq(e,i) * (neq(f,f4) * neq(f,i4) + neq(h,h5) * neq(h,i5)) + eq(e,g) + eq(e,c)) );
#endif

    irlv2l = diff(e,g) * diff(d,g);
    irlv2u = diff(e,c) * diff(b,c);

    vec4 fx45i = clamp((fx   + delta   -Co - Ci)/(2*delta  ), 0.0, 1.0);
    vec4 fx45  = clamp((fx   + delta   -Co     )/(2*delta  ), 0.0, 1.0);
    vec4 fx30  = clamp((fx_l + delta_l -Cx     )/(2*delta_l), 0.0, 1.0);
    vec4 fx60  = clamp((fx_u + delta_u -Cy     )/(2*delta_u), 0.0, 1.0);

    vec4 w1, w2;
    w1.x = dot(abs(E-C),rgbw) + dot(abs(E-G),rgbw) + dot(abs(I-H5),rgbw) + dot(abs(I-F4),rgbw) + 4.0*dot(abs(H-F),rgbw);
    w1.y = dot(abs(E-A),rgbw) + dot(abs(E-I),rgbw) + dot(abs(C-F4),rgbw) + dot(abs(C-B1),rgbw) + 4.0*dot(abs(F-B),rgbw);  
    w1.z = dot(abs(E-G),rgbw) + dot(abs(E-C),rgbw) + dot(abs(A-B1),rgbw) + dot(abs(A-D0),rgbw) + 4.0*dot(abs(B-D),rgbw);
    w1.w = dot(abs(E-I),rgbw) + dot(abs(E-A),rgbw) + dot(abs(G-D0),rgbw) + dot(abs(G-H5),rgbw) + 4.0*dot(abs(D-H),rgbw);

    w2.x = dot(abs(H-D),rgbw) + dot(abs(H-I5),rgbw) + dot(abs(F-I4),rgbw) + dot(abs(F-B),rgbw) + 4.0*dot(abs(E-I),rgbw);
    w2.y = dot(abs(F-H),rgbw) + dot(abs(F-C4),rgbw) + dot(abs(B-C1),rgbw) + dot(abs(B-D),rgbw) + 4.0*dot(abs(E-C),rgbw);
    w2.z = dot(abs(B-F),rgbw) + dot(abs(B-A1),rgbw) + dot(abs(D-A0),rgbw) + dot(abs(D-H),rgbw) + 4.0*dot(abs(E-A),rgbw); 
    w2.w = dot(abs(D-B),rgbw) + dot(abs(D-G0),rgbw) + dot(abs(H-G5),rgbw) + dot(abs(H-F),rgbw) + 4.0*dot(abs(E-G),rgbw);

    edri  = step(w1, w2) * irlv0;
    edr   = step(w1 + vec4(0.1, 0.1, 0.1, 0.1), w2) * step(vec4(0.5, 0.5, 0.5, 0.5), irlv1);
	
    w1.x = dot(abs(F-G),rgbw); w1.y = dot(abs(B-I),rgbw); w1.z = dot(abs(D-C),rgbw); w1.w = dot(abs(H-A),rgbw);
    w2.x = dot(abs(H-C),rgbw); w2.y = dot(abs(F-A),rgbw); w2.z = dot(abs(B-G),rgbw); w2.w = dot(abs(D-I),rgbw);

    edr_l = step( lv2_cf*w1, w2 ) * irlv2l * edr;
    edr_u = step( lv2_cf*w2, w1 ) * irlv2u * edr;

    fx45  = edr   * fx45;
    fx30  = edr_l * fx30;
    fx60  = edr_u * fx60;
    fx45i = edri  * fx45i;

    w1.x = dot(abs(E-F),rgbw); w1.y = dot(abs(E-B),rgbw); w1.z = dot(abs(E-D),rgbw); w1.w = dot(abs(E-H),rgbw);
//w2.x = dot(abs(E-H),rgbw); w2.y = dot(abs(E-F),rgbw); w2.z = dot(abs(E-B),rgbw); w2.w = dot(abs(E-D),rgbw);
    w2   = w1.wxyz;
	
    px = step(w1, w2);

#ifdef SMOOTH_TIPS
    vec4 maximos = max(max(fx30, fx60), max(fx45, fx45i));
#endif
#ifndef SMOOTH_TIPS
    vec4 maximos = max(max(fx30, fx60), fx45);
#endif

    vec3 res1 = E;
    res1 = mix(res1, mix(H, F, px.x), maximos.x);
    res1 = mix(res1, mix(B, D, px.z), maximos.z);
    
    vec3 res2 = E;
    res2 = mix(res2, mix(F, B, px.y), maximos.y);
    res2 = mix(res2, mix(D, H, px.w), maximos.w);
    
    vec3 res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));

    return res;    
} 


float contrast(float x)
{ 
	float y = x*1.1547-1.0;
	y = (sin(y*1.5707)+1.1)*0.83;
	return (1.0-c)*x + c*y;
}



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);
}

// 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 ) ); 
}

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)),diff2D(texcoord));

	vec3 color;

	if (maxdiff <= xbrthreshold)  { color = xBR(gl_TexCoord[0].xy);}
        else  color = FXAA(gl_TexCoord[0].xy);

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

	//float x = sqrt(dot(color,color));

	//color.r = pow(color.r+0.005,a);
	//color.g = pow(color.g+0.005,a);
	//color.b = pow(color.b+0.005,a);

	//gl_FragColor.xyz = contrast(x)*normalize(color*c_ch)*b;
	gl_FragColor.xyz = color;
}
