Browse Source

Implemented custom built-in functions and added HslToRgb in CombineColor

CPKreuz 11 months ago
parent
commit
a48b9d2d0f

+ 15 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -109,6 +109,21 @@ public class FuncContext
         return Builder.ConstructHalf4(r, g, b, a);
     }
 
+    public Half4 HslaToRgba(Expression h, Expression s, Expression l, Expression a)
+    {
+        if (!HasContext && h is Float1 firstFloat && s is Float1 secondFloat && l is Float1 thirdFloat && a is Float1 fourthFloat)
+        {
+            Half4 constantHalf4 = new Half4("");
+            var hValue = firstFloat.ConstantValue * 360;
+            var sValue = secondFloat.ConstantValue * 100;
+            var lValue = thirdFloat.ConstantValue * 100;
+            byte aByte = (byte)(fourthFloat.ConstantValue * 255);
+            constantHalf4.ConstantValue = Color.FromHsl((float)hValue, (float)sValue, (float)lValue, aByte);
+            return constantHalf4;
+        }
+        
+        return Builder.AssignNewHalf4(Builder.Functions.GetHslToRgb(h, s, l, a));
+    }
 
     public Half4 NewHalf4(Expression assignment)
     {

+ 11 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs

@@ -34,10 +34,20 @@ public class CombineColorNode : Node
 
     private Half4 GetColor(FuncContext ctx)
     {
+        var a = ctx.GetValue(A);
+        
+        if (Mode.Value == CombineSeparateColorMode.HSL)
+        {
+            var h = ctx.GetValue(R);
+            var s = ctx.GetValue(G);
+            var l = ctx.GetValue(B);
+
+            return ctx.HslaToRgba(h, s, l, a);
+        }
+        
         var r = ctx.GetValue(R);
         var g = ctx.GetValue(G);
         var b = ctx.GetValue(B);
-        var a = ctx.GetValue(A);
 
         return ctx.NewHalf4(r, g, b, a); 
     }

+ 10 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/BuiltInFunctionType.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation;
+
+public enum BuiltInFunctionType
+{
+    HueToRgb,
+    RgbToHsl,
+    HslToRgb,
+    RgbToHsv,
+    HsvToRgb
+}

+ 99 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/BuiltInFunctions.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation;
+
+public class BuiltInFunctions
+{
+    private readonly List<BuiltInFunctionType> usedFunctions = new(Enum.GetValues(typeof(BuiltInFunctionType)).Length);
+
+    public Expression GetHslToRgb(Expression hsla)
+    {
+        Require(BuiltInFunctionType.HslToRgb);
+        
+        return new Expression($"{nameof(HslToRgb)}({hsla.ExpressionValue})");
+    }
+
+    public Expression GetHslToRgb(Expression h, Expression s, Expression l, Expression a) =>
+        GetHslToRgb(Half4.Constructor(h, s, l, a));
+    
+    private void Require(BuiltInFunctionType type)
+    {
+        if (usedFunctions.Contains(type))
+        {
+            return;
+        }
+
+        if (type is BuiltInFunctionType.HslToRgb)
+        {
+            Require(BuiltInFunctionType.HueToRgb);
+        }
+            
+        usedFunctions.Add(type);
+    }
+
+    public string BuildFunctions()
+    {
+        var builder = new StringBuilder();
+
+        AppendIf(BuiltInFunctionType.HueToRgb, HueToRgb);
+        AppendIf(BuiltInFunctionType.HslToRgb, HslToRgb);
+        
+        return builder.ToString();
+
+        void AppendIf(BuiltInFunctionType type, string source)
+        {
+            if (usedFunctions.Contains(type))
+            {
+                builder.Append(source);
+            }
+        }
+    }
+    
+    // Taken from here https://www.shadertoy.com/view/4dKcWK
+    private const string HueToRgb =
+        $$"""
+        half3 {{nameof(HueToRgb)}}(float hue)
+        {
+            vec3 rgb = abs(hue * 6. - vec3(3, 2, 4)) * vec3(1, -1, -1) + vec3(-1, 2, 2);
+            return clamp(rgb, 0., 1.);
+        }
+        """;
+    
+    private const string HslToRgb = 
+        $$"""
+        half4 {{nameof(HslToRgb)}}(half4 hsla)
+        {
+            half3 rgb = {{nameof(HueToRgb)}}(hsla.x);
+            float c = (1. - abs(2. * hsla.z - 1.)) * hsla.y;
+            return half4((rgb - 0.5) * c + hsla.z, hsla.w);
+        }
+        """;
+    
+    // private const string RgbToHcv = 
+    //     """
+    //     half4 RGBtoHSL(half4 rgba)
+    //     {
+    //         half4 p = (rgba.g < rgba.b) ? half4(rgba.bg, -1., 2. / 3.) : half4(rgba.gb, 0., -1. / 3.);
+    //         half4 q = (rgba.r < p.x) ? half4(p.xyw, rgba.r) : half4(rgba.r, p.yzx);
+    //         float c = q.x - min(q.w, q.y);
+    //         float h = abs((q.w - q.y) / (6. * c + EPSILON) + q.z);
+    //         return half4(h, c, q.x, rgba.w);
+    //     }
+    //     """;
+    //
+    // private const string RgbToHsl = 
+    //     """
+    //     half4 RGBtoHSL(half4 rgba)
+    //     {
+    //         // RGB [0..1] to Hue-Saturation-Lightness [0..1]
+    //         half3 hcv = RGBtoHCV(rgba.xyz);
+    //         half z = hcv.z - hcv.y * 0.5;
+    //         half s = hcv.y / (1. - abs(z * 2. - 1.) + EPSILON);
+    //         return half4(hcv.x, s, z, rgba.w);
+    //     }
+    //     """;
+}

+ 6 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/Expressions/Half4.cs

@@ -36,4 +36,10 @@ public class Half4(string name) : ShaderExpressionVariable<Color>(name), IMultiV
             _ => throw new IndexOutOfRangeException()
         };
     }
+
+    public static string ConstructorText(Expression r, Expression g, Expression b, Expression a) =>
+        $"half4({r.ExpressionValue}, {g.ExpressionValue}, {b.ExpressionValue}, {a.ExpressionValue})";
+
+    public static Expression Constructor(Expression r, Expression g, Expression b, Expression a) =>
+        new Expression(ConstructorText(r, g, b, a));
 }

+ 6 - 6
src/PixiEditor.DrawingApi.Core/Shaders/Generation/ShaderBuilder.cs

@@ -16,6 +16,8 @@ public class ShaderBuilder
 
     private Dictionary<Texture, TextureSampler> _samplers = new Dictionary<Texture, TextureSampler>();
 
+    public BuiltInFunctions Functions { get; } = new();
+
     public Shader BuildShader()
     {
         string generatedSksl = ToSkSl();
@@ -26,6 +28,9 @@ public class ShaderBuilder
     {
         StringBuilder sb = new StringBuilder();
         AppendUniforms(sb);
+
+        sb.AppendLine(Functions.BuildFunctions());
+        
         sb.AppendLine("half4 main(float2 coords)");
         sb.AppendLine("{");
         sb.Append(_bodyBuilder);
@@ -152,12 +157,7 @@ public class ShaderBuilder
         Half4 result = new Half4(name);
         _variables.Add(result);
 
-        string rExpression = r.ExpressionValue;
-        string gExpression = g.ExpressionValue;
-        string bExpression = b.ExpressionValue;
-        string aExpression = a.ExpressionValue;
-
-        _bodyBuilder.AppendLine($"half4 {name} = half4({rExpression}, {gExpression}, {bExpression}, {aExpression});");
+        _bodyBuilder.AppendLine($"half4 {name} = {Half4.ConstructorText(r, g, b, a)};");
         return result;
     }