uniform sampler2D qt_Texture0;
uniform sampler3D lut_table;
uniform sampler2DArray colormap;
uniform vec3 mtf_param[3];
uniform vec2 unit_scale;
uniform bool bw;
uniform bool invert;
uniform bool srgb;
uniform bool false_color;
uniform int filtering;
uniform int colormapIdx;
in vec2 qt_TexCoord0;
layout(location = 0) out vec4 color;

vec3 Linear2sRGB(vec3 color)
{
    return mix(12.92 * color.rgb,
               1.055 * pow(color, vec3(1.0 / 2.4)) - 0.055,
               greaterThan(color, vec3(0.0031308)));
}

vec4 MTF(vec4 x, vec4 low, vec4 mid, vec4 high)
{
    x = (x - low) / (high - low);
    x = clamp(x, vec4(0.0), vec4(1.0));
    return ((mid - 1.0) * x) / ((2.0 * mid - 1.0) * x - mid);
}

vec3 falsecolor(float color)
{
    color *= 255.0 / 256.0;
    color += 0.5 / 256.0;
    return texture(colormap, vec3(color, 0.5, colormapIdx)).rgb;
}

vec3 checker()
{
    vec2 pattern = fract(gl_FragCoord.xy * 0.0625) - 0.5;
    return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25);
}

vec4 cubic(float v)
{
    vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
    vec4 s = n * n * n;
    float x = s.x;
    float y = s.y - 4.0 * s.x;
    float z = s.z - 4.0 * s.y + 6.0 * s.x;
    float w = 6.0 - x - y - z;
    return vec4(x, y, z, w) * (1.0/6.0);
}

vec4 textureBicubic(sampler2D sampler, vec2 texCoords)
{
    vec2 texSize = vec2(textureSize(sampler, 0));
    vec2 invTexSize = 1.0 / texSize;

    texCoords = texCoords * texSize - 0.5;

    vec2 fxy = fract(texCoords);
    texCoords -= fxy;

    vec4 xcubic = cubic(fxy.x);
    vec4 ycubic = cubic(fxy.y);

    vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy;

    vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
    vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s;

    offset *= invTexSize.xxyy;

    vec4 sample0 = texture(sampler, offset.xz);
    vec4 sample1 = texture(sampler, offset.yz);
    vec4 sample2 = texture(sampler, offset.xw);
    vec4 sample3 = texture(sampler, offset.yw);

    float sx = s.x / (s.x + s.y);
    float sy = s.z / (s.z + s.w);

    return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
}

vec4 textureCatmul(sampler2D sampler, vec2 texCoords)
{
   ivec2 texSize = textureSize(sampler, 0);

   texCoords = texCoords * vec2(texSize) - 0.5;

   ivec2 texel = ivec2(floor(texCoords));
   vec2 fra = fract(texCoords);
   texSize -= 1;

   const mat4 CatMul = mat4(0, 1, 0, 0, -0.5, 0, 0.5, 0, 1, -2.5, 2, -0.5, -0.5, 1.5, -1.5, 0.5);
   vec4 xx = CatMul * vec4(1.0, fra.x, fra.x*fra.x, fra.x*fra.x*fra.x);
   vec4 yy = CatMul * vec4(1.0, fra.y, fra.y*fra.y, fra.y*fra.y*fra.y);

   vec4 a00 = texelFetch(sampler, clamp(texel + ivec2(-1, -1), ivec2(0, 0), texSize), 0) * xx.x;
   vec4 a01 = texelFetch(sampler, clamp(texel + ivec2( 0, -1), ivec2(0, 0), texSize), 0) * xx.y;
   vec4 a02 = texelFetch(sampler, clamp(texel + ivec2( 1, -1), ivec2(0, 0), texSize), 0) * xx.z;
   vec4 a03 = texelFetch(sampler, clamp(texel + ivec2( 2, -1), ivec2(0, 0), texSize), 0) * xx.w;
   vec4 a10 = texelFetch(sampler, clamp(texel + ivec2(-1,  0), ivec2(0, 0), texSize), 0) * xx.x;
   vec4 a11 = texelFetch(sampler, clamp(texel + ivec2( 0,  0), ivec2(0, 0), texSize), 0) * xx.y;
   vec4 a12 = texelFetch(sampler, clamp(texel + ivec2( 1,  0), ivec2(0, 0), texSize), 0) * xx.z;
   vec4 a13 = texelFetch(sampler, clamp(texel + ivec2( 2,  0), ivec2(0, 0), texSize), 0) * xx.w;
   vec4 a20 = texelFetch(sampler, clamp(texel + ivec2(-1,  1), ivec2(0, 0), texSize), 0) * xx.x;
   vec4 a21 = texelFetch(sampler, clamp(texel + ivec2( 0,  1), ivec2(0, 0), texSize), 0) * xx.y;
   vec4 a22 = texelFetch(sampler, clamp(texel + ivec2( 1,  1), ivec2(0, 0), texSize), 0) * xx.z;
   vec4 a23 = texelFetch(sampler, clamp(texel + ivec2( 2,  1), ivec2(0, 0), texSize), 0) * xx.w;
   vec4 a30 = texelFetch(sampler, clamp(texel + ivec2(-1,  2), ivec2(0, 0), texSize), 0) * xx.x;
   vec4 a31 = texelFetch(sampler, clamp(texel + ivec2( 0,  2), ivec2(0, 0), texSize), 0) * xx.y;
   vec4 a32 = texelFetch(sampler, clamp(texel + ivec2( 1,  2), ivec2(0, 0), texSize), 0) * xx.z;
   vec4 a33 = texelFetch(sampler, clamp(texel + ivec2( 2,  2), ivec2(0, 0), texSize), 0) * xx.w;

   vec4 c = vec4(0.0);
   c += (a00 + a01 + a02 + a03) * yy.x;
   c += (a10 + a11 + a12 + a13) * yy.y;
   c += (a20 + a21 + a22 + a23) * yy.z;
   c += (a30 + a31 + a32 + a33) * yy.w;

   return c;
}

void main(void)
{
    switch(filtering)
    {
    case 0://nearest
        color = texelFetch(qt_Texture0, ivec2(qt_TexCoord0 * vec2(textureSize(qt_Texture0, 0))), 0);
        break;
    default:
    case 1://bilinear
        color = texture(qt_Texture0, qt_TexCoord0);
        break;
    case 2://catmul bicubic
        color = textureCatmul(qt_Texture0, qt_TexCoord0);
        break;
    }

    color.rgb = color.rgb * unit_scale.x + unit_scale.y;
    if(bw)color = color.rrra;
    color = MTF(color, vec4(mtf_param[0], 0.0), vec4(mtf_param[1], 0.5), vec4(mtf_param[2], 1.0));
    if(false_color)color.rgb = falsecolor(color.r);

    if(invert)color.rgb = vec3(1.0) - color.rgb;

    color.rgb = mix(checker(), color.rgb, color.a);

    if(srgb)
    {
        color.rgb *= 31.0 / 32.0;
        color.rgb += 0.5 / 32.0;
        vec4 lut = texture(lut_table, vec3(color.rgb));
        color.rgb = lut.rgb;
        //color.rgb = Linear2sRGB(lut.rgb);
    }

    if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0))))
        color = vec4(0.0, 0.0, 0.0, 1.0);

    color.a = 1.0;
}
