// Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com)
// Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com)
// Copyright (C) 2013 Belen Masia (bmasia@unizar.es)
// Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com)
// Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to
// do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. As clarification, there
// is no requirement that the copyright notice and permission be included in
// binary distributions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//                  _______  ___  ___       ___           ___
//                 /       ||   \/   |     /   \         /   \
//                |   (---- |  \  /  |    /  ^  \       /  ^  \
//                 \   \    |  |\/|  |   /  /_\  \     /  /_\  \
//              ----)   |   |  |  |  |  /  _____  \   /  _____  \
//             |_______/    |__|  |__| /__/     \__\ /__/     \__\
// 
//                               E N H A N C E D
//       S U B P I X E L   M O R P H O L O G I C A L   A N T I A L I A S I N G
//
//                         http://www.iryoku.com/smaa/

smaadefs = [
    #define SMAA_PRESET @smaapreset

    #if SMAA_PRESET == 1
    #define SMAA_THRESHOLD 0.1
    #define SMAA_MAX_SEARCH_STEPS 8
    #define SMAA_MAX_SEARCH_STEPS_DIAG 0
    #define SMAA_CORNER_ROUNDING 100
    #elif SMAA_PRESET == 2
    #define SMAA_THRESHOLD 0.1
    #define SMAA_MAX_SEARCH_STEPS 16
    #define SMAA_MAX_SEARCH_STEPS_DIAG 8
    #define SMAA_CORNER_ROUNDING 25
    #elif SMAA_PRESET == 3
    #define SMAA_THRESHOLD 0.05
    #define SMAA_MAX_SEARCH_STEPS 32
    #define SMAA_MAX_SEARCH_STEPS_DIAG 16
    #define SMAA_CORNER_ROUNDING 25
    #else
    #define SMAA_THRESHOLD 0.15
    #define SMAA_MAX_SEARCH_STEPS 4
    #define SMAA_MAX_SEARCH_STEPS_DIAG 0
    #define SMAA_CORNER_ROUNDING 100
    #endif

    #define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0
    #define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0)

    #define SMAA_AREATEX_MAX_DISTANCE 16
    #define SMAA_AREATEX_MAX_DISTANCE_DIAG 20
    #define SMAA_AREATEX_WIDTH 160
    #define SMAA_AREATEX_HEIGHT 560
    #define SMAA_SEARCHTEX_WIDTH 66
    #define SMAA_SEARCHTEX_HEIGHT 33

    #define SMAA_AREATEX_SUBSAMPLES 80

    @(? (smaaopt "g") [
        #define SMAA_LUMA(color) (color.g)
    ] [
        #define SMAA_LUMA(color) (color.a)
    ])
    
    @(? (smaaopt "a") [
        #define SMAA_AREA(vals) (vals.ra)
    ] [
        #define SMAA_AREA(vals) (vals.rg)
    ])

    @(? (|| (smaaopt "t") [smaaopt "s"]) [
        #define SMAA_AREA_OFFSET(texcoord, offset) texcoord.y += offset*float(SMAA_AREATEX_SUBSAMPLES)
    ] [
        #define SMAA_AREA_OFFSET(texcoord, offset)
    ])
]

shader 0 [SMAALumaEdgeDetection@smaapreset@smaaopts] [
    in vec4 vvertex;
    @(screentexcoord 0)
    out vec2 texcoord0;
 
    void main(void)
    {
        gl_Position = vvertex;
        texcoord0 = vtexcoord0;
    }
] [
    @smaadefs
    uniform sampler2DRect tex0;
    in vec2 texcoord0;
    layout(location = 0) out vec4 fragcolor;

    void main(void)
    {
        // Calculate lumas:
        float L = SMAA_LUMA(texture2DRect(tex0, texcoord0));
        float Lleft = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(-1, 0)));
        float Ltop  = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(0, -1)));

        // We do the usual threshold:
        vec2 delta = abs(L - vec2(Lleft, Ltop));
        vec2 edges = step(SMAA_THRESHOLD, delta);

        // Then discard if there is no edge:
        @(? (smaaopt "d") [
            if (edges.x + edges.y == 0.0) discard;
            else
        ] [
            if (edges.x + edges.y > 0.0)
        ])
        {
            // Calculate right and bottom deltas:
            float Lright = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(1, 0)));
            float Lbottom  = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(0, 1)));
            // Calculate the maximum delta in the direct neighborhood:
            vec2 maxDelta = max(delta, abs(L - vec2(Lright, Lbottom)));

            // Calculate left-left and top-top deltas:
            float Lleftleft = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(-2, 0)));
            float Ltoptop = SMAA_LUMA(texture2DRectOffset(tex0, texcoord0, ivec2(0, -2)));
            // Calculate the final maximum delta:
            maxDelta = max(maxDelta, abs(vec2(Lleft, Ltop) - vec2(Lleftleft, Ltoptop)));

            /**
             * Each edge with a delta in luma of less than 50% of the maximum luma
             * surrounding this pixel is discarded. This allows to eliminate spurious
             * crossing edges, and is based on the fact that, if there is too much
             * contrast in a direction, that will hide contrast in the other
             * neighbors.
             * This is done after the discard intentionally as this situation doesn't
             * happen too frequently (but it's important to do as it prevents some 
             * edges from going undetected).
             */
            edges *= step(max(maxDelta.x, maxDelta.y), SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta);
        }

        fragcolor = vec4(edges, 0.0, 0.0);
    }
]

shader 0 [SMAAColorEdgeDetection@smaapreset@smaaopts] [
    in vec4 vvertex;
    @(screentexcoord 0)
    out vec2 texcoord0;

    void main(void)
    {
        gl_Position = vvertex;
        texcoord0 = vtexcoord0;
    }
] [
    @smaadefs
    uniform sampler2DRect tex0;
    in vec2 texcoord0;
    layout(location = 0) out vec4 fragcolor;

    void main(void)
    {
        // Calculate color deltas:
        vec3 C = texture2DRect(tex0, texcoord0).rgb;
        vec3 Cleft = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(-1, 0)).rgb);
        vec3 Ctop = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(0, -1)).rgb);
        vec2 delta;
        delta.x = max(max(Cleft.r, Cleft.g), Cleft.b);
        delta.y = max(max(Ctop.r, Ctop.g), Ctop.b);

        // We do the usual threshold:
        vec2 edges = step(SMAA_THRESHOLD, delta);

        // Then discard if there is no edge:
        @(? (smaaopt "d") [
            if (edges.x + edges.y == 0.0) discard;
            else
        ] [
            if (edges.x + edges.y > 0.0)
        ])
        {
            // Calculate right and bottom deltas:
            vec3 Cright = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(1, 0)).rgb);
            vec3 Cbottom = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(0, 1)).rgb);
            // Calculate left-left and top-top deltas:
            vec3 Cleftleft = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(-2, 0)).rgb);
            vec3 Ctoptop = abs(C - texture2DRectOffset(tex0, texcoord0, ivec2(0, -2)).rgb);
            // Calculate the maximum delta in the direct neighborhood:
            vec3 t = max(max(Cright, Cbottom), max(Cleftleft, Ctoptop));
            // Calculate the final maximum delta:
            float maxDelta = max(max(delta.x, delta.y), max(max(t.r, t.g), t.b));

            // Local contrast adaptation in action:
            edges *= step(maxDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta);
        }

        fragcolor = vec4(edges, 0.0, 0.0);
    }
]

shader 0 [SMAABlendingWeightCalculation@smaapreset@smaaopts] [
    @smaadefs
    in vec4 vvertex;
    @(screentexcoord 0)
    out vec2 texcoord0, texcoord1, texcoord2, texcoord3, texcoord4, texcoord5;

    void main(void)
    {
        gl_Position = vvertex;
        texcoord0 = vtexcoord0;

        // We will use these offsets for the searches later on (see PSEUDO_GATHER4):
        texcoord1 = vtexcoord0 + vec2( -0.25, -0.125);
        texcoord2 = vtexcoord0 + vec2(  1.25, -0.125);
        texcoord3 = vtexcoord0 + vec2(-0.125, -0.25);
        texcoord4 = vtexcoord0 + vec2(-0.125,  1.25);
        texcoord5 = vtexcoord0 + vec2(  0.25,  0.0);
    }
] [
    @smaadefs
    in vec2 texcoord0, texcoord1, texcoord2, texcoord3, texcoord4, texcoord5;
    uniform sampler2DRect tex0, tex1, tex2;
    uniform vec4 subsamples;
    layout(location = 0) out vec4 fragcolor;

    #if __VERSION__ >= 130 || defined(GL_EXT_gpu_shader4)
        #define SMAARound(e) round(e)
    #else
        #define SMAARound(e) floor(e + 0.5)
    #endif

    //-----------------------------------------------------------------------------
    // Diagonal Search Functions

    #if SMAA_MAX_SEARCH_STEPS_DIAG > 0

    /**
    * Allows to decode two binary values from a bilinear-filtered access.
    */
    #define SMAADecodeDiagBilinearAccess(e) (e * abs(6.0 * e - 5.0))

    /**
     * These functions allows to perform diagonal pattern searches.
     */
    float SMAASearchDiagRightUp(void) {
        vec2 e = texture2DRectOffset(tex0, texcoord0, ivec2(1, -1)).rg;
        vec2 texcoord = texcoord0 + vec2(1.0, -1.0);
        for (int i = 1; i < SMAA_MAX_SEARCH_STEPS_DIAG; i++) {
            if (e.x + e.y < 1.5) break;
            texcoord += vec2(1.0, -1.0);
            e = texture2DRect(tex0, texcoord).rg;
        }
        return (texcoord.x - texcoord0.x) - 1.0;
    }

    float SMAASearchDiagLeftDown(void) {
        vec2 e = texture2DRectOffset(tex0, texcoord0, ivec2(-1, 1)).rg;
        vec2 texcoord = texcoord0 + vec2(-1.0, 1.0);
        for (int i = 1; i < SMAA_MAX_SEARCH_STEPS_DIAG; i++) {
            if (e.x + e.y < 1.5) break;
            texcoord += vec2(-1.0, 1.0);
            e = texture2DRect(tex0, texcoord).rg;
        }
        return (texcoord0.x - texcoord.x) - 1.0 + SMAARound(e.y);
    }

    float SMAASearchDiagLeftUp(void) {
        vec2 e = texture2DRectOffset(tex0, texcoord5, ivec2(-1, -1)).rg;
        vec2 texcoord = texcoord5 + vec2(-1.0, -1.0);
        for (int i = 1; i < SMAA_MAX_SEARCH_STEPS_DIAG; i++) {
            if (SMAADecodeDiagBilinearAccess(e.x) + e.y < 1.5) break;
            texcoord += vec2(-1.0, -1.0);
            e = texture2DRect(tex0, texcoord).rg;
        }
        return (texcoord5.x - texcoord.x) - 1.0;
    }

    float SMAASearchDiagRightDown(void) {
        vec2 e = texture2DRectOffset(tex0, texcoord5, ivec2(1, 1)).rg;
        vec2 texcoord = texcoord5 + vec2(1.0, 1.0);
        for (int i = 1; i < SMAA_MAX_SEARCH_STEPS_DIAG; i++) {
            if (SMAADecodeDiagBilinearAccess(e.x) + e.y < 1.5) break;
            texcoord += vec2(1.0, 1.0);
            e = texture2DRect(tex0, texcoord).rg;
        }
        return (texcoord.x - texcoord5.x) - 1.0 + SMAARound(e.y);
    }

    /** 
     * Similar to SMAAArea, this calculates the area corresponding to a certain
     * diagonal distance and crossing edges 'e'.
     */
    vec2 SMAAAreaDiag(vec2 dist, vec2 e, float offset) {
        vec2 texcoord = float(SMAA_AREATEX_MAX_DISTANCE_DIAG) * e + dist;

        // We do a scale and bias for mapping to texel space:
        texcoord = texcoord + 0.5;

        // Diagonal areas are on the second half of the texture:
        texcoord.x += 0.5*float(SMAA_AREATEX_WIDTH);

        // Move to proper place, according to the subpixel offset:
        SMAA_AREA_OFFSET(texcoord, offset);
        
        return SMAA_AREA(texture2DRect(tex1, texcoord));
    }

    /**
     * This searches for diagonal patterns and returns the corresponding weights.
     */
    vec2 SMAACalculateDiagWeights(vec2 e) {
        vec2 weights = vec2(0.0);

        vec2 d;
        d.x = e.r > 0.5 ? SMAASearchDiagLeftDown() : 0.0;
        d.y = SMAASearchDiagRightUp();

        if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3
            vec4 coords = vec4(0.25 - d.x, d.x, d.y, -0.25 - d.y) + texcoord0.xyxy;
            vec4 c;
            c.xy = texture2DRectOffset(tex0, coords.xy, ivec2(-1, 0)).rg;
            c.zw = texture2DRectOffset(tex0, coords.zw, ivec2( 1, 0)).rg;
            c.xz = SMAADecodeDiagBilinearAccess(c.xz);
            c = SMAARound(c);

            vec2 e = 2.0 * c.yw + c.xz;
            e *= step(d, vec2(float(SMAA_MAX_SEARCH_STEPS_DIAG) - 0.5));

            weights += SMAAAreaDiag(d, e, subsamples.z);
        }

        d.x = SMAASearchDiagLeftUp();
        d.y = SMAADecodeDiagBilinearAccess(e.r) > 0.5 ? SMAASearchDiagRightDown() : 0.0;

        if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3
            vec4 coords = vec4(-d.xx, d.yy) + texcoord0.xyxy;
            vec4 c;
            c.x  = texture2DRectOffset(tex0, coords.xy, ivec2(-1,  0)).g;
            c.y  = texture2DRectOffset(tex0, coords.xy, ivec2( 0, -1)).r;
            c.zw = texture2DRectOffset(tex0, coords.zw, ivec2( 1,  0)).gr;

            vec2 e = 2.0 * c.xz + c.yw;
            e *= step(d, vec2(float(SMAA_MAX_SEARCH_STEPS_DIAG) - 0.5));

            weights += SMAAAreaDiag(d, e, subsamples.w).gr;
        }

        return weights;
    }
    #endif

    //-----------------------------------------------------------------------------
    // Horizontal/Vertical Search Functions

    /**
     * This allows to determine how much length should we add in the last step
     * of the searches. It takes the bilinearly interpolated edge (see 
     * PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and
     * crossing edges are active.
     */
    float SMAASearchLength(vec2 e, float bias, float scale) {
        e.r = bias + e.r * scale;
        return 255.0 * texture2DRect(tex2, e*vec2(float(SMAA_SEARCHTEX_WIDTH), float(SMAA_SEARCHTEX_HEIGHT))).r;
    }

    /**
    * Horizontal/vertical search functions for the 2nd pass.
    */
    float SMAASearchXLeft(void) {
        /**
         * PSEUDO_GATHER4
         * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to
         * sample between edge, thus fetching four edges in a row.
         * Sampling with different offsets in each direction allows to disambiguate
         * which edges are active from the four fetched ones.
         */
        vec2 e = texture2DRect(tex0, texcoord1).rg;
        vec2 texcoord = texcoord1;
        for(int i = 1; i < SMAA_MAX_SEARCH_STEPS; i++) {
            if(e.g <= 0.8281 || e.r > 0.0) break; // Is there some edge not activated or a crossing edge that breaks the line?
            texcoord.x -= 2.0;
            e = texture2DRect(tex0, texcoord).rg;
        }
        // We correct the previous (-0.25, -0.125) offset we applied:
        // The searches are bias by 1, so adjust the coords accordingly:
        // Disambiguate the length added by the last step:
        return texcoord.x + (0.25 + 1.0) - SMAASearchLength(e, 0.0, 0.5);
    }

    float SMAASearchXRight(void) {
        vec2 e = texture2DRect(tex0, texcoord2).rg;
        vec2 texcoord = texcoord2;
        for(int i = 1; i < SMAA_MAX_SEARCH_STEPS; i++) {
            if(e.g <= 0.8281 || e.r > 0.0) break; // Is there some edge not activated or a crossing edge that breaks the line?
            texcoord.x += 2.0;
            e = texture2DRect(tex0, texcoord).rg;
        }
        return texcoord.x - (0.25 + 1.0) + SMAASearchLength(e, 0.5, 0.5);
    }

    float SMAASearchYUp(void) {
        vec2 e = texture2DRect(tex0, texcoord3).rg;
        vec2 texcoord = texcoord3;
        for(int i = 1; i < SMAA_MAX_SEARCH_STEPS; i++) {
            if(e.r <= 0.8281 || e.g > 0.0) break; // Is there some edge not activated or a crossing edge that breaks the line?
            texcoord.y -= 2.0;
            e = texture2DRect(tex0, texcoord).rg;
        }
        return texcoord.y + (0.25 + 1.0) - SMAASearchLength(e.gr, 0.0, 0.5);
    }

    float SMAASearchYDown(void) {
        vec2 e = texture2DRect(tex0, texcoord4).rg;
        vec2 texcoord = texcoord4;
        for(int i = 1; i < SMAA_MAX_SEARCH_STEPS; i++) {
            if(e.r <= 0.8281 || e.g > 0.0) break; // Is there some edge not activated or a crossing edge that breaks the line?
            texcoord.y += 2.0;
            e = texture2DRect(tex0, texcoord).rg;
        }
        return texcoord.y - (0.25 + 1.0) + SMAASearchLength(e.gr, 0.5, 0.5);
    }

    /** 
     * Ok, we have the distance and both crossing edges. So, what are the areas
     * at each side of current edge?
     */
    vec2 SMAAArea(vec2 dist, vec2 e, float offset) {
        // SMAAArea below needs a sqrt, as the areas texture is compressed 
        // quadratically:
        // Rounding prevents precision errors of bilinear filtering:
        vec2 texcoord = float(SMAA_AREATEX_MAX_DISTANCE) * SMAARound(4.0 * e) + sqrt(dist);

        // We do a scale and bias for mapping to texel space:
        texcoord = texcoord + 0.5;

        // Move to proper place, according to the subpixel offset:
        SMAA_AREA_OFFSET(texcoord, offset);

        return SMAA_AREA(texture2DRect(tex1, texcoord));
    }
   
    //-----------------------------------------------------------------------------
    // Corner Detection Functions

    #if SMAA_CORNER_ROUNDING < 100

    vec2 SMAADetectHorizontalCornerPattern(vec3 coords, vec2 d) {
        vec2 e;
        if(d.x >= d.y) coords.x = coords.z + 1.0 - 0.5*step(d.x, d.y);
        e.r = texture2DRectOffset(tex0, coords.xy, ivec2(0,  1)).r;
        e.g = texture2DRectOffset(tex0, coords.xy, ivec2(0, -2)).r;
        return clamp(1.0 - (1.0 - SMAA_CORNER_ROUNDING_NORM) * e, 0.0, 1.0);
    }

    vec2 SMAADetectVerticalCornerPattern(vec3 coords, vec2 d) {
        vec2 e;
        if(d.x >= d.y) coords.y = coords.z + 1.0 - 0.5*step(d.x, d.y);
        e.r = texture2DRectOffset(tex0, coords.xy, ivec2( 1, 0)).g;
        e.g = texture2DRectOffset(tex0, coords.xy, ivec2(-2, 0)).g;
        return clamp(1.0 - (1.0 - SMAA_CORNER_ROUNDING_NORM) * e, 0.0, 1.0);
    }

    #endif

    //-----------------------------------------------------------------------------
    // Blending Weight Calculation Pixel Shader (Second Pass)

    void main(void) 
    {
        vec4 weights = vec4(0.0);

        vec2 e = texture2DRect(tex0, texcoord5).rg;

        if (e.g > 0.5) { // Edge at north
            #if SMAA_MAX_SEARCH_STEPS_DIAG > 0
            // Diagonals have both north and west edges, so searching for them in
            // one of the boundaries is enough.
            weights.rg = SMAACalculateDiagWeights(e);

            // We give priority to diagonals, so if we find a diagonal we skip 
            // horizontal/vertical processing.
            if (weights.r + weights.g == 0.0) {
            #endif

            // Find the distance to the left:
            vec3 coords;
            coords.x = SMAASearchXLeft();
            coords.y = texcoord3.y; // texcoord3.y = texcoord0.y - 0.25 (CROSSING_OFFSET)
            // Find the distance to the right:
            coords.z = SMAASearchXRight();

            // We want the distances to be in pixel units (doing this here allow to
            // better interleave arithmetic and memory accesses):
            vec2 d = SMAARound(abs(coords.xz - texcoord0.x));

            // Now fetch the left crossing edges, two at a time using bilinear
            // filtering. Sampling at -0.25 (see CROSSING_OFFSET) enables to
            // discern what value each edge has:
            vec2 e;
            e.x = texture2DRect(tex0, coords.xy).r;
            // Fetch the right crossing edges:
            e.y = texture2DRectOffset(tex0, coords.zy, ivec2(1, 0)).r;

            // Ok, we know how this pattern looks like, now it is time for getting
            // the actual area:
            weights.rg = SMAAArea(d, e, subsamples.y);

            #if SMAA_CORNER_ROUNDING < 100
            // Fix corners:
            coords.y = texcoord0.y;
            weights.rg *= SMAADetectHorizontalCornerPattern(coords, d);
            #endif

            #if SMAA_MAX_SEARCH_STEPS_DIAG > 0
            } else
                e.r = 0.0; // Skip vertical processing.
            #endif
        }

        if (e.r > 0.5) { // Edge at west
            // Find the distance to the top:
            vec3 coords;
            coords.y = SMAASearchYUp();
            coords.x = texcoord1.x; // texcoord1.x = texcoord0.x - 0.25;
            // Find the distance to the bottom:
            coords.z = SMAASearchYDown();

            // We want the distances to be in pixel units:
            vec2 d = SMAARound(abs(coords.yz - texcoord0.y));

            // Fetch the top crossing edges:
            vec2 e;
            e.x = texture2DRect(tex0, coords.xy).g;
            // Fetch the bottom crossing edges:
            e.y = texture2DRectOffset(tex0, coords.xz, ivec2(0, 1)).g;

            // Get the area for this direction:
            weights.ba = SMAAArea(d, e, subsamples.x);

            #if SMAA_CORNER_ROUNDING < 100
            // Fix corners:
            coords.x = texcoord0.x;
            weights.ba *= SMAADetectVerticalCornerPattern(coords, d);
            #endif
        }

        fragcolor = weights;
    }
]

shader 0 [SMAANeighborhoodBlending@smaapreset@smaaopts] [
    in vec4 vvertex;
    @(screentexcoord 0)
    out vec2 texcoord0;

    void main(void)
    {
        gl_Position = vvertex;
        texcoord0 = vtexcoord0;
    }
] [
    @smaadefs
    in vec2 texcoord0;
    uniform sampler2DRect tex0, tex1;
    @(? (smaaopt "s") [uniform sampler2DRect tex2, tex3;])
    layout(location = 0) out vec4 fragcolor;

    // Neighborhood Blending Pixel Shader (Third Pass)

    void main(void)
    {
        // Fetch the blending weights for current pixel:
        vec4 a;
        a.xz = texture2DRect(tex1, texcoord0).rb;
        a.y = texture2DRectOffset(tex1, texcoord0, ivec2(0, 1)).g;
        a.w = texture2DRectOffset(tex1, texcoord0, ivec2(1, 0)).a;

        // Up to 4 lines can be crossing a pixel (one through each edge). We
        // favor blending by choosing the line with the maximum weight for each
        // direction:
        vec2 offset;
        offset.x = a.w > a.z ? a.w : -a.z; // left vs. right 
        offset.y = a.y > a.x ? a.y : -a.x; // top vs. bottom

        // Then we go in the direction that has the maximum weight:
        if (abs(offset.x) > abs(offset.y)) // horizontal vs. vertical
            offset.y = 0.0;
        else
            offset.x = 0.0;

        // We exploit bilinear filtering to mix current pixel with the chosen
        // neighbor:
        fragcolor = texture2DRect(tex0, texcoord0 + offset);

        @(? (smaaopt "s") [
            a.xz = texture2DRect(tex3, texcoord0).rb;
            a.y = texture2DRectOffset(tex3, texcoord0, ivec2(0, 1)).g;
            a.w = texture2DRectOffset(tex3, texcoord0, ivec2(1, 0)).a;
            offset.x = a.w > a.z ? a.w : -a.z;
            offset.y = a.y > a.x ? a.y : -a.x;
            if (abs(offset.x) > abs(offset.y))
                offset.y = 0.0;
            else
                offset.x = 0.0;
            fragcolor = 0.5*(fragcolor + texture2DRect(tex2, texcoord0 + offset));
        ])
    }
]

