diff --git a/RGB.NET.Core/Color/HclColor.cs b/RGB.NET.Core/Color/HclColor.cs
new file mode 100644
index 0000000..ee05be7
--- /dev/null
+++ b/RGB.NET.Core/Color/HclColor.cs
@@ -0,0 +1,203 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedMember.Global
+using System;
+
+namespace RGB.NET.Core
+{
+ public static class HclColor
+ {
+ #region Getter
+
+ ///
+ /// Gets the H component value (Hcl-color space) of this in the range [0..360].
+ ///
+ ///
+ ///
+ public static float GetHclH(this in Color color) => color.GetHcl().l;
+
+ ///
+ /// Gets the c component value (Hcl-color space) of this in the range [0..1].
+ ///
+ ///
+ ///
+ public static float GetHclC(this in Color color) => color.GetHcl().c;
+
+ ///
+ /// Gets the l component value (Hcl-color space) of this in the range [0..1].
+ ///
+ ///
+ ///
+ public static float GetHclL(this in Color color) => color.GetHcl().h;
+
+ ///
+ /// Gets the H, c and l component values (Hcl-color space) of this .
+ /// H in the range [0..360].
+ /// c in the range [0..1].
+ /// l in the range [0..1].
+ ///
+ ///
+ ///
+ public static (float h, float c, float l) GetHcl(this in Color color)
+ => CalculateHclFromRGB(color.R, color.G, color.B);
+
+ #endregion
+
+ #region Manipulation
+
+ ///
+ /// Adds the given Hcl values to this color.
+ ///
+ /// The H value to add.
+ /// The c value to add.
+ /// The l value to add.
+ /// The new color after the modification.
+ public static Color AddHcl(this in Color color, float h = 0, float c = 0, float l = 0)
+ {
+ (float cH, float cC, float cL) = color.GetHcl();
+ return Create(color.A, cH + h, cC + c, cL + l);
+ }
+
+ ///
+ /// Subtracts the given Hcl values to this color.
+ ///
+ /// The H value to subtract.
+ /// The c value to subtract.
+ /// The l value to subtract.
+ /// The new color after the modification.
+ public static Color SubtractHcl(this in Color color, float h = 0, float c = 0, float l = 0)
+ {
+ (float cH, float cC, float cL) = color.GetHcl();
+ return Create(color.A, cH - h, cC - c, cL - l);
+ }
+
+ ///
+ /// Multiplies the given Hcl values to this color.
+ ///
+ /// The H value to multiply.
+ /// The c value to multiply.
+ /// The l value to multiply.
+ /// The new color after the modification.
+ public static Color MultiplyHcl(this in Color color, float h = 1, float c = 1, float l = 1)
+ {
+ (float cH, float cC, float cL) = color.GetHcl();
+ return Create(color.A, cH * h, cC * c, cL * l);
+ }
+
+ ///
+ /// Divides the given Hcl values to this color.
+ ///
+ /// The H value to divide.
+ /// The c value to divide.
+ /// The l value to divide.
+ /// The new color after the modification.
+ public static Color DivideHcl(this in Color color, float h = 1, float c = 1, float l = 1)
+ {
+ (float cH, float cC, float cL) = color.GetHcl();
+ return Create(color.A, cH / h, cC / c, cL / l);
+ }
+
+ ///
+ /// Sets the given X value of this color.
+ ///
+ /// The H value to set.
+ /// The c value to set.
+ /// The l value to set.
+ /// The new color after the modification.
+ public static Color SetHcl(this in Color color, float? h = null, float? c = null, float? l = null)
+ {
+ (float cH, float cC, float cL) = color.GetHcl();
+ return Create(color.A, h ?? cH, c ?? cC, l ?? cL);
+ }
+
+ #endregion
+
+ #region Factory
+
+ ///
+ /// Creates a new instance of the struct using Hcl-Values.
+ ///
+ /// The H component value of this .
+ /// The c component value of this .
+ /// The l component value of this .
+ /// The color created from the values.
+ public static Color Create(float h, float c, float l)
+ => Create(1.0f, h, c, l);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Hcl-Values.
+ ///
+ /// The alphc component value of this .
+ /// The H component value of this .
+ /// The c component value of this .
+ /// The l component value of this .
+ /// The color created from the values.
+ public static Color Create(byte alpha, float h, float c, float l)
+ => Create((float)alpha / byte.MaxValue, h, c, l);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Hcl-Values.
+ ///
+ /// The alphc component value of this .
+ /// The H component value of this .
+ /// The c component value of this .
+ /// The l component value of this .
+ /// The color created from the values.
+ public static Color Create(int alpha, float h, float c, float l)
+ => Create((float)alpha / byte.MaxValue, h, c, l);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Hcl-Values.
+ ///
+ /// The alphc component value of this .
+ /// The H component value of this .
+ /// The c component value of this .
+ /// The l component value of this .
+ /// The color created from the values.
+ public static Color Create(float alpha, float h, float c, float l)
+ {
+ (float r, float g, float _b) = CalculateRGBFromHcl(h, c, l);
+ return new Color(alpha, r, g, _b);
+ }
+
+ #endregion
+
+ #region Helper
+
+ private static (float h, float c, float l) CalculateHclFromRGB(float r, float g, float b)
+ {
+ const float RADIANS_DEGREES_CONVERSION = 180.0f / MathF.PI;
+
+ (float l, float a, float _b) = LabColor.CalculateLabFromRGB(r, g, b);
+
+ float h, c;
+ if (r.EqualsInTolerance(g) && r.EqualsInTolerance(b)) //DarthAffe 26.02.2021: The cumulated rounding errors are big enough to cause problems in that case
+ {
+ h = 0;
+ c = 0;
+ }
+ else
+ {
+ h = MathF.Atan2(_b, a);
+ if (h >= 0) h *= RADIANS_DEGREES_CONVERSION;
+ else h = 360 - (-h * RADIANS_DEGREES_CONVERSION);
+
+ c = MathF.Sqrt((a * a) + (_b * _b));
+ }
+
+ return (h, c, l);
+ }
+
+ private static (float r, float g, float b) CalculateRGBFromHcl(float h, float c, float l)
+ {
+ const float DEGREES_RADIANS_CONVERSION = MathF.PI / 180.0f;
+
+ h *= DEGREES_RADIANS_CONVERSION;
+ float a = c * MathF.Cos(h);
+ float b = c * MathF.Sign(h);
+
+ return LabColor.CalculateRGBFromLab(l, a, b);
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Color/LabColor.cs b/RGB.NET.Core/Color/LabColor.cs
new file mode 100644
index 0000000..6b2b17b
--- /dev/null
+++ b/RGB.NET.Core/Color/LabColor.cs
@@ -0,0 +1,228 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedMember.Global
+using System;
+
+namespace RGB.NET.Core
+{
+ public static class LabColor
+ {
+ #region Getter
+
+ ///
+ /// Gets the L component value (Lab-color space) of this in the range [0..100].
+ ///
+ ///
+ ///
+ public static float GetLabL(this in Color color) => color.GetLab().l;
+
+ ///
+ /// Gets the a component value (Lab-color space) of this in the range [0..1].
+ ///
+ ///
+ ///
+ public static float GetLabA(this in Color color) => color.GetLab().a;
+
+ ///
+ /// Gets the b component value (Lab-color space) of this in the range [0..1].
+ ///
+ ///
+ ///
+ public static float GetLabB(this in Color color) => color.GetLab().b;
+
+ ///
+ /// Gets the L, a and b component values (Lab-color space) of this .
+ /// L in the range [0..100].
+ /// a in the range [0..1].
+ /// b in the range [0..1].
+ ///
+ ///
+ ///
+ public static (float l, float a, float b) GetLab(this in Color color)
+ => CalculateLabFromRGB(color.R, color.G, color.B);
+
+ #endregion
+
+ #region Manipulation
+
+ ///
+ /// Adds the given Lab values to this color.
+ ///
+ /// The L value to add.
+ /// The a value to add.
+ /// The b value to add.
+ /// The new color after the modification.
+ public static Color AddLab(this in Color color, float l = 0, float a = 0, float b = 0)
+ {
+ (float cL, float cA, float cB) = color.GetLab();
+ return Create(color.A, cL + l, cA + a, cB + b);
+ }
+
+ ///
+ /// Subtracts the given Lab values to this color.
+ ///
+ /// The L value to subtract.
+ /// The a value to subtract.
+ /// The b value to subtract.
+ /// The new color after the modification.
+ public static Color SubtractLab(this in Color color, float l = 0, float a = 0, float b = 0)
+ {
+ (float cL, float cA, float cB) = color.GetLab();
+ return Create(color.A, cL - l, cA - a, cB - b);
+ }
+
+ ///
+ /// Multiplies the given Lab values to this color.
+ ///
+ /// The L value to multiply.
+ /// The a value to multiply.
+ /// The b value to multiply.
+ /// The new color after the modification.
+ public static Color MultiplyLab(this in Color color, float l = 1, float a = 1, float b = 1)
+ {
+ (float cL, float cA, float cB) = color.GetLab();
+ return Create(color.A, cL * l, cA * a, cB * b);
+ }
+
+ ///
+ /// Divides the given Lab values to this color.
+ ///
+ /// The L value to divide.
+ /// The a value to divide.
+ /// The b value to divide.
+ /// The new color after the modification.
+ public static Color DivideLab(this in Color color, float l = 1, float a = 1, float b = 1)
+ {
+ (float cL, float cA, float cB) = color.GetLab();
+ return Create(color.A, cL / l, cA / a, cB / b);
+ }
+
+ ///
+ /// Sets the given X valueof this color.
+ ///
+ /// The L value to set.
+ /// The a value to set.
+ /// The b value to set.
+ /// The new color after the modification.
+ public static Color SetLab(this in Color color, float? l = null, float? a = null, float? b = null)
+ {
+ (float cL, float cA, float cB) = color.GetLab();
+ return Create(color.A, l ?? cL, a ?? cA, b ?? cB);
+ }
+
+ #endregion
+
+ #region Factory
+
+ ///
+ /// Creates a new instance of the struct using Lab-Values.
+ ///
+ /// The L component value of this .
+ /// The a component value of this .
+ /// The b component value of this .
+ /// The color created from the values.
+ public static Color Create(float l, float a, float b)
+ => Create(1.0f, l, a, b);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Lab-Values.
+ ///
+ /// The alpha component value of this .
+ /// The L component value of this .
+ /// The a component value of this .
+ /// The b component value of this .
+ /// The color created from the values.
+ public static Color Create(byte alpha, float l, float a, float b)
+ => Create((float)alpha / byte.MaxValue, l, a, b);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Lab-Values.
+ ///
+ /// The alpha component value of this .
+ /// The L component value of this .
+ /// The a component value of this .
+ /// The b component value of this .
+ /// The color created from the values.
+ public static Color Create(int alpha, float l, float a, float b)
+ => Create((float)alpha / byte.MaxValue, l, a, b);
+
+ ///
+ /// Creates a new instance of the struct using alpha and Lab-Values.
+ ///
+ /// The alpha component value of this .
+ /// The L component value of this .
+ /// The a component value of this .
+ /// The b component value of this .
+ /// The color created from the values.
+ public static Color Create(float alpha, float l, float a, float b)
+ {
+ (float r, float g, float _b) = CalculateRGBFromLab(l, a, b);
+ return new Color(alpha, r, g, _b);
+ }
+
+ #endregion
+
+ #region Helper
+
+ internal static (float l, float a, float b) CalculateLabFromRGB(float r, float g, float b)
+ {
+ (float x, float y, float z) = XYZColor.CaclulateXYZFromRGB(r, g, b);
+ return CaclulateLabFromXYZ(x, y, z);
+
+ //const float LAB_MULTIPLIER = (1f / 127f) * 0.5f;
+
+ //(float x, float y, float z) = XYZColor.CaclulateXYZFromRGB(r, g, b);
+ //(float l, float a, float _b) = CaclulateLabFromXYZ(x, y, z);
+ //return (l / 100.0f, 0.5f + (a * LAB_MULTIPLIER), 0.5f + (_b * LAB_MULTIPLIER));
+ }
+
+ internal static (float r, float g, float b) CalculateRGBFromLab(float l, float a, float b)
+ {
+ (float x, float y, float z) = CalculateXYZFromLab(l, a, b);
+ return XYZColor.CalculateRGBFromXYZ(x, y, z);
+
+ //(float x, float y, float z) = CalculateXYZFromLab(100.0f * l, 254.0f * (a - 0.5f), 2.0f * 254.0f * (b - 0.5f));
+ //return XYZColor.CalculateRGBFromXYZ(x, y, z);
+ }
+
+ private static (float l, float a, float b) CaclulateLabFromXYZ(float x, float y, float z)
+ {
+ const float ONETHRID = 1.0f / 3.0f;
+ const float FACTOR2 = 16.0f / 116.0f;
+
+ x /= 95.047f;
+ y /= 100.0f;
+ z /= 108.883f;
+
+ x = ((x > 0.008856f) ? (MathF.Pow(x, ONETHRID)) : ((7.787f * x) + FACTOR2));
+ y = ((y > 0.008856f) ? (MathF.Pow(y, ONETHRID)) : ((7.787f * y) + FACTOR2));
+ z = ((z > 0.008856f) ? (MathF.Pow(z, ONETHRID)) : ((7.787f * z) + FACTOR2));
+
+ float l = (116.0f * y) - 16.0f;
+ float a = 500.0f * (x - y);
+ float b = 200.0f * (y - z);
+
+ return (l, a, b);
+ }
+
+ private static (float x, float y, float z) CalculateXYZFromLab(float l, float a, float b)
+ {
+ const float FACTOR2 = 16.0f / 116.0f;
+
+ float y = (l + 16.0f) / 116.0f;
+ float x = (a / 500.0f) + y;
+ float z = y - (b / 200.0f);
+
+ float powX = MathF.Pow(y, 3.0f);
+ float powY = MathF.Pow(y, 3.0f);
+ float powZ = MathF.Pow(y, 3.0f);
+
+ y = ((powY > 0.008856f) ? (powY) : ((y - FACTOR2) / 7.787f));
+ x = ((powX > 0.008856f) ? (powX) : ((x - FACTOR2) / 7.787f));
+ z = ((powZ > 0.008856f) ? (powZ) : ((z - FACTOR2) / 7.787f));
+
+ return (95.047f * x, 100.0f * y, 108.883f * z);
+ }
+
+ #endregion
+ }
+}
diff --git a/RGB.NET.Core/Color/XYZColor.cs b/RGB.NET.Core/Color/XYZColor.cs
new file mode 100644
index 0000000..d02329e
--- /dev/null
+++ b/RGB.NET.Core/Color/XYZColor.cs
@@ -0,0 +1,200 @@
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedMember.Global
+using System;
+
+namespace RGB.NET.Core
+{
+ public static class XYZColor
+ {
+ #region Getter
+
+ ///
+ /// Gets the X component value (XYZ-color space) of this in the range [0..95.047].
+ ///
+ ///
+ ///
+ public static float GetX(this in Color color) => color.GetXYZ().x;
+
+ ///
+ /// Gets the Y component value (XYZ-color space) of this in the range [0..100].
+ ///
+ ///
+ ///
+ public static float GetY(this in Color color) => color.GetXYZ().y;
+
+ ///
+ /// Gets the Z component value (XYZ-color space) of this in the range [0..108.883].
+ ///
+ ///
+ ///
+ public static float GetZ(this in Color color) => color.GetXYZ().z;
+
+ ///
+ /// Gets the X, Y and Z component values (XYZ-color space) of this .
+ /// X in the range [0..95.047].
+ /// Y in the range [0..100].
+ /// Z in the range [0..108.883].
+ ///
+ ///
+ ///
+ public static (float x, float y, float z) GetXYZ(this in Color color)
+ => CaclulateXYZFromRGB(color.R, color.G, color.B);
+
+ #endregion
+
+ #region Manipulation
+
+ ///
+ /// Adds the given XYZ values to this color.
+ ///
+ /// The X value to add.
+ /// The Y value to add.
+ /// The Z value to add.
+ /// The new color after the modification.
+ public static Color AddXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
+ {
+ (float cX, float cY, float cZ) = color.GetXYZ();
+ return Create(color.A, cX + x, cY + y, cZ + z);
+ }
+
+ ///
+ /// Subtracts the given XYZ values to this color.
+ ///
+ /// The X value to subtract.
+ /// The Y value to subtract.
+ /// The Z value to subtract.
+ /// The new color after the modification.
+ public static Color SubtractXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
+ {
+ (float cX, float cY, float cZ) = color.GetXYZ();
+ return Create(color.A, cX - x, cY - y, cZ - z);
+ }
+
+ ///
+ /// Multiplies the given XYZ values to this color.
+ ///
+ /// The X value to multiply.
+ /// The Y value to multiply.
+ /// The Z value to multiply.
+ /// The new color after the modification.
+ public static Color MultiplyXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
+ {
+ (float cX, float cY, float cZ) = color.GetXYZ();
+ return Create(color.A, cX * x, cY * y, cZ * z);
+ }
+
+ ///
+ /// Divides the given XYZ values to this color.
+ ///
+ /// The X value to divide.
+ /// The Y value to divide.
+ /// The Z value to divide.
+ /// The new color after the modification.
+ public static Color DivideXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
+ {
+ (float cX, float cY, float cZ) = color.GetXYZ();
+ return Create(color.A, cX / x, cY / y, cZ / z);
+ }
+
+ ///
+ /// Sets the given X valueof this color.
+ ///
+ /// The X value to set.
+ /// The Y value to set.
+ /// The Z value to set.
+ /// The new color after the modification.
+ public static Color SetXYZ(this in Color color, float? x = null, float? y = null, float? value = null)
+ {
+ (float cX, float cY, float cZ) = color.GetXYZ();
+ return Create(color.A, x ?? cX, y ?? cY, value ?? cZ);
+ }
+
+ #endregion
+
+ #region Factory
+
+ ///
+ /// Creates a new instance of the struct using XYZ-Values.
+ ///
+ /// The X component value of this .
+ /// The Y component value of this .
+ /// The Z component value of this .
+ /// The color created from the values.
+ public static Color Create(float x, float y, float z)
+ => Create(1.0f, x, y, z);
+
+ ///
+ /// Creates a new instance of the struct using alpha and XYZ-Values.
+ ///
+ /// The alpha component value of this .
+ /// The X component value of this .
+ /// The Y component value of this .
+ /// The Z component value of this .
+ /// The color created from the values.
+ public static Color Create(byte a, float x, float y, float z)
+ => Create((float)a / byte.MaxValue, x, y, z);
+
+ ///
+ /// Creates a new instance of the struct using alpha and XYZ-Values.
+ ///
+ /// The alpha component value of this .
+ /// The X component value of this .
+ /// The Y component value of this .
+ /// The Z component value of this .
+ /// The color created from the values.
+ public static Color Create(int a, float x, float y, float z)
+ => Create((float)a / byte.MaxValue, x, y, z);
+
+ ///
+ /// Creates a new instance of the struct using alpha and XYZ-Values.
+ ///
+ /// The alpha component value of this .
+ /// The X component value of this .
+ /// The Y component value of this .
+ /// The Z component value of this .
+ /// The color created from the values.
+ public static Color Create(float a, float x, float y, float z)
+ {
+ (float r, float g, float b) = CalculateRGBFromXYZ(x, y, z);
+ return new Color(a, r, g, b);
+ }
+
+ #endregion
+
+ #region Helper
+
+ internal static (float x, float y, float z) CaclulateXYZFromRGB(float r, float g, float b)
+ {
+ r = ((r > 0.04045f) ? MathF.Pow(((r + 0.055f) / 1.055f), 2.4f) : (r / 12.92f)) * 100.0f;
+ g = ((g > 0.04045f) ? MathF.Pow(((g + 0.055f) / 1.055f), 2.4f) : (g / 12.92f)) * 100.0f;
+ b = ((b > 0.04045f) ? MathF.Pow(((b + 0.055f) / 1.055f), 2.4f) : (b / 12.92f)) * 100.0f;
+
+ float x = (r * 0.4124f) + (g * 0.3576f) + (b * 0.1805f);
+ float y = (r * 0.2126f) + (g * 0.7152f) + (b * 0.0722f);
+ float z = (r * 0.0193f) + (g * 0.1192f) + (b * 0.9505f);
+
+ return (x, y, z);
+ }
+
+ internal static (float r, float g, float b) CalculateRGBFromXYZ(float x, float y, float z)
+ {
+ const float INVERSE_EXPONENT = 1.0f / 2.4f;
+
+ x /= 100.0f;
+ y /= 100.0f;
+ z /= 100.0f;
+
+ float r = (x * 3.2406f) + (y * -1.5372f) + (z * -0.4986f);
+ float g = (x * -0.9689f) + (y * 1.8758f) + (z * 0.0415f);
+ float b = (x * 0.0557f) + (y * -0.2040f) + (z * 1.0570f);
+
+ r = ((r > 0.0031308f) ? ((1.055f * (MathF.Pow(r, INVERSE_EXPONENT))) - 0.055f) : (12.92f * r));
+ g = ((g > 0.0031308f) ? ((1.055f * (MathF.Pow(g, INVERSE_EXPONENT))) - 0.055f) : (12.92f * g));
+ b = ((b > 0.0031308f) ? ((1.055f * (MathF.Pow(b, INVERSE_EXPONENT))) - 0.055f) : (12.92f * b));
+
+ return (r, g, b);
+ }
+
+ #endregion
+ }
+}