108 lines
3.9 KiB
C#
108 lines
3.9 KiB
C#
using System;
|
|
using T3.Core.Logging;
|
|
|
|
namespace T3.Core.Utils;
|
|
|
|
/**
|
|
* Helper methods to deal with OkLab color space. This can be useful for nicer blending of gradients
|
|
*/
|
|
public static class OkLab
|
|
{
|
|
// From Linear to oklab
|
|
public static Vector4 RgbAToOkLab(Vector4 c)
|
|
{
|
|
double cr = c.X;
|
|
double cg = c.Y;
|
|
double cb = c.Z;
|
|
|
|
double l = 0.4122214708d * cr + 0.5363325363d * cg + 0.0514459929d * cb;
|
|
double m = 0.2119034982d * cr + 0.6806995451d * cg + 0.1073969566d * cb;
|
|
double s = 0.0883024619d * cr + 0.2817188376d * cg + 0.6299787005d * cb;
|
|
|
|
double lCbrt = Math.Pow (l, 1.0d / 3.0d);
|
|
double mCbrt = Math.Pow (m, 1.0d / 3.0d);
|
|
double sCbrt = Math.Pow (s, 1.0d / 3.0d);
|
|
|
|
return new Vector4(
|
|
(float)(0.2104542553d * lCbrt + 0.793617785d * mCbrt - 0.0040720468d * sCbrt),
|
|
(float)(1.9779984951d * lCbrt - 2.428592205d * mCbrt + 0.4505937099d * sCbrt),
|
|
(float)(0.0259040371d * lCbrt + 0.7827717662d * mCbrt - 0.808675766d * sCbrt),
|
|
c.W);
|
|
}
|
|
|
|
// From OKLab to Linear sRGB
|
|
public static Vector4 OkLabToRgba(Vector4 c)
|
|
{
|
|
var l1 = c.X + 0.3963377774f * c.Y + 0.2158037573f * c.Z;
|
|
var m1 = c.X - 0.1055613458f * c.Y - 0.0638541728f * c.Z;
|
|
var s1 = c.X - 0.0894841775f * c.Y - 1.2914855480f * c.Z;
|
|
|
|
var l = l1 * l1 * l1;
|
|
var m = m1 * m1 * m1;
|
|
var s = s1 * s1 * s1;
|
|
|
|
return new Vector4(
|
|
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
|
|
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
|
|
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
|
|
c.W
|
|
);
|
|
}
|
|
|
|
public static Vector4 Degamma(Vector4 c)
|
|
{
|
|
const float gamma = 2.2f;
|
|
return new Vector4(
|
|
MathF.Pow(c.X, gamma),
|
|
MathF.Pow(c.Y, gamma),
|
|
MathF.Pow(c.Z, gamma),
|
|
c.W
|
|
);
|
|
}
|
|
|
|
public static Vector4 ToGamma(Vector4 c)
|
|
{
|
|
const float gamma = 2.2f;
|
|
return new Vector4(
|
|
MathF.Pow(c.X, 1.0f/gamma),
|
|
MathF.Pow(c.Y, 1.0f/gamma),
|
|
MathF.Pow(c.Z, 1.0f/gamma),
|
|
c.W
|
|
);
|
|
}
|
|
|
|
public static Vector4 Mix(Vector4 c1, Vector4 c2, float t)
|
|
{
|
|
var c1Linear = Degamma( Vector4.Max(c1, Vector4.Zero));
|
|
var c2Linear = Degamma(Vector4.Max(c2, Vector4.Zero));
|
|
|
|
var labMix= MathUtils.Lerp( RgbAToOkLab(c1Linear), RgbAToOkLab(c2Linear), t);
|
|
return ToGamma(OkLab.OkLabToRgba(labMix));
|
|
}
|
|
|
|
|
|
|
|
public static Vector4 FromOkLab(float L, float a, float b, float alpha = 1f, bool gamma = true)
|
|
{
|
|
var hdrExcess = MathF.Max(0f,L - 1f);
|
|
var linear = OkLabToRgba(new Vector4(L.Clamp(0,1), a, b, alpha));
|
|
var clampedLinear = Vector4.Clamp(linear, Vector4.Zero, Vector4.One);
|
|
var srgb = ToGamma(clampedLinear);
|
|
if(hdrExcess <= 0)
|
|
return srgb;
|
|
|
|
return new Vector4(srgb.X * (1 + hdrExcess),
|
|
srgb.Y * (1 + hdrExcess),
|
|
srgb.Z * (1 + hdrExcess),
|
|
srgb.W
|
|
);
|
|
}
|
|
|
|
public static Vector4 FromOkLCh(float L, float C, float hDegrees, float alpha = 1f, bool gamma = true)
|
|
{
|
|
float h = hDegrees * (MathF.PI / 180f);
|
|
float a = C * MathF.Cos(h);
|
|
float b = C * MathF.Sin(h);
|
|
return FromOkLab(L, a, b, alpha, gamma);
|
|
}
|
|
} |