Applying fragment shaders to images

Viewed 218

My goal is to apply any shader written in SKSL to an image. I am aware that this feature is available in Skia but not in SkiaSharp, but I see that there has been some effort recently to port the functionality. I've been trying to replicate a solution for one of the pre-release versions that one of the contributors made here: https://github.com/mono/SkiaSharp/issues/1319#issuecomment-640529824 but I had to make a few changes to my code and shader as they weren't seemingly compatible with the latest pre-release version (2.88.0-preview.232). Here is mine for reference:

            // input values
            SKBitmap bmp = new SKBitmap(1000, 1000);
            SKCanvas canvas = new SKCanvas(bmp);
            float threshold = 1.05f;
            float exponent = 1.5f;

            // shader
            var src = @"
                in fragmentProcessor color_map;

                uniform float scale;
                uniform half exp;
                uniform float3 in_colors0;

                half4 main(float2 p) {
                    half4 texColor = sample(color_map, p);
                    if (length(abs(in_colors0 - pow(texColor.rgb, half3(exp)))) < scale)
                        discard;
                    return texColor;
                }";
            using var effect = SKRuntimeEffect.Create(src, out var errorText);

            // input values
            var inputs = new SKRuntimeEffectUniforms(effect);
            inputs["scale"] = threshold;
            inputs["exp"] = exponent;
            inputs["in_colors0"] = new[] {1f, 1f, 1f};

            // shader values
            using var blueShirt = SKImage.FromEncodedData(Path.Combine(PathToImages, "blue-shirt.jpg"));
            using var textureShader = blueShirt.ToShader();
            var children = new SKRuntimeEffectChildren(effect);
            children["color_map"] = textureShader;

            // create actual shader
            using var shader = effect.ToShader(true, inputs, children);

            // draw as normal
            canvas.Clear(SKColors.Black);
            using var paint = new SKPaint { Shader = shader };
            canvas.DrawRect(SKRect.Create(400, 400), paint);

However, I get a System.Runtime.InteropServices.SEHException: 'External component has thrown an exception.' on canvas.DrawRect() which I believe is either a problem with my shader or that this feature is not yet fully supported. Besides manually modifying individual pixels with C# which should be a lot slower than a shader operation, is the latter not possible within the current realm of SkiaSharp? If not, I think I'll be needing to load an image into some library with OpenGL bindings (like OpenTK), apply the shader, take a screenshot of the canvas, then load that image into SkiaSharp, which I imagine is not a great solution. I'd like to see possible solutions even outside of Skia.

0 Answers
Related