|
@@ -0,0 +1,149 @@
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Text;
|
|
|
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
|
|
|
+
|
|
|
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation;
|
|
|
+
|
|
|
+public class BuiltInFunctions
|
|
|
+{
|
|
|
+ private readonly List<IBuiltInFunction> usedFunctions = new(6);
|
|
|
+
|
|
|
+ public Expression GetRgbToHsv(Expression rgba) => Call(RgbToHsv, rgba);
|
|
|
+
|
|
|
+ public Expression GetRgbToHsl(Expression rgba) => Call(RgbToHsl, rgba);
|
|
|
+
|
|
|
+ public Expression GetHsvToRgb(Expression hsva) => Call(HsvToRgb, hsva);
|
|
|
+
|
|
|
+ public Expression GetHsvToRgb(Expression h, Expression s, Expression v, Expression a) =>
|
|
|
+ GetHsvToRgb(Half4Float1Accessor.GetOrConstructorExpressionHalf4(h, s, v, a));
|
|
|
+
|
|
|
+ public Expression GetHslToRgb(Expression hsla) => Call(HslToRgb, hsla);
|
|
|
+
|
|
|
+ public Expression GetHslToRgb(Expression h, Expression s, Expression l, Expression a) =>
|
|
|
+ GetHslToRgb(Half4Float1Accessor.GetOrConstructorExpressionHalf4(h, s, l, a));
|
|
|
+
|
|
|
+ public string BuildFunctions()
|
|
|
+ {
|
|
|
+ var builder = new StringBuilder();
|
|
|
+
|
|
|
+ foreach (var function in usedFunctions)
|
|
|
+ {
|
|
|
+ builder.AppendLine(function.FullSource);
|
|
|
+ }
|
|
|
+
|
|
|
+ return builder.ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Expression Call(IBuiltInFunction function, Expression expression)
|
|
|
+ {
|
|
|
+ Require(function);
|
|
|
+
|
|
|
+ return new Expression(function.Call(expression.ExpressionValue));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Require(IBuiltInFunction function)
|
|
|
+ {
|
|
|
+ if (usedFunctions.Contains(function))
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var dependency in function.Dependencies)
|
|
|
+ {
|
|
|
+ Require(dependency);
|
|
|
+ }
|
|
|
+
|
|
|
+ usedFunctions.Add(function);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Taken from here https://www.shadertoy.com/view/4dKcWK
|
|
|
+ private static readonly BuiltInFunction<Half3> HueToRgb = new(
|
|
|
+ "float hue",
|
|
|
+ nameof(HueToRgb),
|
|
|
+ """
|
|
|
+ half3 rgb = abs(hue * 6. - half3(3, 2, 4)) * half3(1, -1, -1) + half3(-1, 2, 2);
|
|
|
+ return clamp(rgb, 0., 1.);
|
|
|
+ """);
|
|
|
+
|
|
|
+ private static readonly BuiltInFunction<Half3> RgbToHcv = new(
|
|
|
+ "half3 rgba",
|
|
|
+ nameof(RgbToHcv),
|
|
|
+ """
|
|
|
+ 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) + q.z);
|
|
|
+ return half3(h, c, q.x);
|
|
|
+ """);
|
|
|
+
|
|
|
+ private static readonly BuiltInFunction<Half4> RgbToHsv = new(
|
|
|
+ "half4 rgba",
|
|
|
+ nameof(RgbToHsv),
|
|
|
+ $"""
|
|
|
+ half3 hcv = {RgbToHcv.Call("rgba.rgb")};
|
|
|
+ float s = hcv.y / (hcv.z);
|
|
|
+ return half4(hcv.x, s, hcv.z, rgba.w);
|
|
|
+ """,
|
|
|
+ RgbToHcv);
|
|
|
+
|
|
|
+ private static readonly BuiltInFunction<Half4> HsvToRgb = new(
|
|
|
+ "half4 hsva",
|
|
|
+ nameof(HsvToRgb),
|
|
|
+ $"""
|
|
|
+ half3 rgb = {HueToRgb.Call("hsva.r")};
|
|
|
+ return half4(((rgb - 1.) * hsva.y + 1.) * hsva.z, hsva.w);
|
|
|
+ """,
|
|
|
+ HueToRgb);
|
|
|
+
|
|
|
+ private static readonly BuiltInFunction<Half4> RgbToHsl = new(
|
|
|
+ "half4 rgba",
|
|
|
+ nameof(RgbToHsl),
|
|
|
+ $"""
|
|
|
+ half3 hcv = {RgbToHcv.Call("rgba.rgb")};
|
|
|
+ half z = hcv.z - hcv.y * 0.5;
|
|
|
+ half s = hcv.y / (1. - abs(z * 2. - 1.));
|
|
|
+ return half4(hcv.x, s, z, rgba.w);
|
|
|
+ """,
|
|
|
+ RgbToHcv);
|
|
|
+
|
|
|
+ private static readonly BuiltInFunction<Half4> HslToRgb = new(
|
|
|
+ "half4 hsla",
|
|
|
+ nameof(HslToRgb),
|
|
|
+ $"""
|
|
|
+ half3 rgb = {HueToRgb.Call("hsla.r")};
|
|
|
+ float c = (1. - abs(2. * hsla.z - 1.)) * hsla.y;
|
|
|
+ return half4((rgb - 0.5) * c + hsla.z, hsla.w);
|
|
|
+ """,
|
|
|
+ HueToRgb);
|
|
|
+
|
|
|
+ private class BuiltInFunction<TReturn>(string argumentList, string name, string body, params IBuiltInFunction[] dependencies) : IBuiltInFunction where TReturn : ShaderExpressionVariable
|
|
|
+ {
|
|
|
+ public string ArgumentList { get; } = argumentList;
|
|
|
+
|
|
|
+ public string Name { get; } = name;
|
|
|
+
|
|
|
+ public string Body { get; } = body;
|
|
|
+
|
|
|
+ public IBuiltInFunction[] Dependencies { get; } = dependencies;
|
|
|
+
|
|
|
+ public string FullSource =>
|
|
|
+ $$"""
|
|
|
+ {{typeof(TReturn).Name.ToLower()}} {{Name}}({{ArgumentList}}) {
|
|
|
+ {{Body}}
|
|
|
+ }
|
|
|
+ """;
|
|
|
+
|
|
|
+ public string Call(string arguments) => $"{Name}({arguments})";
|
|
|
+ }
|
|
|
+
|
|
|
+ private interface IBuiltInFunction
|
|
|
+ {
|
|
|
+ IBuiltInFunction[] Dependencies { get; }
|
|
|
+
|
|
|
+ string Name { get; }
|
|
|
+
|
|
|
+ string FullSource { get; }
|
|
|
+
|
|
|
+ string Call(string arguments);
|
|
|
+ }
|
|
|
+}
|