Fix ColorExtensions math (#41717)

All of this was doing sRGB -> OkLAB conversions without linearizing the sRGB first, so it was broken. I could have sworn I pointed this out in review but I guess that got lost.

Also, add a gamut clipping step since we have out-of-gamut colors and I don't want random negative values causing weird nightmare bugs somewhere. Shouldn't change anything in regular rendering.
This commit is contained in:
Pieter-Jan Briers 2025-12-15 03:07:32 +01:00 committed by BarryNorfolk
parent 091b7d98b6
commit 1c4806740c
1 changed files with 42 additions and 9 deletions

View File

@ -14,10 +14,10 @@ public static class ColorExtensions
{
DebugTools.Assert(lightness is >= 0.0f and <= 1.0f);
var oklab = Color.ToLab(c);
var oklab = c.LabFromSrgb();
oklab.X = lightness;
return Color.FromLab(oklab);
return oklab.LabToSrgb();
}
/// <summary>
@ -25,10 +25,10 @@ public static class ColorExtensions
/// </summary>
public static Color NudgeLightness(this Color c, float lightnessShift)
{
var oklab = Color.ToLab(c);
var oklab = c.LabFromSrgb();
oklab.X = Math.Clamp(oklab.X + lightnessShift, 0, 1);
return Color.FromLab(oklab);
return oklab.LabToSrgb();
}
/// <summary>
@ -39,12 +39,12 @@ public static class ColorExtensions
/// </remarks>
public static Color NudgeChroma(this Color c, float chromaShift)
{
var oklab = Color.ToLab(c);
var oklab = c.LabFromSrgb();
var oklch = Color.ToLch(oklab);
oklch.Y = Math.Clamp(oklch.Y + chromaShift, 0, 1);
return Color.FromLab(Color.FromLch(oklch));
return Color.FromLch(oklch).LabToSrgb();
}
/// <summary>
@ -54,10 +54,43 @@ public static class ColorExtensions
{
DebugTools.Assert(factor is >= 0.0f and <= 1.0f);
var okFrom = Color.ToLab(from);
var okTo = Color.ToLab(to);
var okFrom = from.LabFromSrgb();
var okTo = to.LabFromSrgb();
var blended = Vector4.Lerp(okFrom, okTo, factor);
return Color.FromLab(blended);
return blended.LabToSrgb();
}
/// <summary>
/// Converts a nonlinear sRGB ("normal") color to OkLAB.
/// </summary>
public static Vector4 LabFromSrgb(this Color from)
{
return Color.ToLab(Color.FromSrgb(from));
}
/// <summary>
/// Converts OkLAB to a nonlinear sRGB ("normal") color.
/// </summary>
public static Color LabToSrgb(this Vector4 from)
{
return Color.ToSrgb(Color.FromLab(from).SimpleClipGamut());
}
/// <summary>
/// Clips the gamut of the color so that all color channels are in the range 0 -> 1.
/// </summary>
/// <remarks>
/// This uses no clever perceptual techniques, it literally just clamps the individual channels.
/// </remarks>
public static Color SimpleClipGamut(this Color from)
{
return new Color
{
R = Math.Clamp(from.R, 0, 1),
G = Math.Clamp(from.G, 0, 1),
B = Math.Clamp(from.B, 0, 1),
A = from.A,
};
}
}