diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs
new file mode 100644
index 0000000000..cf6dbd363b
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/HunterLab.cs
@@ -0,0 +1,202 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+///
+/// Represents an Hunter LAB color.
+/// .
+///
+public readonly struct HunterLab : IColorProfile
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The lightness dimension.
+ /// The a (green - magenta) component.
+ /// The b (blue - yellow) component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public HunterLab(float l, float a, float b)
+ : this(new Vector3(l, a, b))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the l a b components.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public HunterLab(Vector3 vector)
+ {
+ // Not clamping as documentation about this space only indicates "usual" ranges
+ this.L = vector.X;
+ this.A = vector.Y;
+ this.B = vector.Z;
+ }
+
+ ///
+ /// Gets the lightness dimension.
+ /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).
+ ///
+ public readonly float L { get; }
+
+ ///
+ /// Gets the a color component.
+ /// A value usually ranging from -100 to 100. Negative is green, positive magenta.
+ ///
+ public readonly float A { get; }
+
+ ///
+ /// Gets the b color component.
+ /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow
+ ///
+ public readonly float B { get; }
+
+ ///
+ /// Gets the reference white point of this color.
+ ///
+ public readonly CieXyz WhitePoint { get; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right);
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
+
+ ///
+ public static HunterLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
+ {
+ // Conversion algorithm described here:
+ // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
+ CieXyz whitePoint = options.TargetWhitePoint;
+ float x = source.X, y = source.Y, z = source.Z;
+ float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z;
+
+ float ka = ComputeKa(in whitePoint);
+ float kb = ComputeKb(in whitePoint);
+
+ float yByYn = y / yn;
+ float sqrtYbyYn = MathF.Sqrt(yByYn);
+ float l = 100 * sqrtYbyYn;
+ float a = ka * (((x / xn) - yByYn) / sqrtYbyYn);
+ float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn);
+
+ if (float.IsNaN(a))
+ {
+ a = 0;
+ }
+
+ if (float.IsNaN(b))
+ {
+ b = 0;
+ }
+
+ return new HunterLab(l, a, b);
+ }
+
+ ///
+ public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ CieXyz xyz = source[i];
+ destination[i] = FromProfileConnectingSpace(options, in xyz);
+ }
+ }
+
+ ///
+ public CieXyz ToProfileConnectingSpace(ColorConversionOptions options)
+ {
+ // Conversion algorithm described here:
+ // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
+ CieXyz whitePoint = options.WhitePoint;
+ float l = this.L, a = this.A, b = this.B;
+ float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z;
+
+ float ka = ComputeKa(whitePoint);
+ float kb = ComputeKb(whitePoint);
+
+ float pow = Numerics.Pow2(l / 100F);
+ float sqrtPow = MathF.Sqrt(pow);
+ float y = pow * yn;
+
+ float x = (((a / ka) * sqrtPow) + pow) * xn;
+ float z = (((b / kb) * sqrtPow) - pow) * (-zn);
+
+ return new CieXyz(x, y, z);
+ }
+
+ ///
+ public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ HunterLab lab = source[i];
+ destination[i] = lab.ToProfileConnectingSpace(options);
+ }
+ }
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource()
+ => ChromaticAdaptionWhitePointSource.WhitePoint;
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
+
+ ///
+ public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
+
+ ///
+ public override bool Equals(object? obj) => obj is HunterLab other && this.Equals(other);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(HunterLab other)
+ => this.L.Equals(other.L)
+ && this.A.Equals(other.A)
+ && this.B.Equals(other.B)
+ && this.WhitePoint.Equals(other.WhitePoint);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float ComputeKa(in CieXyz whitePoint)
+ {
+ if (whitePoint.Equals(Illuminants.C))
+ {
+ return 175F;
+ }
+
+ return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float ComputeKb(in CieXyz whitePoint)
+ {
+ if (whitePoint == Illuminants.C)
+ {
+ return 70F;
+ }
+
+ return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z);
+ }
+}