--[[ single_pass_filters.lua

	Sources for filter shaders that can be drawn entirely in a single pass.
]]

single_pass_filters = {

    ["passthrough"] = [[
    void main()
    {
        fragColor = texture(tex, uv);
    }
    ]],

    ["invert"] = [[
    void main()
    {
        fragColor = vec4(1.) - texture(tex, uv); // Invert color
    }
    ]],

    ["black&white"] = [[
    void main()
    {
        fragColor = vec4(.3*length(texture(tex, uv))); // Black and white
    }
    ]],

    -- Standard image convolution by kernel
    ["convolve"] = [[
    uniform int ResolutionX;
    uniform int ResolutionY;

    #define KERNEL_SIZE 9
    float kernel[KERNEL_SIZE] = float[](
    #if 0
        1./16., 2./16., 1./16.,
        2./16., 4./16., 2./16.,
        1./16., 2./16., 1./16.
    #elif 1
        1./9., 1./9., 1./9.,
        1./9., 1./9., 1./9.,
        1./9., 1./9., 1./9.
    #elif 0
        0., 1., 0.,
        1., -4., 1.,
        0., 1., 0.
    #else
        1., 2., 1.,
        0., 0., 0.,
        -1., -2., -1.
    #endif
    );

    void main()
    {
        float step_x = 1./float(ResolutionX);
        float step_y = 1./float(ResolutionY);

        vec2 offset[KERNEL_SIZE] = vec2[](
            vec2(-step_x, -step_y), vec2(0.0, -step_y), vec2(step_x, -step_y),
            vec2(-step_x,     0.0), vec2(0.0,     0.0), vec2(step_x,     0.0),
            vec2(-step_x,  step_y), vec2(0.0,  step_y), vec2(step_x,  step_y)
        );

        vec4 sum = vec4(0.);
        int i;
        for( i=0; i<KERNEL_SIZE; i++ )
        {
            vec4 tc = texture(tex, uv + offset[i]);
            sum += tc * kernel[i];
        }
        //if (sum.x + sum.y + sum.z > .1)
        //    sum = vec4(vec3(1.)-sum.xyz,1.);
        fragColor = sum;
        fragColor.w = 1.;
    }
    ]],

    -- http://haxepunk.com/documentation/tutorials/post-process/
    ["scanline"] = [[
    uniform int ResolutionX;
    uniform int ResolutionY;
    //uniform
    float scale = 3.0;

    void main()
    {
        if (mod(floor(uv.y * float(ResolutionY) / scale), 2.0) == 0.0)
            fragColor = vec4(0.0, 0.0, 0.0, 1.0);
        else
            fragColor = texture(tex, uv);
    }
    ]],

    ["wiggle"] = [[
    uniform float time;

    void main()
    {
        vec2 tc = uv + .1*vec2(sin(time), cos(.7*time));
        fragColor = texture(tex, tc);
    }
    ]],

    -- TODO: Include a uniform default value array per-pass
    -- Wouold be nice if default uniform values were supported everywhere...
    --[[
    uniforms = {
        wobbleSpeed = 5,
        amplitude = .05,
    }
    ]]
    ["wobble"] = [[
    uniform float time;
    uniform float wobbleSpeed;

    void main()
    {
        vec2 fromCenter = uv - vec2(.5);
        float len = length(fromCenter);
        float f = 1.05 + .05 * sin(wobbleSpeed*time);
        len = pow(len, f);

        vec2 adjFromCenter = len * normalize(fromCenter);
        vec2 uv01 = vec2(.5) + adjFromCenter;
        fragColor = texture(tex, uv01);
    }
    ]],

    ["hueshift"] = [[
    uniform float time;

    // http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
    vec3 rgb2hsv(vec3 c)
    {
        vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
        vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
        vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);

        float d = q.x - min(q.w, q.y);
        float e = 1.0e-10;
        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
    }

    vec3 hsv2rgb(vec3 c)
    {
        vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
        vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
        return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
    }

    void main()
    {
        vec3 col = texture(tex, uv).xyz;
        vec3 hsv = rgb2hsv(col);
        hsv.x += .5 * time;
        fragColor = vec4(hsv2rgb(hsv), 1.);
    }
    ]],

    -- Give a graphical indication  of what scanout looks like.
    ["beamrace"] = [[
    #line 150
    uniform int ResolutionX;
    uniform int ResolutionY;
    uniform float time;

    float fps = 1.;
    float persistence = .20; // duty cycle

    float sawTooth(float a, float b, float x)
    {
        return clamp(((fract(a*(x-time))-(1.-b))/b),0.,1.);

        float attack = step(.2,fract(x-a));//1.-step(a, x);
        float decay = b*((x-a))+1.;
        return clamp(attack*decay, 0., 1.);
    }

    float getBrightnessAtPixel(vec2 uv, float spf, float pers)
    {
        uv = uv.yx;
        float t = mod(time, spf) / spf; //[0,1]

        float secondsPerLine = spf / float(ResolutionY);
        float u = mod(time, secondsPerLine) / secondsPerLine;

        ///@todo X
        float colScaleX = 1.0;//clamp(  1.0 - .1*float(ResolutionX)*abs(uv.x-u)  ,0.,1.);
        float colScaleY = clamp(  1.0 - .1*float(ResolutionY)*abs(uv.y-t)  ,0.,1.);

        colScaleY = sawTooth(fps, pers, uv.y);

        return colScaleX * colScaleY;
    }

    void main()
    {
        fragColor = getBrightnessAtPixel(uv, 1./fps, persistence) *
            texture(tex, uv);
    }
    ]],


    -- From the early Oculus VR SDK
    ["lenswarp"] = [[
    vec2 LensCenter;
    vec2 ScreenCenter;
    vec2 Scale;
    vec2 ScaleIn;
    vec4 HmdWarpParam;

    vec2 HmdWarp(vec2 in01)
    {
        vec2  theta = (in01 - LensCenter) * ScaleIn; // Scales to [-1, 1]
        float rSq = theta.x * theta.x + theta.y * theta.y;
        vec2  theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq +
                                HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);
        return LensCenter + Scale * theta1;
    }
     
    void main()
    {
        float lensOff = 0.287994 - 0.25;

        // Left eye
        LensCenter = vec2(0.25 + lensOff, 0.5);
        ScreenCenter = vec2(.25, .5);
        if (uv.x > .5)
        {
            LensCenter = vec2(0.75 + lensOff, 0.5);
            ScreenCenter = vec2(.75, .5);
        }

        Scale = vec2(0.145806,  0.233290);
        ScaleIn = vec2(4.0, 2.5);
        HmdWarpParam = vec4(1.0, 0.5, 0.25, 0.0);

        vec2 tc = HmdWarp(uv);
        if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))
            fragColor = vec4(0);
        else
            fragColor = texture(tex, tc);
    }
    ]],

    ["sidebyside_double"] = [[
    void main()
    {
        vec2 tc = uv;
        tc.x = fract(2.*tc.x);
        fragColor = texture(tex, tc);
    }
    ]],

    -- http://www.geeks3d.com/20091027/shader-library-posterization-post-processing-effect-glsl/
    ["posterize"] = [[
    void main()
    {
        float gamma = 0.6;
        float numColors = 8.0;

        vec3 c = texture(tex, uv).rgb;
        c = pow(c, vec3(gamma, gamma, gamma));
        c = c * numColors;
        c = floor(c);
        c = c / numColors;
        c = pow(c, vec3(1.0/gamma));
        fragColor = vec4(c, 1.0);
    }
    ]],

    ["threshold_luminance"] = [[
    uniform float time;
    uniform float lumThresh;
    void main()
    {
        vec3 col = texture(tex, uv).xyz;
        vec3 coeff = vec3(0.2126, 0.7152, 0.0722);
        fragColor = vec4(vec3(1.-step(length(coeff*col), lumThresh)), 1.);
    }
    ]],

    -- https://www.shadertoy.com/view/MtlSWj
    -- TODO: Include a uniform default value array per-pass
    -- Wouold be nice if default uniform values were supported everywhere...
    --[[
    uniforms = {
        sigma = 5,
    }
    ]]
    ["blurX"] = [[
    uniform int ResolutionX;
    uniform float sigma;// = 5.0;
    uniform float samplesx; // = 25

    float Gaussian (float sigma, float x)
    {
        return exp(-(x*x) / (2.0 * sigma*sigma));
    }
    void main()
    {
        int samples = int(samplesx);
        int halfSamples = samples/2;
        float step_x = 1./float(ResolutionX);

        vec3 sum = vec3(0.);
        float total = 0.;
        for (int i=0; i<samples; ++i)
        {
            float fx = Gaussian(sigma, float(i) - float(halfSamples));
            vec3 col = texture(tex, uv).xyz;
            float offset = float(i-halfSamples) * step_x;
            sum += texture(tex, uv + vec2(offset, 0.)).rgb * fx;
            total += fx;
        }

        fragColor = vec4(sum/total, 1.);
    }
    ]],

    ["blurY"] = [[
    uniform int ResolutionY;
    uniform float sigma;// = 5.0;
    uniform float samplesy; // = 25

    float Gaussian (float sigma, float x)
    {
        return exp(-(x*x) / (2.0 * sigma*sigma));
    }
    void main()
    {
        int samples = int(samplesy);
        int halfSamples = samples/2;
        float step_y = 1./float(ResolutionY);

        vec3 sum = vec3(0.);
        float total = 0.;
        for (int i=0; i<samples; ++i)
        {
            float fy = Gaussian(sigma, float(i) - float(halfSamples));
            vec3 col = texture(tex, uv).xyz;
            float offset = float(i-halfSamples) * step_y;
            sum += texture(tex, uv + vec2(0., offset)).rgb * fy;
            total += fy;
        }

        fragColor = vec4(sum/total, 1.);
    }
    ]],

    ["juliaBorder"] = [[
    uniform float time;
    uniform float border;
    uniform float juliaParam;

    vec4 juliaBorder(vec2 uv, float border, vec2 an)
    {
        vec2 uv11 = 2.*uv - vec2(1.);

        // Invert quadrants - center to borders
        if (uv11.x<0. && uv11.y<0.)
        {
            uv11.y = -1.-uv11.y;
        }
        else if (uv11.x>0. && uv11.y<0.)
        {
            uv11.x *= -1.;
            uv11.y = -1.-uv11.y;
        }
        else if (uv11.x>0. && uv11.y>0.)
        {
            uv11.x *= -1.;
            uv11.y = -1.+uv11.y;
        }
        else
        {
            uv11.y = -1.+uv11.y;
        }

        // offset out to frame
        uv11.x += 1.6*border;
        uv11.y -= border;

        float r = 1.65;
        mat3 rotation = mat3(
            vec3( cos(r),  sin(r),  0.),
            vec3(-sin(r),  cos(r),  0.),
            vec3(     0.,      0.,  1.)
        );

        // https://www.shadertoy.com/view/4d23WG
        vec2 z = 1.15*(rotation*vec3(uv11,1.)).xy;

        float f = 1e20;
        for( int i=0; i<128; i++ ) 
        {
            z = vec2( z.x*z.x-z.y*z.y, 2.0*z.x*z.y ) + an;
            f = min( f, dot(z,z) );
        }
        
        f = 1.0+log(f)/16.0;

        return vec4(1.)-vec4(f,f*f,f*f*f,f*f);
    }

    vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d )
    {
        return a + b*cos( 6.28318*(c*t+d) );
    }

    vec3 getColor(float i)
    {
        float colorPhase = .6;
        return pal( i+colorPhase, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,0.5),vec3(0.8,0.90,0.30) );
    }

    void main()
    {
        //float border = .25 + .014*sin(time);
        float t = juliaParam;
        vec2 an = vec2(.23,.47) + .03*vec2(sin(t), cos(t));

        vec4 juliaColor = juliaBorder(uv, border, an);
        juliaColor.rgb = vec3(length(juliaColor));//getColor(juliaColor.z);
        fragColor = mix(juliaColor, texture(tex, uv), 1.-juliaColor.a);
    }
    ]],

    ["chromaticAberration"] = [[
    uniform float magnitude;

    void main()
    {
        vec2 toCenter = vec2(.5) - uv;
        fragColor.r = texture(tex, uv + 3.*magnitude*toCenter).r;
        fragColor.g = texture(tex, uv + 2.*magnitude*toCenter).g;
        fragColor.b = texture(tex, uv + 1.*magnitude*toCenter).b;
        fragColor.a = texture(tex, uv).a;
    }
    ]],
}

return single_pass_filters
