#!/usr/bin/env python
""" This example shows hoe the GLSL program for a 2D texture
can be modified to achieve all kinds of graphic effects.
In this example we will demonstrate sharpening an image using
unsharp masking.
"""

import visvis as vv

# First define our part of the shading code
# The '>>' Denote what piece of code we want to replace.
# There is a pre-loop section, that is executed before the anti-aliasing
# loop. We use this to modify the aliasing kernel so it always does a fixed
# amount of smoothing.
# In the post-loop section we combine the subtracted image with the normal
# sampled image. Unsharp masking consists of subtracting the smoothed image
# from the original (thus removing low frequency components), and then adding
# the result with a certain factor to the original.
#
# Note that the aa kernel is symetric; kernel[0] is the center pixel, and
# kernel[1] through kernel[3] is the tail on all ends.
SH_2F_SHARPEN = vv.shaders.ShaderCodePart('sharpen','unsharp masking',
"""
    >>--uniforms--
    uniform float amount;
    // --uniforms--
    
    >>--pre-loop--
    sze = 3; // Use full kernel (otherwise it wont work if t.aa == 0)
    kernel = vec4(1.0, 0.9, 0.6, 0.3); // approximate Gauss of sigma 2
    float kernel_norm = kernel[0] + (kernel[1] + kernel[2] + kernel[3])*2.0;
    kernel /= kernel_norm;
    // --pre-loop--
    
    >>--post-loop--
    float th = 0.05;
    vec4 normalColor = texture2D(texture, pos);
    // Element-wise mask on blurred image (color1), using a threshold
    float mask = float(length(color1.rgb)-length(color2.rgb)>th);
    normalColor.rgb += mask * amount * (normalColor.rgb -color1.rgb);
    color1 = normalColor;
    // --post-loop--
""")

# Read image
im = vv.imread('astronaut.png')

# Show two times, the second will be sharpened
vv.subplot(121); t1 = vv.imshow(im)
vv.subplot(122); t2 = vv.imshow(im)

# Share cameras and turn off anti-aliasing for proper comparison
t1.parent.camera = t2.parent.camera
t1.aa = 0

# Insert our part in the fragment shader program
t2.shader.fragment.AddOrReplace(SH_2F_SHARPEN, after='base')
if False: # Execute this line to turn it off:
    t2.shader.fragment.RemovePart('sharpen')

# Make a slider to set the amount
def sliderCallback(event):
    t2.shader.SetStaticUniform('amount', slider.value)
    t2.Draw()
slider = vv.Slider(t2.parent, (0.0, 1.5))
slider.textColor = 'blue'
slider.position = 0.05, 10, 0.9, 40
slider.eventSliding.Bind(sliderCallback)
sliderCallback(None) # init uniform

# In case there are bugs in the code, it might be helpfull to see the code
# t2.fragmentShader.ShowCode() # Shows the whole code
t2.shader.fragment.ShowCode('sharpen') # Shows only our bit, with line numbers

# Run app
app = vv.use()
app.Run()
