EssentialGG/Elementa

Add support for rounded images through shaders

caoimhebyrne opened this issue · 1 comments

Is your feature request related to a problem? Please describe.
At the moment, images can only be rounded through stencil which can look very pixelated and ugly

Describe the solution you'd like
fsh:

#version 110
uniform float u_Radius;
uniform vec4 u_InnerRect;

uniform sampler2D u_Texture;

varying vec2 f_Position;
varying vec2 f_TexCoord;

void main() {
    vec2 tl = u_InnerRect.xy - f_Position;
    vec2 br = f_Position - u_InnerRect.zw;
    vec2 dis = max(br, tl);
    float v = length(max(vec2(0.0), dis)) - u_Radius;
    float a = 1.0 - smoothstep(0.0, 1.0, v);
    gl_FragColor = vec4(texture2D(u_Texture, f_TexCoord).rgb, a);
}

vsh:

#version 110

varying vec2 f_Position;
varying out vec2 f_TexCoord;

void main() {
    f_Position = gl_Vertex.xy;
    f_TexCoord = gl_MultiTexCoord0.xy;

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_FrontColor = gl_Color;
}

effect:

class ShaderEffect(private val radius: Float) : Effect() {
    private var shader: Shader = Shader("rounded_image", "rounded_image")
    private var shaderRadiusUniform: FloatUniform = FloatUniform(shader.getUniformLocation("u_Radius"))
    private var shaderInnerRectUniform = Vec4Uniform(shader.getUniformLocation("u_InnerRect"))

    override fun beforeDraw() {
        shader.bindIfUsable()
        shaderRadiusUniform.setValue(radius)
        shaderInnerRectUniform.setValue(
            Vector4f(
                boundComponent.getLeft() + radius,
                boundComponent.getTop() + radius,
                boundComponent.getRight() - radius,
                boundComponent.getBottom() - radius
            )
        )
    }

    override fun afterDraw() {
        shader.unbindIfUsable()
    }
}

Describe alternatives you've considered
Stencil

Additional context
with the shader:
image

mew commented

I think for the vast majority of cases StencilEffect will be more than adequate.

which can look very pixelated and ugly.

I literally had to squint and toggle stencil on and off to see the difference. Yes, it's there, but it's so negligible and definitely doesn't warrant adding a whole new component / effect to remove a few pixels.