#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require

precision highp float;
//precision mediump float;

in vec2 vTextureCoord;
in vec3 vGrainCoord;

uniform lowp samplerExternalOES uSampler;
uniform mediump sampler3D lookupSampler;
uniform mediump sampler3D grainSampler;

uniform float exposure;
uniform float exposureGain;
//uniform vec3 undoCurve[8];
uniform vec3 renderCurve[8];

uniform vec3 toneCurveR[8];
uniform vec3 toneCurveG[8];
uniform vec3 toneCurveB[8];

uniform vec3 hslValues[9];

uniform float blacks;
uniform float whites;

uniform vec2 shadowHighlights;

uniform highp mat3 abcMatr;
uniform highp mat3 indexMatr;
uniform highp vec3 abcScale;

uniform float saturation;
uniform float vibrance;

uniform float grayMix;

uniform float splttAlpha;
uniform vec3 splttShadow;
uniform vec3 splttDelta;

uniform float lumShadowFactor;
uniform float lumHighlightFactor;
uniform float blending;
uniform vec4 midtoneMapAlpha;
uniform vec4 globalMapAlpha;

uniform float vignetteAmount;
uniform float roundness;
uniform float feather;
uniform float midpoint;
uniform float highlightContrast;
uniform float highlightMask;
uniform float lutAmount;

uniform float grainAmount;
uniform float grainSize;
uniform float grainRoughness;
uniform vec3 grainPos;

uniform vec2 aspect;

uniform highp mat3 preColorMatr;
uniform highp mat3 postColorMatr;


out vec4 fragmentColor;

vec3 sRGBGamma(in vec3 col)
{
    vec3 sV = step(col,vec3(0.0031308));
    return mix(max(1.055 * pow (col, vec3(1.0/2.4)) - 0.055,vec3(0.0)),(col*12.92),sV);
}

vec3 invSRGBGamma(in vec3 col)
{
    vec3 sV = step(col,vec3(0.040449936));
    return mix(max(pow ((col + 0.055)/1.055, vec3(2.4)),vec3(0.0)),(col/12.92),sV);
}

vec3 invHLGGamma(vec3 val)
{
    const float a = 0.17883277;
    const float b = 1.0 - (4.0 * a); //0.28466892;
    const float c = 0.5 - a * log(4.0 * a); //0.55991073
    const float e = 2.71828182845904523536;
    vec3 lowVal = pow(val, vec3(2.0)) / 3.0;
    vec3 highVal = (pow(vec3(e),vec3(((val - c)/a))) + b) / 12.0;
    val.r = val.r <= 0.5 ? lowVal.r : highVal.r;
    val.g = val.g <= 0.5 ? lowVal.g : highVal.g;
    val.b = val.b <= 0.5 ? lowVal.b : highVal.b;
    return val;
}

vec3 invPQGamma(vec3 val)
{
    const float m1 = 0.1593017578125;
    const float m2 = 78.84375;
    const float c2 = 18.8515625;
    const float c3 = 18.6875;
    const float c1 = 0.8359375;
    vec3 exVal = pow(val, vec3(1.0/m2));
    val = pow(max(exVal - c1, 0.0) / (c2 - c3 * exVal), vec3(1.0 / m1));
    return val;
}

vec3 DivMap (vec3 x, float alpha)
{
    vec3 scaled = alpha * x;
    return scaled / (vec3 (1.0, 1.0, 1.0) + scaled - x);
}

vec3 DivMap (vec3 x, vec3 alpha)
{
    vec3 scaled = alpha * x;
    return scaled / (vec3 (1.0, 1.0, 1.0) + scaled - x);
}

vec3 DivMapInverse (vec3 y, float alpha)
{
    vec3 scaled = y / alpha;
    return scaled / (vec3 (1.0, 1.0, 1.0) + scaled - y);
}

vec3 maskfunc (vec3 x, float weight)
{
    vec3 m1 = 2.0 * x - 1.0;

    m1 = 1.0 - m1*m1; //maskfunc0
    vec3 m2 = m1*m1; // maskfunc1

    return mix(m1,m2,weight);
}


vec3 EvaluateMainLumFactors (vec3 color)
{

    vec3 y = color;
    vec3 y3 = y*y*y;
    vec3 iY = y*(1.0-y);

    // Find the original factors value (Blending = 100).

    //vec3 lumShadowFactor   = uniforms.luminance.xxx;
    //vec3 lumHighlightFactor = uniforms.luminance.zzz;

    float lumDeltaFactor = lumHighlightFactor - lumShadowFactor;
    float deltaForShadowOnly = 2.0 - lumShadowFactor;

    vec3 p1 = iY*lumShadowFactor;

    vec3 stY = p1 + iY*lumDeltaFactor*y + y3;       //EvaluateFactors (y, lumShadowFactor, lumDeltaFactor);

    // For reduced crosstalk as blending is reduced, find the shadows and
    // highlights values separately.

    // Shadows first.


    vec3 shadowY = p1 + iY*deltaForShadowOnly*y + y3; //EvaluateFactors (y, lumShadowFactor, deltaForShadowOnly);

    // Highlights.

    //vec3 deltaForHighlightOnly = (lumHighlightFactor - vec3(1.0));
    float deltaForHighlightOnly = lumHighlightFactor - 1.0;

    p1 = iY*lumShadowFactor;
    vec3 highlightY = iY + iY*deltaForHighlightOnly*y + y3; //EvaluateFactors (y, vec3(1.0), deltaForHighlightOnly);

    // Put the shadows-only and highlights-only results together with the
    // original image data to get the Blending = 0 result.

    // Use the midtone range maskfunc for this.

    vec3 compositeWeight = 2.0 * y - 1.0; //maskfunc (y, kZero);
    compositeWeight = 1.0 - compositeWeight*compositeWeight;

    vec3 sh0 = mix (shadowY   , y, compositeWeight);
    vec3 sh1 = mix (highlightY, y, compositeWeight);

    // Important: With cr_select, the order of the 1st two arguments are
    // different than the ? : operator used in the reference C++ code.


    // step(a,b) : b<a ? 0 : 1; a>=b ? 0 : 1; (-a)<=(-b) ? 0 : 1; (-b)>(-a) ? 0 : 1;

    vec3 blendMix = step(-y,vec3(-0.5));
    vec3 shNoBlend = mix(sh0,sh1,blendMix);
    /*
        vec3 shNoBlend = y <= vec3(0.5) ? sh0 : sh1;

        shNoBlend.x = y.x <= 0.5 ? sh0.x : sh1.x;
        shNoBlend.y = y.y <= 0.5 ? sh0.y : sh1.y;
        shNoBlend.z = y.z <= 0.5 ? sh0.z : sh1.z;
    */
    // Lerp the independent shadow/highlight result with the full legacy ST
    // result.

    y = mix (shNoBlend, stY, blending);

    return y;

}


vec3 LiftShadowsAndDimHighlights (vec3 y)
{

    // For both shadows and highlights: get the maximum about of lifting
    // and/or dimmimg, then lerp it with y using the scale to get the
    // actual amount as set by the lum sliders.

    vec3 retval = sRGBGamma (y);

    //vec3 lumShadowFactor   = uniforms.luminance.xxx;
    //vec3 lumHighlightFactor = uniforms.luminance.zzz;

    float fShadowLiftScale = lumShadowFactor - 1.0;

    vec3 calcResult = 0.16 + retval * (0.5 + retval * (-0.000000000000000495264 + (retval * 0.72338)));

    vec3 blendMix = step(-retval,vec3(-0.48));
    vec3 fullLift = mix(calcResult,retval,blendMix);

    //vec3 fullLift = (retval <= vec3(0.48)) ? calcResult : retval;
    fullLift = mix (retval, fullLift, fShadowLiftScale);

    retval = lumShadowFactor > 1.0 ? fullLift : retval;

    float fHighlightDimScale = abs (lumHighlightFactor - 2.0);

    calcResult = -0.38338 + retval * (2.67014 + retval * (-2.17014 + (retval * 0.72338)));

    blendMix = step(vec3(-0.52),-retval);
    vec3 fullDim = mix(calcResult,retval,blendMix);

    //vec3 fullDim = (retval > 0.52) ? calcResult : retval;

    fullDim = mix (retval, fullDim, fHighlightDimScale);

    retval = lumHighlightFactor< 2.0 ? fullDim : retval;

    return invSRGBGamma(retval);

}

vec3 doColorGrading (vec3 color)
{
    vec3 y = color;

    y = DivMap (y, splttAlpha);

    vec3 origX = y;

    y = EvaluateMainLumFactors (y);
    y = DivMapInverse (y, splttAlpha);

    y = LiftShadowsAndDimHighlights (y);
    y = clamp(y,0.0,1.0);

    y = DivMap (y, splttAlpha);

    vec3 postSHLumX = y;

    vec3 fShadowFactor = splttShadow;
    vec3 fDeltaFactor  = splttDelta;
    vec3 fHighlightFactor = fShadowFactor + fDeltaFactor;

    vec3 deltaForShadowOnly = (vec3(2.0) - fShadowFactor);
    vec3 deltaForHighlightOnly = (fHighlightFactor - vec3(1.0));

    vec3 y3 = y*y*y;
    vec3 iY = y*(1.0-y);

    vec3 p1 = iY*fShadowFactor;

    vec3 stY = p1 + iY*fDeltaFactor*y + y3;
    vec3 shadowY = p1 + iY*deltaForShadowOnly*y + y3;           //EvaluateFactors (y, fShadowFactor, deltaForShadowOnly);
    vec3 highlightY = iY + iY*deltaForHighlightOnly*y + y3;     //EvaluateFactors (y, vec3(1.0), deltaForHighlightOnly);

    vec3 compositeWeight = 2.0 * origX - 1.0;                   //maskfunc (origX, kZero);
    compositeWeight = 1.0 - compositeWeight*compositeWeight;

    vec3 blendMix = step(-origX,vec3(-0.5));
    vec3 shNoBlend = mix(shadowY,highlightY,blendMix);

    //vec3 shNoBlend = origX <= 0.5 ? shadowY : highlightY;       //cr_select (tempH, tempS, origX <= 0.5f);

    shNoBlend = mix (shNoBlend, postSHLumX, compositeWeight);

    y = mix (shNoBlend, stY, blending);

    // Apply midtone hue/sat if needed.

    vec3 midY = DivMap (y, midtoneMapAlpha.xyz);


    // Use fBlending with the mask to control how the midtones
    // falloff as they approach 0.0 and 1.0.

    vec3 compositeMidWeight = maskfunc (origX, blending);

    y = mix (y, midY, compositeMidWeight);
    y = clamp(y,0.0,1.0);

    // Apply midtone luminance adjustment if needed.

    midY = DivMap (y, midtoneMapAlpha.w);
    y = mix (y, midY, blending);

    y = DivMapInverse (y, splttAlpha);

    // Apply global settings outside the balance map.

    y = DivMap (y, 3.67199902667);

    // Apply color and Luminance.

    y = DivMap (y, globalMapAlpha.xyz);
    y = DivMap (y, globalMapAlpha.w);

    y = DivMapInverse (y, 3.67199902667);
    y = clamp(y,0.0,1.0);
    return y;
}

float calcProtection(in float hue, in float sat)
{
    float val = mod(hue + 1.0,6.0);
    float minSide = min(val,1.0);
    float maxSide = clamp(1.0 - (val - 1.5) * 4.0,0.0,1.0);
    val = min(minSide, maxSide);
    return val * (1.0 - sat*sat);
}

vec3 rgb2HSL(in vec3 rgbCol)
{
    vec3 colFactors;
    vec3 outVal;

    outVal.y = min(min(rgbCol.r, rgbCol.g), rgbCol.b);
    outVal.z = max(max(rgbCol.r, rgbCol.g), rgbCol.b);

    //float delta = (outVal.z - outVal.y);

    float delta = max(0.0001,outVal.z - outVal.y);
    /*
        float deltaFactor = sign(delta);
        delta += 1.0 - deltaFactor;
    */
    colFactors.xyz = 1.0 - sign(outVal.z - rgbCol.rgb);
    colFactors.yzx *= 1.0 - colFactors.xyz;

    outVal.x = ((rgbCol.g - rgbCol.b) / delta)*colFactors.x;
    outVal.x += (2.0+(rgbCol.b - rgbCol.r) / delta)*colFactors.y;
    outVal.x += (4.0+(rgbCol.r - rgbCol.g) / delta)*colFactors.z;

    //outVal.x *= deltaFactor;
    outVal.x = mod(outVal.x+6.0,6.0);

    return outVal;
}

vec3 calcLumSat(float l, float satShift, float initGap, vec3 hsl)
{
    vec3 outVal = hsl;

    float lumShift = l;
    lumShift *= 1.0 - pow(1.0 - initGap,8.0);

    outVal.yz *= lumShift * (1.0 - outVal.yz) + 1.0;
    outVal.yz *= lumShift * (1.0 - outVal.yz) + 1.0;


    //negative satShift

    float negShift = min(0.0,satShift);
    float L = 0.5 * (outVal.z + outVal.y);

    outVal.y += negShift * (outVal.y - L);
    outVal.z += negShift * (outVal.z - L);


    //pos satShift

    float gap = outVal.z - outVal.y;
    float lumSaturation = gap / outVal.z;

    float saturationFactor = 1.0 - (1.0/6.0);

    saturationFactor *= max(0.0,satShift);

    saturationFactor *= 1.0 - outVal.y;


    float shadowRolloff = min (1.0,16.0 * outVal.z);
    shadowRolloff *= 2.0 - shadowRolloff;
    saturationFactor *= shadowRolloff;

    lumSaturation = lumSaturation / (1.0 - saturationFactor * (1.0 - lumSaturation));

    outVal.y = outVal.z - lumSaturation * outVal.z;

        float outGray = l;
        float grayFactor = step(0.0,outGray);

        float minT = (9.0 * hsl.y < 1.0) ? (3.0 * hsl.y) / (0.25 + 6.75 * hsl.y) : 0.25 + 0.75 * hsl.y;
        float maxT = (9.0 * hsl.z < 1.0) ? (3.0 * hsl.z) / (0.25 + 6.75 * hsl.z) : 0.25 + 0.75 * hsl.z;
        //float resultT = clamp (minT + outGray * (maxT - minT), 0.0 , 1.0);
        float resultT = clamp (mix(minT,maxT,outGray), 0.0 , 1.0);

        resultT = (3.0 * resultT < 1.0) ? resultT / (12.0 - 27.0 * resultT) : (resultT - 0.25) / 0.75;

        outGray = hsl.y + outGray * (hsl.z-hsl.y);
        outGray = clamp(outGray,0.0,1.0);

        outGray = mix(resultT,outGray,grayFactor);

        outVal.yz = mix(outVal.yz,vec2(outGray,outGray),grayMix);

    return outVal;
}

vec3 getHSLForHue(float hue, vec3 hslValues[9])
{
    vec4 mask;
    //const float tapAngles[9] = { 0.0, 0.5, 0.9166667, 1.5833334, 2.6666667, 3.8333334, 4.58333334, 5.5, 6.0 };

    const vec4 tapSlopesA = vec4(2.0, 2.3999998080000156, 1.4999999250000038, 0.9230769514792908);
    const vec4 tapAddsA = vec4(0.0, -1.1999999040000078, -1.374999981250001, -1.4615385680473403);
    const vec4 tapSlopesB = vec4(0.857142832653062, 1.3333334400000079, 1.0909090988429755, 2.0);
    const vec4 tapAddsB = vec4(-2.285714248979593, -5.111111608888926, -5.000000043636366, -11.0);

    vec3 outVals;
    vec4 t;
    vec4 vA,vB;

    t = hue*tapSlopesA + tapAddsA;

    mask = (1.0-step(1.0,t))*step(0.0,t);
    outVals = mask.x*mix(hslValues[0],hslValues[1],t.x); // ((hslValues[1] - hslValues[0])*t.x + hslValues[0]);
    outVals += mask.y*mix(hslValues[1],hslValues[2],t.y); // ((hslValues[2] - hslValues[1])*t.y + hslValues[1]);
    outVals += mask.z*mix(hslValues[2],hslValues[3],t.z); // ((hslValues[3] - hslValues[2])*t.z + hslValues[2]);
    outVals += mask.w*mix(hslValues[3],hslValues[4],t.w); // ((hslValues[4] - hslValues[3])*t.w + hslValues[3]);

    t = hue*tapSlopesB + tapAddsB;
    mask = (1.0-step(1.0,t))*step(0.0,t);
    outVals += mask.x*mix(hslValues[4],hslValues[5],t.x); // ((hslValues[5] - hslValues[4])*t.x + hslValues[4]);
    outVals += mask.y*mix(hslValues[5],hslValues[6],t.y); // ((hslValues[6] - hslValues[5])*t.y + hslValues[5]);
    outVals += mask.z*mix(hslValues[6],hslValues[7],t.z); // ((hslValues[7] - hslValues[6])*t.z + hslValues[6]);
    outVals += mask.w*mix(hslValues[7],hslValues[8],t.w); // ((hslValues[8] - hslValues[7])*t.w + hslValues[7]);

    outVals.x = mod(outVals.x+6.0,6.0);
    return outVals;
}

vec3 hsl2RGB(in vec3 hslCol)
{
    float dH = floor(hslCol.x);
    float h = fract(hslCol.x);

    //if(mod(dH,2.0) > 0.5)
    //    h = 1.0 - h;
    //float f = sign(max(mod(dH,2.0)-0.5,0.0));
    //h = mix(h,1.0-h,f);

    float g = mod(dH,2.0); // = 0 , 1
    float f = 1.0-2.0*g;
    h = f*h + g;

    //h = (mod(dH,2.0) > 0.5) ? (1.0-h) : h;

    vec3 cCol;
    vec3 outCol;

    cCol.x = hslCol.y;
    cCol.y = hslCol.z;
    cCol.z = (cCol.y - cCol.x) * h + cCol.x;

    outCol = mix(cCol.yzx,cCol.zyx,float(dH == 1.0));
    outCol = mix(outCol,cCol.xyz, float(dH == 2.0));
    outCol = mix(outCol,cCol.xzy, float(dH == 3.0));
    outCol = mix(outCol,cCol.zxy, float(dH == 4.0));
    outCol = mix(outCol,cCol.yxz, float(dH == 5.0));

    return outCol;
}

float spline8Value(in vec3 curve[8], in float t,float powVal)
{
    float checkT; vec3 tVec; float tMask; float outVal;

	t = pow(t, powVal);
    t *= 8.0;
    checkT = t;
    t = fract(t);
    tVec.x = 1.0;
    tVec.y = t;
    tVec.z = t*t;

    tMask = step(checkT,1.0);
    outVal = dot(curve[0],tVec)*tMask;

    tMask = step(checkT,2.0)*step(1.0,checkT);  outVal += dot(curve[1],tVec)*tMask;
    tMask = step(checkT,3.0)*step(2.0,checkT);  outVal += dot(curve[2],tVec)*tMask;
    tMask = step(checkT,4.0)*step(3.0,checkT);  outVal += dot(curve[3],tVec)*tMask;
    tMask = step(checkT,5.0)*step(4.0,checkT);  outVal += dot(curve[4],tVec)*tMask;
    tMask = step(checkT,6.0)*step(5.0,checkT);  outVal += dot(curve[5],tVec)*tMask;
    tMask = step(checkT,7.0)*step(6.0,checkT);  outVal += dot(curve[6],tVec)*tMask;
    tMask = step(7.0,checkT);                   outVal += dot(curve[7],tVec)*tMask;

    return outVal;
}

vec3 evalUndoRenderCurve(vec3 col)
{
    vec2 fMinMax;
    vec2 nMinMax;

    float eps = 0.00001;


    fMinMax.x = min(min(col.r,col.g),col.b);
    fMinMax.y = max(max(col.r,col.g),col.b);

    vec2 t = pow(fMinMax,vec2(3.14453125));

    nMinMax = pow(fMinMax,vec2(0.8125))*0.3828125*(1.0-t) + (1.0-pow(1.0-fMinMax,vec2(0.69140625)))*t;

    fMinMax.y = (nMinMax.y-nMinMax.x)/(fMinMax.y-fMinMax.x+eps);

    return (col-fMinMax.x)*fMinMax.y + nMinMax.x;
}

vec3 evalToneCurve(vec3 curve[8],vec3 col)
{
    float fMin,fMax;
    vec2 nMinMax;
    float eps = 0.00001;

    fMin = min(min(col.r,col.g),col.b);
    fMax = max(max(col.r,col.g),col.b);

    nMinMax.x = spline8Value(curve,fMin,1.0/1.78);
    nMinMax.y = spline8Value(curve,fMax,1.0/1.78);

    nMinMax = min(nMinMax,0.9999);

    fMax = (nMinMax.y-nMinMax.x)/(fMax-fMin+eps);

    return (col-fMin)*fMax + nMinMax.x;
}

float calcPosBlacks(float x,float blacks)  // scale
{
    float pVal = 1.0/(1.0+blacks*0.18);
    return (1.0 - x*x)*pow(x,pVal) + x*x*x;
}

float calcNegWhites(float x,float whites) // scale
{
    float smallX = x;
    float pVal = 1.0/(1.0-whites*0.72);
    return mix(x,pow(x,pVal),smallX); //smallX*pow(x,pVal) + (1.0-smallX)*x;
}

vec2 calcPosWhites(vec2 x,float whites)
{
    float val = 1.0 - (0.8*whites);
    return min((x/(1.0+x))*(1.0+x/(val*val)), vec2(1.0,1.0));
}

vec3 calcNegBlacks(vec3 x,float blacks)
{
    highp float n = 0.04*blacks*blacks;

    highp float m = min(0.05,n);
    highp float a = 1.0/(4.0*max(m,0.0004)*(1.0-n));
    highp vec3 b = (n-m)-x;
    b = b*b;

    highp vec3 roundY = a*b;
    highp vec3 straightY = max(1.0/(1.0-n)*(x-n),0.0);

    highp float sect = (n-m)+((1.0/(1.0-n))/(2.0*a));

    highp vec3 straightFactor = step(sect,x);

    return mix(roundY,straightY,straightFactor);

    //return max(vec3(0.0,0.0,0.0),straightY);
}


void main(void)
{
    vec2 fMinMax;
    vec2 nMinMax;
    vec2 endPin;

    float pinWeight;
    float eps = 0.00001;
/*
    const highp mat3 fromSRGBMatr = mat3(0.529285,0.098394,0.016823,
    0.330046,0.873493,0.117671,
    0.140669,0.028113,0.865506);
*/
    const highp mat3 toSRGBMatr = mat3(2.034302,-0.228882,-0.00842285,
    -0.727295,1.23168945,-0.15332,
    -0.307007,-0.00280762,1.16174316);

    const highp mat3 fromBT2020Full = mat3(0.8352,0.05407,-0.002447,
                                           0.04872,0.9290,0.03639,
                                            0.1161,0.01695,0.9661);

    highp vec4 col = texture(uSampler, vTextureCoord.st);

    highp vec3 rgbCol = col.rgb;
    //rgbCol = clamp(rgbCol,0.0001,0.999);

    //rgbCol = preColorMatr * rgbCol;
    //rgbCol = max(rgbCol,vec3(0.0,0.0,0.0));

    //rgbCol = invSRGBGamma(rgbCol);
    //rgbCol = fromSRGBMatr * rgbCol;

    rgbCol = invHLGGamma(rgbCol);
    rgbCol = fromBT2020Full * rgbCol;
    rgbCol = clamp(rgbCol,0.0001,0.999);

    rgbCol = evalUndoRenderCurve(rgbCol);

    // White Balance

    rgbCol = indexMatr * rgbCol;
    rgbCol *= abcScale;
    vec3 colorThresh = abcScale*0.25;
    vec3 colorScale = 1.0/(abcScale*0.75);
    float t1 = max(rgbCol.r, rgbCol.g);
    vec2 recT;
    recT = max ((rgbCol.rg - colorThresh.xy) * colorScale.xy, 0.0);
    recT = recT * recT;
    rgbCol.rg = mix(rgbCol.rg, vec2(t1), recT);
    t1 = max(0.5 * (rgbCol.r + rgbCol.g), rgbCol.b);
    recT.x = max((rgbCol.b - colorThresh.z) * colorScale.z, 0.0);
    recT.x = recT.x * recT.x;
    rgbCol.b = mix(rgbCol.b, t1, recT.x);
    rgbCol = abcMatr * rgbCol;
    rgbCol = clamp(rgbCol,0.0,1.0);

    // Vignette

    vec2 pos = vTextureCoord - 0.5;
    float dlFactor = 1.0-max(sign(vignetteAmount),0.0);
    vec3 resultColor = mix(vec3(1.0,1.0,1.0),vec3(0.0,0.0,0.0),dlFactor);
    float blackShift = dlFactor*0.28;
    float maskShift = mix(2.4,0.334,dlFactor);
    float absAmount = abs(vignetteAmount);
    vec2 circlePos = pos*aspect;
    float circleLen = length(circlePos)*0.95;
    float elipseLen = length(pos)*1.15;
    float rectRadius = 1.0-(2.0*max(-roundness-0.5,0.0)*0.86);
    vec2 minValue = max(2.0*abs(circlePos)-aspect+rectRadius,0.0);
    float rectLen = length(minValue)*0.576;
    rectLen /= rectRadius;
    float rectFactor = min(1.0,max(-roundness,0.0)*2.0);
    elipseLen = mix(elipseLen,rectLen,rectFactor);
    elipseLen = mix(elipseLen,circleLen,max(roundness,0.0));
    elipseLen = pow(1.0-elipseLen,0.8125);
    float addVal = midpoint*0.3 + elipseLen*max(midpoint,0.0)*0.5 - feather*0.3;
    float start = mix(0.495,-0.25+blackShift,feather) - addVal;
    float end = mix(0.505,0.4+blackShift,feather) - addVal;
    float maskFactor = 1.0 - (1.0 - smoothstep(start,end,elipseLen))*pow(absAmount,maskShift);
    float maxScale = mix(maskFactor,1.0,highlightContrast);
    float v = max(max(rgbCol.r,rgbCol.g),rgbCol.b);
    v = max(v*1.21951219512195 - 0.21951219512195,0.0);
    maskFactor = mix(maskFactor,maxScale,v);
    rgbCol = mix(rgbCol,resultColor,1.0-maskFactor);

    const float lumPower = 1.8;
    const float highRange = 0.5;
    const float lowRange = 0.0;

    float shLum = pow(dot(rgbCol,vec3(0.25,0.5,0.25)),1.0/2.4);
    float startLum = shLum;

    vec2 lightValues;
    shLum = pow(shLum,1.0/lumPower);

    float shDiff = dot(shadowHighlights,vec2(0.5,0.5));
    shLum += shDiff*0.15;
    shLum = shLum = clamp(shLum,0.0,1.0);

    lightValues.x = (shLum - highRange)/(1.0-highRange);
    lightValues.y = ((1.0 - shLum) - lowRange)/(1.0-lowRange);

    lightValues = max(lightValues,0.0);
    lightValues = (1.0 - lightValues)*lightValues*lightValues;

    shLum += dot(lightValues,shadowHighlights); ///sum;
    shLum = pow(shLum,lumPower);

    rgbCol *= shLum/startLum;

    float negWhites = max(-whites,0.0);
    float posWhites = max(whites,0.0);
    float negBlacks = max(-blacks,0.0);
    float posBlacks = max(blacks,0.0);

    // Exposure + black&whites

    rgbCol *= exposureGain;


    pinWeight = max(exposure,0.0)/8.488339269336635;
    pinWeight *= pinWeight;
    pinWeight *= pinWeight;
    fMinMax.x = min(min(rgbCol.r,rgbCol.g),rgbCol.b);
    fMinMax.y = max(max(rgbCol.r,rgbCol.g),rgbCol.b);

    nMinMax = fMinMax;

    nMinMax = calcPosWhites(nMinMax,posWhites);

    nMinMax = min(nMinMax,1.0);
    nMinMax = pow(nMinMax,vec2(1.0/2.2));
    endPin = pow(1.0-nMinMax,vec2(exposure));
    nMinMax = 1.0-endPin;
    endPin *= endPin;
    endPin = 1.0-endPin*pinWeight;
    nMinMax *= endPin;

    nMinMax = pow(nMinMax,vec2(2.2));
    fMinMax.y = (nMinMax.y-nMinMax.x)/(fMinMax.y-fMinMax.x+eps);

    rgbCol = (rgbCol-fMinMax.x)*fMinMax.y + nMinMax.x;

    float curLum = dot(rgbCol,vec3(0.25,0.5,0.25));
    float newLum = calcNegWhites(curLum,negWhites);
    highp float lumScale = newLum/(curLum+0.00000190734863);
    rgbCol *= lumScale;

    rgbCol = calcNegBlacks(rgbCol,negBlacks);

    curLum = dot(rgbCol,vec3(0.25,0.5,0.25));
    newLum = calcPosBlacks(curLum,posBlacks);
    lumScale = newLum/(curLum+0.00000190734863);
    rgbCol *= lumScale;
    rgbCol = clamp(rgbCol,0.0,1.0);

    // Saturation

    const vec3 lumvec = vec3(0.2880859375, 0.7119140625,0.0);
    vec3 yuv;
    yuv.x = dot(rgbCol,lumvec);
    yuv.y = (rgbCol.r-yuv.x) * (saturation*0.70233196159122);
    yuv.z = (rgbCol.b-yuv.x) * saturation;
    yuv = yuv.yyz * vec3(1.423828125,-0.576171875,1.0) + yuv.x;
    rgbCol = clamp(yuv,0.0,1.0);

    // Vibrance

    vec3 hsl = rgb2HSL(rgbCol);
    float gap = hsl.z - hsl.y;
    float sFactor = step(0.0,-vibrance);
    float vibSat = (hsl.z - hsl.y) / hsl.z;
    float protection = calcProtection(hsl.x, vibSat);
    float shadowRolloff = min( 1.0, 16.0 * hsl.z );
    float saturationMask = vibSat * (1.0 - vibSat);
    saturationMask *= 2.0 - saturationMask;
    shadowRolloff *= 2.0 - shadowRolloff;
    float brightnessFactor = shadowRolloff * saturationMask;
    float shiftFactor = vibrance * (vibrance * protection + 1.0 - protection);
    float saturationFactor = (35.0 - 17.0*protection)/42.0 * shiftFactor * (1.0 - hsl.y)*shadowRolloff;
    float satPos = vibSat / (1.0 - saturationFactor * (1.0 - vibSat));
    float brightPos = brightnessFactor * shiftFactor * 0.25;
    saturationFactor = -vibrance;
    float satNeg = vibSat;
    satNeg *= 1.0 - saturationFactor + saturationFactor * vibSat * (0.5 + 0.5 * vibSat) * shadowRolloff;
    satNeg *= 1.0 - 0.25 * saturationFactor;
    float brightNeg = -(brightnessFactor*saturationFactor);
    vibSat = mix(satPos,satNeg,sFactor);
    brightnessFactor = mix(brightPos,brightNeg,sFactor);
    hsl.z *= 1.0 + brightnessFactor * (1.0 - hsl.z);
    hsl.y = hsl.z - vibSat * hsl.z;

    vec3 hslMix = getHSLForHue(hsl.x, hslValues);
    hsl.x = hslMix.x;

    hsl = calcLumSat(hslMix.z,hslMix.y,gap,hsl);

    rgbCol = hsl2RGB(hsl);
    rgbCol = clamp(rgbCol,0.0,1.0);

    rgbCol = evalToneCurve(renderCurve,rgbCol);
	rgbCol = clamp(rgbCol,0.0,1.0);

	rgbCol.r = spline8Value(toneCurveR, rgbCol.r,1.0);
	rgbCol.g = spline8Value(toneCurveG, rgbCol.g,1.0);
	rgbCol.b = spline8Value(toneCurveB, rgbCol.b,1.0);
	rgbCol = clamp(rgbCol,0.0,1.0);

    // Splittone
/*
    vec3 tCol = rgbCol*splttAlpha;
    rgbCol = tCol/(1.0+tCol-rgbCol);
    rgbCol = rgbCol*((1.0-rgbCol) * (splttShadow + splttDelta * rgbCol) + rgbCol*rgbCol);
    tCol = rgbCol/splttAlpha;
    rgbCol = tCol/(1.0+tCol-rgbCol);
*/
    rgbCol = doColorGrading(rgbCol);

    // Grain

    vec4 grainCoord = texture(grainSampler,vGrainCoord);

    float grainTime = fract(grainPos.z/64.0);
    float grainZ = 1.0-grainCoord.g;
    grainCoord.xy = fract(vTextureCoord.st *  grainPos.xy/128.0);
    grainCoord.z = fract(grainZ+grainTime);
    grainCoord.w = 1.0;
    grainCoord = texture(grainSampler,grainCoord.xyz);

    vec3 mask = clamp(rgbCol * 20.0    + 0.0,0.0,1.0) * clamp(rgbCol * -20.0 + 20.0,0.0,1.0);
    vec2 highFreq = grainCoord.rb;
    highFreq.x = 0.4 + grainCoord.r - grainCoord.g*0.8;

    vec2 freq = mix(highFreq,grainCoord.rg,grainRoughness);
    float grainFreq = mix(freq.x,freq.y,grainSize);

    rgbCol += grainAmount*(grainFreq-0.5)*mask;


    rgbCol = toSRGBMatr * rgbCol;
    col.rgb = sRGBGamma(rgbCol);

    // Profile 3D Lookup

    col.rgb = clamp(col.rgb,0.0,1.0);
    col.rgb = mix(col.rgb,texture(lookupSampler,col.rgb).rgb,lutAmount);

    col.rgb = postColorMatr * col.rgb;

    fragmentColor = col;
}
