/*
   Hyllian's 2xBR v3.8c Hybrid + DDT 2D+3D with FXAA + NTSC TV colors emulation Shader - merged by 2016 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 2xBR v3.8c+ReverseAA (squared) Shader - v4
   Copyright (C) 2011/2012 Hyllian/Jararaca - sergiogdb@gmail.com

   Hyllian's Data Dependent Triangulation 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.

   *This shader is best used for windowed gameplay up to 1024x768 (OGL2 texture filtering 4).

*/

/*
 *  ReverseAA part of the code
 *
 *  Copyright (c) 2012, Christoph Feck <christoph@maxiom.de>
 *  All Rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *    * Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */


const float NTSC      = 0.50;          // NTSC Color emulation strength, 0.0 -> OFF, 1.0 -> FULL
const float threshold = 0.020;         // xBR algorithm threshold (3D -> 0.0....0.05 -> 2D)
const float gamma     = 1.075;         // Gamma correction, increase when games are dark 

#define FxaaSubpixMax 2.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 OGL2Size;
uniform vec4 OGL2Param;
uniform vec4 OGL2InvSize;

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 0.063
#define FxaaEdgeThresholdMin 0.000
#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 (sampler2D OGL2Texture, 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; 
}


vec2 size = vec2(2048.0,1024.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);

} 


//  Hyllian's 2xBR v3.8c+ReverseAA (squared) Shader - v4 code

const   float coef          = 2.0;
const   vec3  rgbw          = vec3(14.352, 28.176, 5.472);
const   vec4  delta         = vec4(0.5, 0.5, 0.5, 0.5);

const   vec4  eq_threshold  = vec4(15.0, 15.0, 15.0, 15.0);
const   float sharpness     = 0.65;


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.00390625 );  
const   vec4 wz         = vec4( 0.0009765625, 0.00390625 ,-0.0009765625,-0.00390625 );  



vec4 noteq(vec4 A, vec4 B)
{
	return vec4(notEqual(A, B));
}

vec4 not(vec4 A)
{
	return vec4(1.0)-A;
}

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

vec4 eq(vec4 A, vec4 B)
{
	return vec4(lessThan(df(A, B),eq_threshold));
}

vec4 weighted_distance(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 (sampler2D OGL2Texture, vec2 texcoord) {

    vec4 edr, edr_left, edr_up;                 // px = pixel, edr = edge detection rule
    vec4 interp_restriction_lv1, interp_restriction_lv2_left, interp_restriction_lv2_up;
    vec4 nc30, nc60, nc45;                     // new_color
    vec4 fx, fx_left, fx_up, final_fx;              // inequations of straight lines.
    vec3 res1, res2, pix1, pix2;
    bvec4 nc, px;
    float blend1, blend2; 

    vec2 fp  = fract(texcoord*OGLSize);

    vec2 TexCoord_0 = texcoord-fp*OGLInvSize + 0.5*OGLInvSize;

    if (fp.x < 0.5) fp.x = 0.25; else fp.x = 0.75;
    if (fp.y < 0.5) fp.y = 0.25; else fp.y = 0.75;

    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;
    vec4 c1 = i4.yzwx;
    
vec4 g0 = i5.wxyz;
    
    vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
    vec4 Bo = vec4( 1.0,  1.0, -1.0,-1.0 );
    vec4 Co = vec4( 1.5,  0.5, -0.5, 0.5 );
    vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
    vec4 Bx = vec4( 0.5,  2.0, -0.5,-2.0 );
    vec4 Cx = vec4( 1.0,  1.0, -0.5, 0.0 );
    vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
    vec4 By = vec4( 2.0,  0.5, -2.0,-0.5 );
    vec4 Cy = vec4( 2.0,  0.0, -1.0, 0.5 );
    
 // These inequations define the line below which interpolation occurs.
	
    fx      = (Ao*fp.y+Bo*fp.x); 
	
    fx_left = (Ax*fp.y+Bx*fp.x);
	
    fx_up   = (Ay*fp.y+By*fp.x);

    interp_restriction_lv1      = sign(noteq(e,f)*noteq(e,h)*(not(eq(f,b))* not(eq(h,d)) + eq(e,i) * not(eq(f,i4)) * not(eq(h,i5)) + eq(e,g) + eq(e,c) )  * (noteq(f,f4)* noteq(f,i) + noteq(h,h5) * noteq(h,i) + noteq(h,g) + noteq(f,c) + eq(b,c1) * eq(d,g0)));
    interp_restriction_lv2_left = (noteq(e,g) * noteq(d,g));
    interp_restriction_lv2_up   = (noteq(e,c) * noteq(b,c));

    vec4 fx45 = smoothstep(Co - delta, Co + delta, fx);
    vec4 fx30 = smoothstep(Cx - delta, Cx + delta, fx_left);
    vec4 fx60 = smoothstep(Cy - delta, Cy + delta, fx_up);
	
    edr      = vec4(lessThan(weighted_distance( e, c, g, i, h5, f4, h, f)+vec4(3.5), weighted_distance( h, d, i5, f, i4, b, e, i)))*interp_restriction_lv1;
    edr_left = vec4(lessThanEqual(coef*df(f,g),df(h,c)))*interp_restriction_lv2_left; 
    edr_up   = vec4(greaterThanEqual(df(f,g),coef*df(h,c)))*interp_restriction_lv2_up;

    nc45 = ( edr *             sign(fx45));
    nc30 = ( edr * edr_left *  sign(fx30));
    nc60 = ( edr * edr_up   *  sign(fx60));
	
    px = lessThanEqual(df(e,f),df(e,h));

    vec3 res = E;	


    vec3 n1, n2, n3, n4, s, aa, bb, cc, dd;


    n1 = B1; n2 = B; s = E; n3 = H; n4 = H5;
    aa = n2-n1; bb = s-n2; cc = n3-s; dd = n4-n3;

    vec3 t = (7 * (bb + cc) - 3 * (aa + dd)) / 16;

    vec3 m;
	m.x= (s.x < 0.5) ? 2*s.x : 2*(1.0-s.x);
	m.y= (s.y < 0.5) ? 2*s.y : 2*(1.0-s.y);
	m.z= (s.z < 0.5) ? 2*s.z : 2*(1.0-s.z);

        m = min(m, sharpness*abs(bb));
        m = min(m, sharpness*abs(cc));

    t = clamp(t, -m, m);

   
    vec3 s1 = (2*fp.y-1)*t + s;

    n1 = D0; n2 = D; s = s1; n3 = F; n4 = F4;
    aa = n2-n1; bb = s-n2; cc = n3-s; dd = n4-n3;

    t = (7 * (bb + cc) - 3 * (aa + dd)) / 16;

    m.x = (s.x < 0.5) ? 2*s.x : 2*(1.0-s.x);
    m.y = (s.y < 0.5) ? 2*s.y : 2*(1.0-s.y);
    m.z = (s.z < 0.5) ? 2*s.z : 2*(1.0-s.z);
	
        m = min(m, sharpness*abs(bb));
        m = min(m, sharpness*abs(cc));

    t = clamp(t, -m, m);

    vec3 s0 = (2*fp.x-1)*t + s;	
	
	nc = bvec4(nc30 + nc60 + nc45);
	
	blend1 = blend2 = 0.0;

	vec4 final45 = dot(nc45, fx45);
	vec4 final30 = dot(nc30, fx30);
	vec4 final60 = dot(nc60, fx60);

	vec4 maximo = max(max(final30, final60), final45);

	     if (nc.x) {pix1 = px.x ? F : H; blend1 = maximo.x;}
	else if (nc.y) {pix1 = px.y ? B : F; blend1 = maximo.y;}
	else if (nc.z) {pix1 = px.z ? D : B; blend1 = maximo.z;}
	else if (nc.w) {pix1 = px.w ? H : D; blend1 = maximo.w;}

	     if (nc.w) {pix2 = px.w ? H : D; blend2 = maximo.w;}
	else if (nc.z) {pix2 = px.z ? D : B; blend2 = maximo.z;}
	else if (nc.y) {pix2 = px.y ? B : F; blend2 = maximo.y;}
	else if (nc.x) {pix2 = px.x ? F : H; blend2 = maximo.x;}

	res1 = mix(s0, pix1, blend1);
	res2 = mix(s0, pix2, blend2);

	res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));	


    return res;    
} 


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

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 <= threshold)
	{
		// DDT code

		vec2 DDTTex = gl_TexCoord[0].xy + vec2(0.00000001);
		vec2 pos = fract(DDTTex*size)-vec2(0.5, 0.5); // pos = pixel position
	
		vec2 dir = sign(pos); // dir = pixel direction

		vec2 g1 = dir*gl_TexCoord[1].xy;

		vec2 g2 = dir*gl_TexCoord[1].zw; 

		vec3 A = xBR(OGL2Texture, DDTTex          ); 
		vec3 B = xBR(OGL2Texture, DDTTex + g1     ); 
		vec3 C = xBR(OGL2Texture, DDTTex      + g2); 
		vec3 D = xBR(OGL2Texture, DDTTex + g1 + g2); 

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


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



		float k = distance(pos,g1);

		float l = distance(pos,g2); 

		float wd1 = abs(a-d);

		float wd2 = abs(b-c);



		if ( wd1 < wd2 )

		{	if (k < l)

			{

				C = A + D - B;
		}

			else
		
			{

				B = A + D - C;
		}

		}

		else if (wd1 > wd2)

		{

				D = B + C - A;

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

        else color = FXAA(OGL2Texture, gl_TexCoord[0].xy);

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

	gl_FragColor.xyz=pow(color, vec3(1.0/gamma));
}