diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2021-07-05 11:26:01 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2021-07-05 11:26:01 +0300 |
| commit | 3ac0c04e830c7199039979bff44d96a88cd9cc51 (patch) | |
| tree | 018c60565e3414d87bc8309b3382d85b6eb5a076 /Software/Visual_Studio | |
| parent | 22dd2a3dea15628a9242cb585d615b9b2e4e5422 (diff) | |
| download | Tango-3ac0c04e830c7199039979bff44d96a88cd9cc51.tar.gz Tango-3ac0c04e830c7199039979bff44d96a88cd9cc51.zip | |
Fixed RGB to LAB conversion using Colorful.
Diffstat (limited to 'Software/Visual_Studio')
76 files changed, 5993 insertions, 10 deletions
diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/HunterLabColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/HunterLabColor.cs new file mode 100644 index 000000000..259633805 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/HunterLabColor.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// Hunter Lab color + /// </summary> + public readonly struct HunterLabColor : IColorVector + { + /// <summary> + /// C standard illuminant. + /// Used when reference white is not specified explicitly. + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.C; + + #region Constructor + + /// <param name="l">L (lightness) (from 0 to 100)</param> + /// <param name="a">a (usually from -100 to 100)</param> + /// <param name="b">b (usually from -100 to 100)</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public HunterLabColor(double l, double a, double b) : this(l, a, b, DefaultWhitePoint) + { + } + + /// <param name="l">L (lightness) (from 0 to 100)</param> + /// <param name="a">a (usually from -100 to 100)</param> + /// <param name="b">b (usually from -100 to 100)</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public HunterLabColor(double l, double a, double b, XYZColor whitePoint) + { + L = l; + this.a = a; + this.b = b; + _whitePoint = whitePoint; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public HunterLabColor(Vector vector) : this(vector, DefaultWhitePoint) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public HunterLabColor(Vector vector, XYZColor whitePoint) + : this(vector[0], vector[1], vector[2], whitePoint) + { + } + + #endregion + + #region Channels + + /// <summary> + /// L (lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double L { get; } + + /// <summary> + /// a + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// Negative values indicate green while positive values indicate magenta. + /// </remarks> + public double a { get; } + + /// <summary> + /// b + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// Negative values indicate blue and positive values indicate yellow. + /// </remarks> + public double b { get; } + + /// <remarks> + /// <see cref="Illuminants" /> + /// </remarks> + public XYZColor WhitePoint => _whitePoint ?? DefaultWhitePoint; + + private readonly XYZColor? _whitePoint; + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, a, b }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + public bool Equals(HunterLabColor other) => L.Equals(other.L) && a.Equals(other.a) && b.Equals(other.b); + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is HunterLabColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ a.GetHashCode(); + hashCode = (hashCode * 397) ^ b.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(HunterLabColor left, HunterLabColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(HunterLabColor left, HunterLabColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "HunterLab [L={0:0.##}, a={1:0.##}, b={2:0.##}]", L, a, b); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/IColorVector.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/IColorVector.cs new file mode 100644 index 000000000..2555db9b4 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/IColorVector.cs @@ -0,0 +1,15 @@ +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// Color represented as a vector in its color space + /// </summary> + public interface IColorVector + { + /// <summary> + /// Vector + /// </summary> + Vector Vector { get; } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/IRGBWorkingSpace.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/IRGBWorkingSpace.cs new file mode 100644 index 000000000..b53e2578b --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/IRGBWorkingSpace.cs @@ -0,0 +1,29 @@ +using Colourful.Implementation.RGB; + +namespace Colourful +{ + /// <summary> + /// RGB working color space + /// </summary> + public interface IRGBWorkingSpace + { + /// <summary> + /// Reference white of the color space + /// </summary> + XYZColor WhitePoint { get; } + + /// <summary> + /// Chromaticity coordinates of the primaries + /// </summary> + RGBPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// <summary> + /// The companding function associated with the RGB color system. + /// Used for conversion to XYZ and backwards. + /// See this for more information: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + /// </summary> + ICompanding Companding { get; } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/Illuminants.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/Illuminants.cs new file mode 100644 index 000000000..811bcfcb7 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/Illuminants.cs @@ -0,0 +1,70 @@ +namespace Colourful +{ + /// <summary> + /// Standard illuminants + /// </summary> + /// <remarks> + /// Coefficients taken from: + /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + /// <br /> + /// Descriptions taken from: + /// http://en.wikipedia.org/wiki/Standard_illuminant + /// </remarks> + public static class Illuminants + { + /// <summary> + /// Incandescent / Tungsten + /// </summary> + public static readonly XYZColor A = new XYZColor(1.09850, 1, 0.35585); + + /// <summary> + /// Direct sunlight at noon (obsolete) + /// </summary> + public static readonly XYZColor B = new XYZColor(0.99072, 1, 0.85223); + + /// <summary> + /// Average / North sky Daylight (obsolete) + /// </summary> + public static readonly XYZColor C = new XYZColor(0.98074, 1, 1.18232); + + /// <summary> + /// Horizon Light. ICC profile PCS + /// </summary> + public static readonly XYZColor D50 = new XYZColor(0.96422, 1, 0.82521); + + /// <summary> + /// Mid-morning / Mid-afternoon Daylight + /// </summary> + public static readonly XYZColor D55 = new XYZColor(0.95682, 1, 0.92149); + + /// <summary> + /// Noon Daylight: Television, sRGB color space + /// </summary> + public static readonly XYZColor D65 = new XYZColor(0.95047, 1, 1.08883); + + /// <summary> + /// North sky Daylight + /// </summary> + public static readonly XYZColor D75 = new XYZColor(0.94972, 1, 1.22638); + + /// <summary> + /// Equal energy + /// </summary> + public static readonly XYZColor E = new XYZColor(1, 1, 1); + + /// <summary> + /// Cool White Fluorescent + /// </summary> + public static readonly XYZColor F2 = new XYZColor(0.99186, 1, 0.67393); + + /// <summary> + /// D65 simulator, Daylight simulator + /// </summary> + public static readonly XYZColor F7 = new XYZColor(0.95041, 1, 1.08747); + + /// <summary> + /// Philips TL84, Ultralume 40 + /// </summary> + public static readonly XYZColor F11 = new XYZColor(1.00962, 1, 0.64350); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LChabColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LChabColor.cs new file mode 100644 index 000000000..141b9fef3 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LChabColor.cs @@ -0,0 +1,145 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE L*C*h°, cylindrical form of <see cref="LabColor">CIE L*a*b* (1976)</see> + /// </summary> + public readonly struct LChabColor : IColorVector + { + /// <summary> + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.D50; + + #region Constructor + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="c">C* (chroma) (from 0 to 100)</param> + /// <param name="h">h° (hue in degrees) (from 0 to 360)</param> + public LChabColor(double l, double c, double h) : this(l, c, h, DefaultWhitePoint) + { + } + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="c">C* (chroma) (from 0 to 100)</param> + /// <param name="h">h° (hue in degrees) (from 0 to 360)</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LChabColor(double l, double c, double h, XYZColor whitePoint) + { + L = l; + C = c; + this.h = h; + _whitePoint = whitePoint; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LChabColor(Vector vector) : this(vector, DefaultWhitePoint) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LChabColor(Vector vector, XYZColor whitePoint) + : this(vector[0], vector[1], vector[2], whitePoint) + { + } + + #endregion + + #region Channels + + /// <summary> + /// L* (lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double L { get; } + + /// <summary> + /// C* (chroma) + /// </summary> + /// <remarks> + /// Ranges usually from 0 to 100. + /// </remarks> + public double C { get; } + + /// <summary> + /// h° (hue in degrees) + /// </summary> + /// <remarks> + /// Ranges from 0 to 360. + /// </remarks> + public double h { get; } + + /// <remarks> + /// <see cref="Illuminants" /> + /// </remarks> + public XYZColor WhitePoint => _whitePoint ?? DefaultWhitePoint; + + private readonly XYZColor? _whitePoint; + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, C, h }; + + #endregion + + #region Saturation + + /// <summary> + /// Computes saturation of the color (chroma normalized by lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double Saturation => SaturationLChFormulas.GetSaturation(L, C); + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LChabColor other) => + L == other.L && + C == other.C && + h == other.h; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LChabColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ C.GetHashCode(); + hashCode = (hashCode * 397) ^ h.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LChabColor left, LChabColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LChabColor left, LChabColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "LChab [L={0:0.##}, C={1:0.##}, h={2:0.##}]", L, C, h); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LChuvColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LChuvColor.cs new file mode 100644 index 000000000..dd5823ef4 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LChuvColor.cs @@ -0,0 +1,154 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE L*C*h°, cylindrical form of <see cref="LuvColor">CIE L*u*v* (1976)</see> + /// </summary> + public readonly struct LChuvColor : IColorVector + { + /// <summary> + /// D65 standard illuminant. + /// Used when reference white is not specified explicitly. + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.D65; + + #region Constructor + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="c">C* (chroma) (from 0 to 100)</param> + /// <param name="h">h° (hue in degrees) (from 0 to 360)</param> + public LChuvColor(double l, double c, double h) : this(l, c, h, DefaultWhitePoint) + { + } + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="c">C* (chroma) (from 0 to 100)</param> + /// <param name="h">h° (hue in degrees) (from 0 to 360)</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LChuvColor(double l, double c, double h, XYZColor whitePoint) + { + L = l; + C = c; + this.h = h; + _whitePoint = whitePoint; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LChuvColor(Vector vector) : this(vector, DefaultWhitePoint) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LChuvColor(Vector vector, XYZColor whitePoint) + : this(vector[0], vector[1], vector[2], whitePoint) + { + } + + #endregion + + #region Channels + + /// <summary> + /// L* (lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double L { get; } + + /// <summary> + /// C* (chroma) + /// </summary> + /// <remarks> + /// Ranges usually from 0 to 100. + /// </remarks> + public double C { get; } + + /// <summary> + /// h° (hue in degrees) + /// </summary> + /// <remarks> + /// Ranges from 0 to 360. + /// </remarks> + public double h { get; } + + /// <remarks> + /// <see cref="Illuminants" /> + /// </remarks> + public XYZColor WhitePoint => _whitePoint ?? DefaultWhitePoint; + + private readonly XYZColor? _whitePoint; + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, C, h }; + + #endregion + + #region Saturation + + /// <summary> + /// Computes saturation of the color (chroma normalized by lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double Saturation => SaturationLChFormulas.GetSaturation(L, C); + + /// <summary> + /// Constructs the color using saturation instead of chromas + /// </summary> + public static LChuvColor FromSaturation(double lightness, double hue, double saturation) + { + var chroma = SaturationLChFormulas.GetChroma(saturation, lightness); + return new LChuvColor(lightness, chroma, hue); + } + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LChuvColor other) => + L == other.L && + C == other.C && + h == other.h; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LChuvColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ C.GetHashCode(); + hashCode = (hashCode * 397) ^ h.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LChuvColor left, LChuvColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LChuvColor left, LChuvColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "LChuv [L={0:0.##}, C={1:0.##}, h={2:0.##}]", L, C, h); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LMSColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LMSColor.cs new file mode 100644 index 000000000..a13b7651c --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LMSColor.cs @@ -0,0 +1,104 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// LMS color space represented by the response of the three types of cones of the human eye + /// </summary> + public readonly struct LMSColor : IColorVector + { + #region Constructor + + /// <param name="l">L (usually from -1 to 1)</param> + /// <param name="m">M (usually from -1 to 1)</param> + /// <param name="s">S (usually from -1 to 1)</param> + public LMSColor(double l, double m, double s) + { + L = l; + M = m; + S = s; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (usually from 0 to 1)</param> + public LMSColor(Vector vector) + : this(vector[0], vector[1], vector[2]) + { + } + + #endregion + + #region Channels + + /// <summary> + /// Long wavelengths (red) cone response (Rho) + /// </summary> + /// <remarks> + /// Ranges usually from -1 to 1. + /// </remarks> + public double L { get; } + + /// <summary> + /// Medium wavelengths (green) cone response (Gamma) + /// </summary> + /// <remarks> + /// Ranges usually from -1 to 1. + /// </remarks> + public double M { get; } + + /// <summary> + /// Short wavelengths (blue) cone response (Beta) + /// </summary> + /// <remarks> + /// Ranges usually from -1 to 1. + /// </remarks> + public double S { get; } + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, M, S }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LMSColor other) => + L == other.L && + M == other.M && + S == other.S; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LMSColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ M.GetHashCode(); + hashCode = (hashCode * 397) ^ S.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LMSColor left, LMSColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LMSColor left, LMSColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "LMS [L={0:0.##}, M={1:0.##}, S={2:0.##}]", L, M, S); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LabColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LabColor.cs new file mode 100644 index 000000000..638f0763d --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LabColor.cs @@ -0,0 +1,136 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE L*a*b* (1976) color + /// </summary> + public readonly struct LabColor : IColorVector + { + /// <summary> + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.D50; + + #region Constructor + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="a">a* (usually from -100 to 100)</param> + /// <param name="b">b* (usually from -100 to 100)</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LabColor(double l, double a, double b) : this(l, a, b, DefaultWhitePoint) + { + } + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="a">a* (usually from -100 to 100)</param> + /// <param name="b">b* (usually from -100 to 100)</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LabColor(double l, double a, double b, XYZColor whitePoint) + { + L = l; + this.a = a; + this.b = b; + _whitePoint = whitePoint; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LabColor(Vector vector) : this(vector, DefaultWhitePoint) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LabColor(Vector vector, XYZColor whitePoint) + : this(vector[0], vector[1], vector[2], whitePoint) + { + } + + #endregion + + #region Channels + + /// <summary> + /// L* (lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double L { get; } + + /// <summary> + /// a* + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// Negative values indicate green while positive values indicate magenta. + /// </remarks> + public double a { get; } + + /// <summary> + /// b* + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// Negative values indicate blue and positive values indicate yellow. + /// </remarks> + public double b { get; } + + /// <remarks> + /// <see cref="Illuminants" /> + /// </remarks> + public XYZColor WhitePoint => _whitePoint ?? DefaultWhitePoint; + + private readonly XYZColor? _whitePoint; + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, a, b }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LabColor other) => + L == other.L && + a == other.a && + b == other.b; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LabColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ a.GetHashCode(); + hashCode = (hashCode * 397) ^ b.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LabColor left, LabColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LabColor left, LabColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "Lab [L={0:0.##}, a={1:0.##}, b={2:0.##}]", L, a, b); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LinearRGBColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LinearRGBColor.cs new file mode 100644 index 000000000..bc85cb5f5 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LinearRGBColor.cs @@ -0,0 +1,168 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Colourful.Implementation; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// RGB color with specified <see cref="IRGBWorkingSpace">working space</see>, which has linear channels (not companded) + /// </summary> + public readonly struct LinearRGBColor : IColorVector + { + #region Other + + /// <summary> + /// sRGB color space. + /// Used when working space is not specified explicitly. + /// </summary> + public static readonly IRGBWorkingSpace DefaultWorkingSpace = RGBWorkingSpaces.sRGB; + + #endregion + + #region Constructor + + /// <param name="r">Red (from 0 to 1)</param> + /// <param name="g">Green (from 0 to 1)</param> + /// <param name="b">Blue (from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public LinearRGBColor(double r, double g, double b) + : this(r, g, b, DefaultWorkingSpace) + { + } + + /// <param name="r">Red (from 0 to 1)</param> + /// <param name="g">Green (from 0 to 1)</param> + /// <param name="b">Blue (from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public LinearRGBColor(double r, double g, double b, IRGBWorkingSpace workingSpace) + { + R = r.CheckRange(0, 1); + G = g.CheckRange(0, 1); + B = b.CheckRange(0, 1); + _workingSpace = workingSpace; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (range from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public LinearRGBColor(Vector vector) + : this(vector, DefaultWorkingSpace) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (range from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public LinearRGBColor(Vector vector, IRGBWorkingSpace workingSpace) + : this(vector[0], vector[1], vector[2], workingSpace) + { + } + + #endregion + + #region Channels + + /// <summary> + /// Red + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double R { get; } + + /// <summary> + /// Green + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double G { get; } + + /// <summary> + /// Blue + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double B { get; } + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { R, G, B }; + + #endregion + + #region Attributes + + /// <summary> + /// RGB color space + /// <seealso cref="RGBWorkingSpaces" /> + /// </summary> + public IRGBWorkingSpace WorkingSpace => _workingSpace ?? DefaultWorkingSpace; + + private readonly IRGBWorkingSpace _workingSpace; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LinearRGBColor other) => + R == other.R && + G == other.G && + B == other.B && + WorkingSpace.Equals(other.WorkingSpace); + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LinearRGBColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ WorkingSpace.GetHashCode(); + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LinearRGBColor left, LinearRGBColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LinearRGBColor left, LinearRGBColor right) => !Equals(left, right); + + #endregion + + #region Factory methods + + /// <summary> + /// Creates RGB color with all channels equal + /// </summary> + /// <param name="value">Grey value (from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public static LinearRGBColor FromGrey(double value, IRGBWorkingSpace workingSpace) => new LinearRGBColor(value, value, value, workingSpace); + + /// <summary> + /// Creates RGB color with all channels equal + /// </summary> + /// <param name="value">Grey value (from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public static LinearRGBColor FromGrey(double value) => FromGrey(value, DefaultWorkingSpace); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "LinearRGB [R={0:0.##}, G={1:0.##}, B={2:0.##}]", R, G, B); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/LuvColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/LuvColor.cs new file mode 100644 index 000000000..b32128032 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/LuvColor.cs @@ -0,0 +1,134 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE L*u*v* (1976) color + /// </summary> + public readonly struct LuvColor : IColorVector + { + /// <summary> + /// D65 standard illuminant. + /// Used when reference white is not specified explicitly. + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.D65; + + #region Constructor + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="u">u* (usually from -100 to 100)</param> + /// <param name="v">v* (usually from -100 to 100)</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LuvColor(double l, double u, double v) : this(l, u, v, DefaultWhitePoint) + { + } + + /// <param name="l">L* (lightness) (from 0 to 100)</param> + /// <param name="u">u* (usually from -100 to 100)</param> + /// <param name="v">v* (usually from -100 to 100)</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LuvColor(double l, double u, double v, XYZColor whitePoint) + { + L = l; + this.u = u; + this.v = v; + _whitePoint = whitePoint; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <remarks>Uses <see cref="DefaultWhitePoint" /> as white point.</remarks> + public LuvColor(Vector vector) : this(vector, DefaultWhitePoint) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions</param> + /// <param name="whitePoint">Reference white (see <see cref="Illuminants" />)</param> + public LuvColor(Vector vector, XYZColor whitePoint) + : this(vector[0], vector[1], vector[2], whitePoint) + { + } + + #endregion + + #region Channels + + /// <summary> + /// L* (lightness) + /// </summary> + /// <remarks> + /// Ranges from 0 to 100. + /// </remarks> + public double L { get; } + + /// <summary> + /// u* + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// </remarks> + public double u { get; } + + /// <summary> + /// v* + /// </summary> + /// <remarks> + /// Ranges usually from -100 to 100. + /// </remarks> + public double v { get; } + + /// <remarks> + /// <see cref="Illuminants" /> + /// </remarks> + public XYZColor WhitePoint => _whitePoint ?? DefaultWhitePoint; + + private readonly XYZColor? _whitePoint; + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { L, u, v }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(LuvColor other) => + L == other.L && + u == other.u && + v == other.v; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LuvColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = L.GetHashCode(); + hashCode = (hashCode * 397) ^ u.GetHashCode(); + hashCode = (hashCode * 397) ^ v.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(LuvColor left, LuvColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LuvColor left, LuvColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "Luv [L={0:0.##}, u={1:0.##}, v={2:0.##}]", L, u, v); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/MacbethColorChecker.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/MacbethColorChecker.cs new file mode 100644 index 000000000..d2e2ab316 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/MacbethColorChecker.cs @@ -0,0 +1,138 @@ +using ColorList = System.Collections.Generic.IReadOnlyList<Colourful.RGBColor>; + +namespace Colourful +{ + /// <summary> + /// Colors of the Macbeth ColorChecker + /// </summary> + /// <remarks> + /// Values obtained from: http://xritephoto.com/documents/literature/en/ColorData-1p_EN.pdf + /// </remarks> + public static class MacbethColorChecker + { + /// <summary> + /// Dark skin (color #1) + /// </summary> + public static readonly RGBColor DarkSkin = RGBColor.FromRGB8bit(115, 82, 68, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Light skin (color #2) + /// </summary> + public static readonly RGBColor LightSkin = RGBColor.FromRGB8bit(194, 150, 130, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Blue sky (color #3) + /// </summary> + public static readonly RGBColor BlueSky = RGBColor.FromRGB8bit(98, 122, 157, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Foliage (color #4) + /// </summary> + public static readonly RGBColor Foliage = RGBColor.FromRGB8bit(87, 108, 67, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Blue flower (color #5) + /// </summary> + public static readonly RGBColor BlueFlower = RGBColor.FromRGB8bit(133, 128, 177, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Bluish green (color #6) + /// </summary> + public static readonly RGBColor BluishGreen = RGBColor.FromRGB8bit(103, 189, 170, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Orange (color #7) + /// </summary> + public static readonly RGBColor Orange = RGBColor.FromRGB8bit(214, 126, 44, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Purplish blue (color #8) + /// </summary> + public static readonly RGBColor PurplishBlue = RGBColor.FromRGB8bit(80, 91, 166, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Moderate red (color #9) + /// </summary> + public static readonly RGBColor ModerateRed = RGBColor.FromRGB8bit(193, 90, 99, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Purple (color #10) + /// </summary> + public static readonly RGBColor Purple = RGBColor.FromRGB8bit(94, 60, 108, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Yellow green (color #11) + /// </summary> + public static readonly RGBColor YellowGreen = RGBColor.FromRGB8bit(157, 188, 64, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Orange Yellow (color #12) + /// </summary> + public static readonly RGBColor OrangeYellow = RGBColor.FromRGB8bit(224, 163, 46, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Blue (color #13) + /// </summary> + public static readonly RGBColor Blue = RGBColor.FromRGB8bit(56, 61, 150, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Green (color #14) + /// </summary> + public static readonly RGBColor Green = RGBColor.FromRGB8bit(70, 148, 73, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Red (color #15) + /// </summary> + public static readonly RGBColor Red = RGBColor.FromRGB8bit(175, 54, 60, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Yellow (color #16) + /// </summary> + public static readonly RGBColor Yellow = RGBColor.FromRGB8bit(231, 199, 31, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Magenta (color #17) + /// </summary> + public static readonly RGBColor Magenta = RGBColor.FromRGB8bit(187, 86, 149, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Cyan (color #18) + /// </summary> + public static readonly RGBColor Cyan = RGBColor.FromRGB8bit(8, 133, 161, RGBWorkingSpaces.sRGB); + + /// <summary> + /// White (color #19) + /// </summary> + public static readonly RGBColor White = RGBColor.FromRGB8bit(243, 243, 242, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Neutral 8 (color #20) + /// </summary> + public static readonly RGBColor Neutral8 = RGBColor.FromRGB8bit(200, 200, 200, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Neutral 6.5 (color #21) + /// </summary> + public static readonly RGBColor Neutral6p5 = RGBColor.FromRGB8bit(160, 160, 160, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Neutral 5 (color #22) + /// </summary> + public static readonly RGBColor Neutral5 = RGBColor.FromRGB8bit(122, 122, 121, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Neutral 3.5 (color #23) + /// </summary> + public static readonly RGBColor Neutral3p5 = RGBColor.FromRGB8bit(85, 85, 85, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Black (color #24) + /// </summary> + public static readonly RGBColor Black = RGBColor.FromRGB8bit(52, 52, 52, RGBWorkingSpaces.sRGB); + + /// <summary> + /// Array of 24 colors of the Macbeth ColorChecker + /// </summary> + public static readonly ColorList Colors = new[] { DarkSkin, LightSkin, BlueSky, Foliage, BlueFlower, BluishGreen, Orange, PurplishBlue, ModerateRed, Purple, YellowGreen, OrangeYellow, Blue, Green, Red, Yellow, Magenta, Cyan, White, Neutral8, Neutral6p5, Neutral5, Neutral3p5, Black }; + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/RGBColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/RGBColor.cs new file mode 100644 index 000000000..cf392cf32 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/RGBColor.cs @@ -0,0 +1,239 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Colourful.Implementation; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +#if (DRAWING) +using System.Drawing; + +#endif + +namespace Colourful +{ + /// <summary> + /// RGB color with specified <see cref="IRGBWorkingSpace">working space</see> + /// </summary> + public readonly struct RGBColor : IColorVector, IEquatable<RGBColor> + { + #region Other + + /// <summary> + /// sRGB color space. + /// Used when working space is not specified explicitly. + /// </summary> + public static readonly IRGBWorkingSpace DefaultWorkingSpace = RGBWorkingSpaces.sRGB; + + #endregion + + #region Constructor + + /// <param name="r">Red (from 0 to 1)</param> + /// <param name="g">Green (from 0 to 1)</param> + /// <param name="b">Blue (from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public RGBColor(double r, double g, double b) + : this(r, g, b, DefaultWorkingSpace) + { + } + + /// <param name="r">Red (from 0 to 1)</param> + /// <param name="g">Green (from 0 to 1)</param> + /// <param name="b">Blue (from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public RGBColor(double r, double g, double b, IRGBWorkingSpace workingSpace) + { + R = r.CheckRange(0, 1); + G = g.CheckRange(0, 1); + B = b.CheckRange(0, 1); + _workingSpace = workingSpace; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (range from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public RGBColor(Vector vector) + : this(vector, DefaultWorkingSpace) + { + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (range from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public RGBColor(Vector vector, IRGBWorkingSpace workingSpace) + : this(vector[0], vector[1], vector[2], workingSpace) + { + } + +#if (DRAWING) +/// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public RGBColor(Color color) + : this(color, DefaultWorkingSpace) + { + } + + /// <param name="color"></param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public RGBColor(Color color, IRGBWorkingSpace workingSpace) + : this((double)color.R / 255, (double)color.G / 255, (double)color.B / 255, workingSpace) + { + } + +#endif + + #endregion + + #region Channels + + /// <summary> + /// Red + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double R { get; } + + /// <summary> + /// Green + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double G { get; } + + /// <summary> + /// Blue + /// </summary> + /// <remarks> + /// Ranges from 0 to 1. + /// </remarks> + public double B { get; } + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { R, G, B }; + + #endregion + + #region Attributes + + /// <summary> + /// RGB color space + /// <seealso cref="RGBWorkingSpaces" /> + /// </summary> + public IRGBWorkingSpace WorkingSpace => _workingSpace ?? DefaultWorkingSpace; + + private readonly IRGBWorkingSpace _workingSpace; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(RGBColor other) => + R == other.R && + G == other.G && + B == other.B && + WorkingSpace.Equals(other.WorkingSpace); + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is RGBColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ WorkingSpace.GetHashCode(); + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(RGBColor left, RGBColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(RGBColor left, RGBColor right) => !Equals(left, right); + + #endregion + + #region Factory methods + + /// <summary> + /// Creates RGB color with all channels equal + /// </summary> + /// <param name="value">Grey value (from 0 to 1)</param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public static RGBColor FromGrey(double value, IRGBWorkingSpace workingSpace) => new RGBColor(value, value, value, workingSpace); + + /// <summary> + /// Creates RGB color with all channels equal + /// </summary> + /// <param name="value">Grey value (from 0 to 1)</param> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public static RGBColor FromGrey(double value) => FromGrey(value, DefaultWorkingSpace); + + /// <summary> + /// Creates RGB color from 8-bit channels + /// </summary> + /// <param name="red"></param> + /// <param name="green"></param> + /// <param name="blue"></param> + /// <param name="workingSpace"> + /// <see cref="RGBWorkingSpaces" /> + /// </param> + public static RGBColor FromRGB8bit(byte red, byte green, byte blue, IRGBWorkingSpace workingSpace) => new RGBColor(red / 255d, green / 255d, blue / 255d, workingSpace); + + + /// <summary> + /// Creates RGB color from 8-bit channels + /// </summary> + /// <remarks>Uses <see cref="DefaultWorkingSpace" /> as working space.</remarks> + public static RGBColor FromRGB8bit(byte red, byte green, byte blue) => FromRGB8bit(red, green, blue, DefaultWorkingSpace); + + #endregion + +#if (DRAWING) + #region Color conversions + + /// <summary> + /// Convert to <see cref="System.Drawing.Color" />. + /// </summary> + public Color ToColor() => this; + + /// <summary> + /// Convert to <see cref="System.Drawing.Color" />. + /// </summary> + public static implicit operator Color(RGBColor input) + { + var r = (byte)Math.Round(input.R * 255).CropRange(0, 255); + var g = (byte)Math.Round(input.G * 255).CropRange(0, 255); + var b = (byte)Math.Round(input.B * 255).CropRange(0, 255); + var output = Color.FromArgb(r, g, b); + return output; + } + + /// <summary> + /// Convert from <see cref="System.Drawing.Color" />. + /// </summary> + public static explicit operator RGBColor(Color color) => new RGBColor(color); + + #endregion + +#endif + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "RGB [R={0:0.##}, G={1:0.##}, B={2:0.##}]", R, G, B); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/RGBWorkingSpaces.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/RGBWorkingSpaces.cs new file mode 100644 index 000000000..fe20d20a0 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/RGBWorkingSpaces.cs @@ -0,0 +1,111 @@ +using Colourful.Implementation.RGB; + +namespace Colourful +{ + /// <remarks> + /// Chromaticity coordinates taken from: + /// http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html + /// </remarks> + public static class RGBWorkingSpaces + { + /// <summary> + /// sRGB + /// </summary> + /// <remarks> + /// Uses proper companding function, according to: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// </remarks> + public static readonly RGBWorkingSpace sRGB = new RGBWorkingSpace(Illuminants.D65, new sRGBCompanding(), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6400, 0.3300), new xyChromaticityCoordinates(0.3000, 0.6000), new xyChromaticityCoordinates(0.1500, 0.0600))); + + /// <summary> + /// Simplified sRGB (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="sRGBCompanding" />). + /// See also <see cref="sRGB" />. + /// </summary> + public static readonly RGBWorkingSpace sRGBSimplified = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6400, 0.3300), new xyChromaticityCoordinates(0.3000, 0.6000), new xyChromaticityCoordinates(0.1500, 0.0600))); + + /// <summary> + /// Rec. 709 (ITU-R Recommendation BT.709) + /// </summary> + public static readonly RGBWorkingSpace Rec709 = new RGBWorkingSpace(Illuminants.D65, new Rec709Companding(), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.64, 0.33), new xyChromaticityCoordinates(0.30, 0.60), new xyChromaticityCoordinates(0.15, 0.06))); + + /// <summary> + /// Rec. 2020 (ITU-R Recommendation BT.2020) + /// </summary> + public static readonly RGBWorkingSpace Rec2020 = new RGBWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.708, 0.292), new xyChromaticityCoordinates(0.170, 0.797), new xyChromaticityCoordinates(0.131, 0.046))); + + /// <summary> + /// ECI RGB v2 + /// </summary> + public static readonly RGBWorkingSpace ECIRGBv2 = new RGBWorkingSpace(Illuminants.D50, new LCompanding(), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6700, 0.3300), new xyChromaticityCoordinates(0.2100, 0.7100), new xyChromaticityCoordinates(0.1400, 0.0800))); + + /// <summary> + /// Adobe RGB (1998) + /// </summary> + public static readonly RGBWorkingSpace AdobeRGB1998 = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6400, 0.3300), new xyChromaticityCoordinates(0.2100, 0.7100), new xyChromaticityCoordinates(0.1500, 0.0600))); + + /// <summary> + /// Apple sRGB + /// </summary> + public static readonly RGBWorkingSpace ApplesRGB = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(1.8), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6250, 0.3400), new xyChromaticityCoordinates(0.2800, 0.5950), new xyChromaticityCoordinates(0.1550, 0.0700))); + + /// <summary> + /// Best RGB + /// </summary> + public static readonly RGBWorkingSpace BestRGB = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.7347, 0.2653), new xyChromaticityCoordinates(0.2150, 0.7750), new xyChromaticityCoordinates(0.1300, 0.0350))); + + /// <summary> + /// Beta RGB + /// </summary> + public static readonly RGBWorkingSpace BetaRGB = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6888, 0.3112), new xyChromaticityCoordinates(0.1986, 0.7551), new xyChromaticityCoordinates(0.1265, 0.0352))); + + /// <summary> + /// Bruce RGB + /// </summary> + public static readonly RGBWorkingSpace BruceRGB = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6400, 0.3300), new xyChromaticityCoordinates(0.2800, 0.6500), new xyChromaticityCoordinates(0.1500, 0.0600))); + + /// <summary> + /// CIE RGB + /// </summary> + public static readonly RGBWorkingSpace CIERGB = new RGBWorkingSpace(Illuminants.E, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.7350, 0.2650), new xyChromaticityCoordinates(0.2740, 0.7170), new xyChromaticityCoordinates(0.1670, 0.0090))); + + /// <summary> + /// ColorMatch RGB + /// </summary> + public static readonly RGBWorkingSpace ColorMatchRGB = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(1.8), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6300, 0.3400), new xyChromaticityCoordinates(0.2950, 0.6050), new xyChromaticityCoordinates(0.1500, 0.0750))); + + /// <summary> + /// Don RGB 4 + /// </summary> + public static readonly RGBWorkingSpace DonRGB4 = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6960, 0.3000), new xyChromaticityCoordinates(0.2150, 0.7650), new xyChromaticityCoordinates(0.1300, 0.0350))); + + /// <summary> + /// Ekta Space PS5 + /// </summary> + public static readonly RGBWorkingSpace EktaSpacePS5 = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6950, 0.3050), new xyChromaticityCoordinates(0.2600, 0.7000), new xyChromaticityCoordinates(0.1100, 0.0050))); + + /// <summary> + /// NTSC RGB + /// </summary> + public static readonly RGBWorkingSpace NTSCRGB = new RGBWorkingSpace(Illuminants.C, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6700, 0.3300), new xyChromaticityCoordinates(0.2100, 0.7100), new xyChromaticityCoordinates(0.1400, 0.0800))); + + /// <summary> + /// PAL/SECAM RGB + /// </summary> + public static readonly RGBWorkingSpace PALSECAMRGB = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6400, 0.3300), new xyChromaticityCoordinates(0.2900, 0.6000), new xyChromaticityCoordinates(0.1500, 0.0600))); + + /// <summary> + /// ProPhoto RGB + /// </summary> + public static readonly RGBWorkingSpace ProPhotoRGB = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(1.8), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.7347, 0.2653), new xyChromaticityCoordinates(0.1596, 0.8404), new xyChromaticityCoordinates(0.0366, 0.0001))); + + /// <summary> + /// SMPTE-C RGB + /// </summary> + public static readonly RGBWorkingSpace SMPTECRGB = new RGBWorkingSpace(Illuminants.D65, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.6300, 0.3400), new xyChromaticityCoordinates(0.3100, 0.5950), new xyChromaticityCoordinates(0.1550, 0.0700))); + + /// <summary> + /// Wide Gamut RGB + /// </summary> + public static readonly RGBWorkingSpace WideGamutRGB = new RGBWorkingSpace(Illuminants.D50, new GammaCompanding(2.2), new RGBPrimariesChromaticityCoordinates(new xyChromaticityCoordinates(0.7350, 0.2650), new xyChromaticityCoordinates(0.1150, 0.8260), new xyChromaticityCoordinates(0.1570, 0.0180))); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/XYZColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/XYZColor.cs new file mode 100644 index 000000000..ac893804e --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/XYZColor.cs @@ -0,0 +1,95 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE 1931 XYZ color space + /// </summary> + public readonly struct XYZColor : IColorVector + { + #region Constructor + + /// <param name="x">X (usually from 0 to 1)</param> + /// <param name="y">Y (usually from 0 to 1)</param> + /// <param name="z">Z (usually from 0 to 1)</param> + public XYZColor(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (usually from 0 to 1)</param> + public XYZColor(Vector vector) + : this(vector[0], vector[1], vector[2]) + { + } + + #endregion + + #region Channels + + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double X { get; } + + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double Y { get; } + + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double Z { get; } + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { X, Y, Z }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(XYZColor other) => + X == other.X && + Y == other.Y && + Z == other.Z; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is XYZColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + hashCode = (hashCode * 397) ^ Z.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(XYZColor left, XYZColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(XYZColor left, XYZColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "XYZ [X={0:0.##}, Y={1:0.##}, Z={2:0.##}]", X, Y, Z); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/xyChromaticityCoordinates.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/xyChromaticityCoordinates.cs new file mode 100644 index 000000000..aedfd1aa2 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/xyChromaticityCoordinates.cs @@ -0,0 +1,62 @@ +using System.Globalization; + +namespace Colourful +{ + /// <summary> + /// Coordinates of CIE xy chromaticity space + /// </summary> + public readonly struct xyChromaticityCoordinates + { + /// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param> + /// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param> + public xyChromaticityCoordinates(double x, double y) + { + this.x = x; + this.y = y; + } + + /// <summary> + /// Chromaticity coordinate x + /// </summary> + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double x { get; } + + /// <summary> + /// Chromaticity coordinate y + /// </summary> + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double y { get; } + + /// <inheritdoc cref="object" /> + public bool Equals(xyChromaticityCoordinates other) => x.Equals(other.x) && y.Equals(other.y); + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is xyChromaticityCoordinates coordinates && Equals(coordinates); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + return (x.GetHashCode() * 397) ^ y.GetHashCode(); + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(xyChromaticityCoordinates left, xyChromaticityCoordinates right) => left.Equals(right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(xyChromaticityCoordinates left, xyChromaticityCoordinates right) => !left.Equals(right); + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "xy [x={0:0.##}, y={1:0.##}]", x, y); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colors/xyYColor.cs b/Software/Visual_Studio/SideChains/Colourful/Colors/xyYColor.cs new file mode 100644 index 000000000..fe73163dd --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colors/xyYColor.cs @@ -0,0 +1,109 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful +{ + /// <summary> + /// CIE xyY color space (derived from <see cref="XYZColor" /> color space) + /// </summary> + public readonly struct xyYColor : IColorVector + { + #region Constructor + + /// <param name="x">x (usually from 0 to 1) chromaticity coordinate</param> + /// <param name="y">y (usually from 0 to 1) chromaticity coordinate</param> + /// <param name="Y">Y (usually from 0 to 1)</param> + public xyYColor(double x, double y, double Y) + : this(new xyChromaticityCoordinates(x, y), Y) + { + } + + /// <param name="chromaticity">Chromaticity coordinates (x and y together)</param> + /// <param name="Y">Y (usually from 0 to 1)</param> + public xyYColor(xyChromaticityCoordinates chromaticity, double Y) + { + Chromaticity = chromaticity; + Luminance = Y; + } + + /// <param name="vector"><see cref="Vector" />, expected 3 dimensions (usually from 0 to 1)</param> + public xyYColor(Vector vector) + : this(vector[0], vector[1], vector[2]) + { + } + + #endregion + + #region Channels + + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double x => Chromaticity.x; + + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double y => Chromaticity.y; + + /// <summary> + /// Y channel (luminance) + /// </summary> + /// <remarks> + /// Ranges usually from 0 to 1. + /// </remarks> + public double Luminance { get; } + + /// <remarks> + /// Chromaticity coordinates (identical to x and y) + /// </remarks> + public xyChromaticityCoordinates Chromaticity { get; } + + /// <summary> + /// <see cref="IColorVector" /> + /// </summary> + public Vector Vector => new[] { x, y, Luminance }; + + #endregion + + #region Equality + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(xyYColor other) => + x == other.x && + y == other.y && + Luminance == other.Luminance; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is xyYColor other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = x.GetHashCode(); + hashCode = (hashCode * 397) ^ y.GetHashCode(); + hashCode = (hashCode * 397) ^ Luminance.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(xyYColor left, xyYColor right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(xyYColor left, xyYColor right) => !Equals(left, right); + + #endregion + + #region Overrides + + /// <inheritdoc cref="object" /> + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "xyY [x={0:0.##}, y={1:0.##}, Y={2:0.##}]", x, y, Luminance); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Colourful.csproj b/Software/Visual_Studio/SideChains/Colourful/Colourful.csproj new file mode 100644 index 000000000..113934620 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Colourful.csproj @@ -0,0 +1,65 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup Label="Basic build"> + <TargetFrameworks>net45;netstandard1.1;netstandard2.0;</TargetFrameworks> + <LangVersion>7.3</LangVersion> + <Deterministic>true</Deterministic> + </PropertyGroup> + + <PropertyGroup Label="NuGet package"> + <Authors>Tomáš Pažourek</Authors> + <Company>$(Authors)</Company> + <Copyright>$(Copyright)</Copyright> + <PackageLicenseExpression>MIT</PackageLicenseExpression> + <RepositoryUrl>https://github.com/tompazourek/Colourful</RepositoryUrl> + <RepositoryType>git</RepositoryType> + <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> + <PackageReleaseNotes>$(RepositoryUrl)/releases</PackageReleaseNotes> + <PackageIcon>logo_64.png</PackageIcon> + <PackageTags>adobe-rgb bradford c-sharp cct chromatic-adaptation chromaticity cie-lab cie-luv cie-xyy cie-xyz color-difference color-space conversion delta-e lab lms luv rgb srgb xyz</PackageTags> + <Description>Open source .NET library for working with color spaces.</Description> + </PropertyGroup> + + <PropertyGroup Label="Symbols, docs"> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <PublishRepositoryUrl>true</PublishRepositoryUrl> + <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder> + <EmbedUntrackedSources>true</EmbedUntrackedSources> + </PropertyGroup> + + <PropertyGroup Label="CI build only" Condition=" '$(CI)' == 'true' "> + <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> + </PropertyGroup> + + <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> + <GeneratePackageOnBuild>true</GeneratePackageOnBuild> + </PropertyGroup> + + <PropertyGroup Condition=" '$(TargetFramework)' == 'net45' "> + <DefineConstants>$(DefineConstants);DRAWING</DefineConstants> + </PropertyGroup> + + <ItemGroup Condition=" '$(TargetFramework)' == 'net45' "> + <Reference Include="System.Drawing" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> + </PackageReference> + <PackageReference Include="MinVer" Version="2.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + </ItemGroup> + + <ItemGroup> + <None Remove="*.DotSettings" /> + <None Include="..\..\assets\logo_64.png" Pack="true" PackagePath="" /> + </ItemGroup> + + <Target Name="UpdateAppVeyorBuildVersion" AfterTargets="MinVer" Condition=" '$(APPVEYOR)' == 'true' "> + <Exec Command="appveyor UpdateBuild -Version "$(MinVerVersion)"" /> + </Target> +</Project> diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.Adapt.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.Adapt.cs new file mode 100644 index 000000000..7ad705970 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.Adapt.cs @@ -0,0 +1,121 @@ +using System; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Performs chromatic adaptation of given XYZ color. + /// Target white point is <see cref="WhitePoint" />. + /// </summary> + public XYZColor Adapt(in XYZColor color, in XYZColor sourceWhitePoint) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + var result = ChromaticAdaptation.Transform(color, sourceWhitePoint, WhitePoint); + return result; + } + + /// <summary> + /// Adapts linear RGB color from the source working space to working space set in <see cref="TargetRGBWorkingSpace" />. + /// </summary> + public LinearRGBColor Adapt(in LinearRGBColor color) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + if (color.WorkingSpace.Equals(TargetRGBWorkingSpace)) + return color; + + // conversion to XYZ + var converterToXYZ = GetLinearRGBToXYZConverter(color.WorkingSpace); + var unadapted = converterToXYZ.Convert(color); + + // adaptation + var adapted = ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, TargetRGBWorkingSpace.WhitePoint); + + // conversion back to RGB + var converterToRGB = GetXYZToLinearRGBConverter(TargetRGBWorkingSpace); + var result = converterToRGB.Convert(adapted); + + return result; + } + + /// <summary> + /// Adapts RGB color from the source working space to working space set in <see cref="TargetRGBWorkingSpace" />. + /// </summary> + public RGBColor Adapt(in RGBColor color) + { + var linearInput = ToLinearRGB(color); + var linearOutput = Adapt(linearInput); + var compandedOutput = ToRGB(linearOutput); + + return compandedOutput; + } + + /// <summary> + /// Adapts Lab color from the source white point to white point set in <see cref="TargetLabWhitePoint" />. + /// </summary> + public LabColor Adapt(in LabColor color) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + if (color.WhitePoint.Equals(TargetLabWhitePoint)) + return color; + + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Adapts LChab color from the source white point to white point set in <see cref="TargetLabWhitePoint" />. + /// </summary> + public LChabColor Adapt(in LChabColor color) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + if (color.WhitePoint.Equals(TargetLabWhitePoint)) + return color; + + var labColor = ToLab(color); + var result = ToLChab(labColor); + return result; + } + + /// <summary> + /// Adapts Lab color from the source white point to white point set in <see cref="TargetHunterLabWhitePoint" />. + /// </summary> + public HunterLabColor Adapt(in HunterLabColor color) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + if (color.WhitePoint.Equals(TargetHunterLabWhitePoint)) + return color; + + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Adapts Luv color from the source white point to white point set in <see cref="TargetLuvWhitePoint" />. + /// </summary> + public LuvColor Adapt(in LuvColor color) + { + if (!IsChromaticAdaptationPerformed) + throw new InvalidOperationException("Cannot perform chromatic adaptation, provide chromatic adaptation method and white point."); + + if (color.WhitePoint.Equals(TargetLuvWhitePoint)) + return color; + + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToHunterLab.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToHunterLab.cs new file mode 100644 index 000000000..963ba4b40 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToHunterLab.cs @@ -0,0 +1,136 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in XYZColor color) + { + // adaptation + var adapted = !WhitePoint.Equals(TargetHunterLabWhitePoint) && IsChromaticAdaptationPerformed + ? ChromaticAdaptation.Transform(color, WhitePoint, TargetHunterLabWhitePoint) + : color; + + // conversion + var converter = new XYZToHunterLabConverter(TargetHunterLabWhitePoint); + var result = converter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToHunterLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to Hunter Lab color + /// </summary> + public HunterLabColor ToHunterLab<T>(T color) where T : struct, IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToHunterLab(in typedColor); + case LinearRGBColor typedColor: + return ToHunterLab(in typedColor); + case XYZColor typedColor: + return ToHunterLab(in typedColor); + case xyYColor typedColor: + return ToHunterLab(in typedColor); + case HunterLabColor typedColor: + return typedColor; + case LabColor typedColor: + return ToHunterLab(in typedColor); + case LChabColor typedColor: + return ToHunterLab(in typedColor); + case LuvColor typedColor: + return ToHunterLab(in typedColor); + case LChuvColor typedColor: + return ToHunterLab(in typedColor); + case LMSColor typedColor: + return ToHunterLab(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChab.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChab.cs new file mode 100644 index 000000000..7529a0fba --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChab.cs @@ -0,0 +1,134 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in XYZColor color) + { + var labColor = ToLab(color); + var result = ToLChab(labColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in LabColor color) + { + // adaptation to target lab white point (LabWhitePoint) + var adapted = IsChromaticAdaptationPerformed ? Adapt(color) : color; + + // conversion (preserving white point) + var converter = LabToLChabConverter.Default; + var result = converter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Lab) color + /// </summary> + public LChabColor ToLChab<T>(T color) where T : struct, IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLChab(in typedColor); + case LinearRGBColor typedColor: + return ToLChab(in typedColor); + case XYZColor typedColor: + return ToLChab(in typedColor); + case xyYColor typedColor: + return ToLChab(in typedColor); + case HunterLabColor typedColor: + return ToLChab(in typedColor); + case LabColor typedColor: + return ToLChab(in typedColor); + case LChabColor typedColor: + return typedColor; + case LuvColor typedColor: + return ToLChab(in typedColor); + case LChuvColor typedColor: + return ToLChab(in typedColor); + case LMSColor typedColor: + return ToLChab(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChuv.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChuv.cs new file mode 100644 index 000000000..7503e4478 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLChuv.cs @@ -0,0 +1,134 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in XYZColor color) + { + var luvColor = ToLuv(color); + var result = ToLChuv(luvColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in LuvColor color) + { + // adaptation to target luv white point (LuvWhitePoint) + var adapted = IsChromaticAdaptationPerformed ? Adapt(color) : color; + + // conversion (preserving white point) + var converter = LuvToLChuvConverter.Default; + var result = converter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLChuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*C*h° (Luv) color + /// </summary> + public LChuvColor ToLChuv<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLChuv(in typedColor); + case LinearRGBColor typedColor: + return ToLChuv(in typedColor); + case XYZColor typedColor: + return ToLChuv(in typedColor); + case xyYColor typedColor: + return ToLChuv(in typedColor); + case HunterLabColor typedColor: + return ToLChuv(in typedColor); + case LabColor typedColor: + return ToLChuv(in typedColor); + case LChabColor typedColor: + return ToLChuv(in typedColor); + case LuvColor typedColor: + return ToLChuv(in typedColor); + case LChuvColor typedColor: + return typedColor; + case LMSColor typedColor: + return ToLChuv(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLMS.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLMS.cs new file mode 100644 index 000000000..579c2c39b --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLMS.cs @@ -0,0 +1,130 @@ +using System; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in XYZColor color) + { + // conversion + var converter = _cachedXYZAndLMSConverter; + var result = converter.Convert(color); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLMS(xyzColor); + return result; + } + + /// <summary> + /// Convert to LMS color + /// </summary> + public LMSColor ToLMS<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLMS(in typedColor); + case LinearRGBColor typedColor: + return ToLMS(in typedColor); + case XYZColor typedColor: + return ToLMS(in typedColor); + case xyYColor typedColor: + return ToLMS(in typedColor); + case HunterLabColor typedColor: + return ToLMS(in typedColor); + case LabColor typedColor: + return ToLMS(in typedColor); + case LChabColor typedColor: + return ToLMS(in typedColor); + case LuvColor typedColor: + return ToLMS(in typedColor); + case LChuvColor typedColor: + return ToLMS(in typedColor); + case LMSColor typedColor: + return typedColor; + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLab.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLab.cs new file mode 100644 index 000000000..5a1b367f3 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLab.cs @@ -0,0 +1,143 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in XYZColor color) + { + // adaptation + var adapted = !WhitePoint.Equals(TargetLabWhitePoint) && IsChromaticAdaptationPerformed + ? ChromaticAdaptation.Transform(color, WhitePoint, TargetLabWhitePoint) + : color; + + // conversion + var converter = new XYZToLabConverter(TargetLabWhitePoint); + var result = converter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in LChabColor color) + { + // conversion (preserving white point) + var converter = LChabToLabConverter.Default; + var unadapted = converter.Convert(color); + + if (!IsChromaticAdaptationPerformed) + return unadapted; + + // adaptation to target lab white point (LabWhitePoint) + var adapted = Adapt(unadapted); + return adapted; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLab(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*a*b* (1976) color + /// </summary> + public LabColor ToLab<T>(T color) where T : struct, IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLab(in typedColor); + case LinearRGBColor typedColor: + return ToLab(in typedColor); + case XYZColor typedColor: + return ToLab(in typedColor); + case xyYColor typedColor: + return ToLab(in typedColor); + case HunterLabColor typedColor: + return ToLab(in typedColor); + case LabColor typedColor: + return typedColor; + case LChabColor typedColor: + return ToLab(in typedColor); + case LuvColor typedColor: + return ToLab(in typedColor); + case LChuvColor typedColor: + return ToLab(in typedColor); + case LMSColor typedColor: + return ToLab(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLinearRGB.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLinearRGB.cs new file mode 100644 index 000000000..2c6a8a821 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLinearRGB.cs @@ -0,0 +1,136 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in RGBColor color) + { + var converter = RGBToLinearRGBConverter.Default; + + return converter.Convert(color); + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in XYZColor color) + { + // adaptation + var adapted = TargetRGBWorkingSpace.WhitePoint.Equals(WhitePoint) || !IsChromaticAdaptationPerformed + ? color + : ChromaticAdaptation.Transform(color, WhitePoint, TargetRGBWorkingSpace.WhitePoint); + + // conversion to linear RGB + var xyzConverter = GetXYZToLinearRGBConverter(TargetRGBWorkingSpace); + var result = xyzConverter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLinearRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to linear RGB + /// </summary> + public LinearRGBColor ToLinearRGB<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLinearRGB(in typedColor); + case LinearRGBColor typedColor: + return typedColor; + case XYZColor typedColor: + return ToLinearRGB(in typedColor); + case xyYColor typedColor: + return ToLinearRGB(in typedColor); + case HunterLabColor typedColor: + return ToLinearRGB(in typedColor); + case LabColor typedColor: + return ToLinearRGB(in typedColor); + case LChabColor typedColor: + return ToLinearRGB(in typedColor); + case LuvColor typedColor: + return ToLinearRGB(in typedColor); + case LChuvColor typedColor: + return ToLinearRGB(in typedColor); + case LMSColor typedColor: + return ToLinearRGB(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLuv.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLuv.cs new file mode 100644 index 000000000..a6fd90f64 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToLuv.cs @@ -0,0 +1,143 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in XYZColor color) + { + // adaptation + var adapted = !WhitePoint.Equals(TargetLuvWhitePoint) && IsChromaticAdaptationPerformed + ? ChromaticAdaptation.Transform(color, WhitePoint, TargetLuvWhitePoint) + : color; + + // conversion + var converter = new XYZToLuvConverter(TargetLuvWhitePoint); + var result = converter.Convert(adapted); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in LChuvColor color) + { + // conversion (preserving white point) + var converter = LChuvToLuvConverter.Default; + var unadapted = converter.Convert(color); + + if (!IsChromaticAdaptationPerformed) + return unadapted; + + // adaptation to target luv white point (LuvWhitePoint) + var adapted = Adapt(unadapted); + return adapted; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToLuv(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE L*u*v* (1976) color + /// </summary> + public LuvColor ToLuv<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToLuv(in typedColor); + case LinearRGBColor typedColor: + return ToLuv(in typedColor); + case XYZColor typedColor: + return ToLuv(in typedColor); + case xyYColor typedColor: + return ToLuv(in typedColor); + case HunterLabColor typedColor: + return ToLuv(in typedColor); + case LabColor typedColor: + return ToLuv(in typedColor); + case LChabColor typedColor: + return ToLuv(in typedColor); + case LuvColor typedColor: + return typedColor; + case LChuvColor typedColor: + return ToLuv(in typedColor); + case LMSColor typedColor: + return ToLuv(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToRGB.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToRGB.cs new file mode 100644 index 000000000..a899e46a0 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToRGB.cs @@ -0,0 +1,146 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + private XYZToLinearRGBConverter _lastXYZToLinearRGBConverter; + + private XYZToLinearRGBConverter GetXYZToLinearRGBConverter(IRGBWorkingSpace workingSpace) + { + if (_lastXYZToLinearRGBConverter != null && + _lastXYZToLinearRGBConverter.TargetRGBWorkingSpace.Equals(workingSpace)) + return _lastXYZToLinearRGBConverter; + + return _lastXYZToLinearRGBConverter = new XYZToLinearRGBConverter(workingSpace); + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LinearRGBColor color) + { + // conversion + var converter = LinearRGBToRGBConverter.Default; + + var result = converter.Convert(color); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in XYZColor color) + { + // conversion + var linear = ToLinearRGB(color); + + // companding to RGB + var result = ToRGB(linear); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in xyYColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToRGB(xyzColor); + return result; + } + + /// <summary> + /// Convert to RGB color + /// </summary> + public RGBColor ToRGB<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return typedColor; + case LinearRGBColor typedColor: + return ToRGB(in typedColor); + case XYZColor typedColor: + return ToRGB(in typedColor); + case xyYColor typedColor: + return ToRGB(in typedColor); + case HunterLabColor typedColor: + return ToRGB(in typedColor); + case LabColor typedColor: + return ToRGB(in typedColor); + case LChabColor typedColor: + return ToRGB(in typedColor); + case LuvColor typedColor: + return ToRGB(in typedColor); + case LChuvColor typedColor: + return ToRGB(in typedColor); + case LMSColor typedColor: + return ToRGB(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToXYZ.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToXYZ.cs new file mode 100644 index 000000000..eb2af21cf --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToXYZ.cs @@ -0,0 +1,187 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + private LinearRGBToXYZConverter _lastLinearRGBToXYZConverter; + + private LinearRGBToXYZConverter GetLinearRGBToXYZConverter(IRGBWorkingSpace workingSpace) + { + if (_lastLinearRGBToXYZConverter != null && + _lastLinearRGBToXYZConverter.SourceRGBWorkingSpace.Equals(workingSpace)) + return _lastLinearRGBToXYZConverter; + + return _lastLinearRGBToXYZConverter = new LinearRGBToXYZConverter(workingSpace); + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in RGBColor color) + { + // uncompanding + var rgbConverter = RGBToLinearRGBConverter.Default; + + var linear = rgbConverter.Convert(color); + + // conversion + var result = ToXYZ(linear); + return result; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LinearRGBColor color) + { + // conversion + var converterXyz = GetLinearRGBToXYZConverter(color.WorkingSpace); + var unadapted = converterXyz.Convert(color); + + // adaptation + var adapted = color.WorkingSpace.WhitePoint.Equals(WhitePoint) || !IsChromaticAdaptationPerformed + ? unadapted + : Adapt(unadapted, color.WorkingSpace.WhitePoint); + + return adapted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in xyYColor color) + { + // conversion + var converter = xyYAndXYZConverter.Default; + var converted = converter.Convert(color); + return converted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LabColor color) + { + // conversion + var converter = LabToXYZConverter.Default; + var unadapted = converter.Convert(color); + + // adaptation + var adapted = color.WhitePoint.Equals(WhitePoint) || !IsChromaticAdaptationPerformed + ? unadapted + : Adapt(unadapted, color.WhitePoint); + + return adapted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LChabColor color) + { + // conversion to Lab + var labConverter = LChabToLabConverter.Default; + + var labColor = labConverter.Convert(color); + + // conversion to XYZ (incl. adaptation) + var result = ToXYZ(labColor); + return result; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in HunterLabColor color) + { + // conversion + var converter = HunterLabToXYZConverter.Default; + + var unadapted = converter.Convert(color); + + // adaptation + var adapted = color.WhitePoint.Equals(WhitePoint) || !IsChromaticAdaptationPerformed + ? unadapted + : Adapt(unadapted, color.WhitePoint); + + return adapted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LuvColor color) + { + // conversion + var converter = LuvToXYZConverter.Default; + var unadapted = converter.Convert(color); + + // adaptation + var adapted = color.WhitePoint.Equals(WhitePoint) || !IsChromaticAdaptationPerformed + ? unadapted + : Adapt(unadapted, color.WhitePoint); + + return adapted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LChuvColor color) + { + // conversion to Luv + var luvConverter = LChuvToLuvConverter.Default; + + var labColor = luvConverter.Convert(color); + + // conversion to XYZ (incl. adaptation) + var result = ToXYZ(labColor); + return result; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ(in LMSColor color) + { + // conversion + var converter = _cachedXYZAndLMSConverter; + var converted = converter.Convert(color); + return converted; + } + + /// <summary> + /// Convert to CIE 1931 XYZ color + /// </summary> + public XYZColor ToXYZ<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToXYZ(in typedColor); + case LinearRGBColor typedColor: + return ToXYZ(in typedColor); + case XYZColor typedColor: + return typedColor; + case xyYColor typedColor: + return ToXYZ(in typedColor); + case HunterLabColor typedColor: + return ToXYZ(in typedColor); + case LabColor typedColor: + return ToXYZ(in typedColor); + case LChabColor typedColor: + return ToXYZ(in typedColor); + case LuvColor typedColor: + return ToXYZ(in typedColor); + case LChuvColor typedColor: + return ToXYZ(in typedColor); + case LMSColor typedColor: + return ToXYZ(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToxyY.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToxyY.cs new file mode 100644 index 000000000..823356403 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.ToxyY.cs @@ -0,0 +1,131 @@ +using System; +using Colourful.Implementation.Conversion; + +namespace Colourful.Conversion +{ + public partial class ColourfulConverter + { + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in RGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LinearRGBColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in XYZColor color) + { + // conversion + var converter = new xyYAndXYZConverter(); + var result = converter.Convert(color); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LChabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in HunterLabColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LChuvColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY(in LMSColor color) + { + var xyzColor = ToXYZ(color); + var result = ToxyY(xyzColor); + return result; + } + + /// <summary> + /// Convert to CIE xyY color + /// </summary> + public xyYColor ToxyY<T>(T color) where T : IColorVector + { + switch (color) + { + case RGBColor typedColor: + return ToxyY(in typedColor); + case LinearRGBColor typedColor: + return ToxyY(in typedColor); + case XYZColor typedColor: + return ToxyY(in typedColor); + case xyYColor typedColor: + return typedColor; + case HunterLabColor typedColor: + return ToxyY(in typedColor); + case LabColor typedColor: + return ToxyY(in typedColor); + case LChabColor typedColor: + return ToxyY(in typedColor); + case LuvColor typedColor: + return ToxyY(in typedColor); + case LChuvColor typedColor: + return ToxyY(in typedColor); + case LMSColor typedColor: + return ToxyY(in typedColor); + default: + throw new ArgumentException($"Cannot accept type '{typeof(T)}'.", nameof(color)); + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.cs new file mode 100644 index 000000000..b32b018d1 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/ColourfulConverter.cs @@ -0,0 +1,98 @@ +using Colourful.Implementation.Conversion; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Conversion +{ + /// <summary> + /// Converts between color spaces and makes sure that the color is adapted using chromatic adaptation. + /// </summary> + public partial class ColourfulConverter + { + /// <summary> + /// Constructs the converter and sets the defaults + /// </summary> + public ColourfulConverter() + { + WhitePoint = DefaultWhitePoint; + LMSTransformationMatrix = XYZAndLMSConverter.DefaultTransformationMatrix; + ChromaticAdaptation = new VonKriesChromaticAdaptation(_cachedXYZAndLMSConverter, _cachedXYZAndLMSConverter); + + TargetLabWhitePoint = LabColor.DefaultWhitePoint; + TargetHunterLabWhitePoint = HunterLabColor.DefaultWhitePoint; + TargetLuvWhitePoint = LuvColor.DefaultWhitePoint; + TargetRGBWorkingSpace = RGBColor.DefaultWorkingSpace; + } + + private bool IsChromaticAdaptationPerformed => ChromaticAdaptation != null; + + #region Attributes + + /// <summary> + /// Default white point + /// </summary> + public static readonly XYZColor DefaultWhitePoint = Illuminants.D65; + + private Matrix _transformationMatrix; + + /// <summary> + /// Chromatic adaptation method used. When null, no adaptation will be performed. + /// </summary> + public IChromaticAdaptation ChromaticAdaptation { get; set; } + + /// <summary> + /// Transformation matrix used in conversion to <see cref="LMSColor" />, also used in the default Von Kries Chromatic Adaptation method. + /// </summary> + public Matrix LMSTransformationMatrix + { + get => _transformationMatrix; + set + { + _transformationMatrix = value; + + if (_cachedXYZAndLMSConverter == null) + { + _cachedXYZAndLMSConverter = new XYZAndLMSConverter(value); + } + else + { + _cachedXYZAndLMSConverter.TransformationMatrix = value; + } + } + } + + private XYZAndLMSConverter _cachedXYZAndLMSConverter; + + /// <summary> + /// White point used for chromatic adaptation in conversions from/to XYZ color space. + /// When null, no adaptation will be performed. + /// <seealso cref="TargetLabWhitePoint" /> + /// </summary> + public XYZColor WhitePoint { get; set; } + + /// <summary> + /// White point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information) + /// Defaults to: <see cref="LabColor.DefaultWhitePoint" />. + /// </summary> + public XYZColor TargetLabWhitePoint { get; set; } + + /// <summary> + /// White point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information) + /// Defaults to: <see cref="LuvColor.DefaultWhitePoint" />. + /// </summary> + public XYZColor TargetLuvWhitePoint { get; set; } + + /// <summary> + /// White point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) + /// Defaults to: <see cref="HunterLabColor.DefaultWhitePoint" />. + /// </summary> + public XYZColor TargetHunterLabWhitePoint { get; set; } + + /// <summary> + /// Working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) + /// Defaults to: <see cref="RGBColor.DefaultWorkingSpace" />. + /// </summary> + public IRGBWorkingSpace TargetRGBWorkingSpace { get; set; } + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/IChromaticAdaptation.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/IChromaticAdaptation.cs new file mode 100644 index 000000000..084ba07d6 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/IChromaticAdaptation.cs @@ -0,0 +1,13 @@ +namespace Colourful.Conversion +{ + /// <summary> + /// Chromatic adaptation. + /// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] + /// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD). + /// </summary> + public interface IChromaticAdaptation + { + /// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks> + XYZColor Transform(in XYZColor sourceColor, in XYZColor sourceWhitePoint, in XYZColor targetWhitePoint); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Conversion/VonKriesChromaticAdaptation.cs b/Software/Visual_Studio/SideChains/Colourful/Conversion/VonKriesChromaticAdaptation.cs new file mode 100644 index 000000000..5a6b4999c --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Conversion/VonKriesChromaticAdaptation.cs @@ -0,0 +1,77 @@ +using System; +using Colourful.Implementation; +using Colourful.Implementation.Conversion; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Conversion +{ + /// <summary> + /// Basic implementation of the von Kries chromatic adaptation model + /// </summary> + /// <remarks> + /// Transformation described here: + /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + /// </remarks> + public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation + { + private readonly IColorConversion<XYZColor, LMSColor> _conversionToLMS; + private readonly IColorConversion<LMSColor, XYZColor> _conversionToXYZ; + private Matrix _cachedDiagonalMatrix; + + private XYZColor _lastSourceWhitePoint; + private XYZColor _lastTargetWhitePoint; + + /// <summary> + /// Constructs von Kries chromatic adaptation using default <see cref="XYZAndLMSConverter" /> + /// </summary> + public VonKriesChromaticAdaptation() : this(new XYZAndLMSConverter()) + { + } + + /// <summary> + /// Transformation matrix used for the conversion (definition of the cone response domain). + /// <see cref="LMSTransformationMatrix" /> + /// </summary> + public VonKriesChromaticAdaptation(Matrix transformationMatrix) : this(new XYZAndLMSConverter(transformationMatrix)) + { + } + + private VonKriesChromaticAdaptation(XYZAndLMSConverter converter) : this(converter, converter) + { + } + + /// <summary> + /// Constructs von Kries chromatic adaptation using given converters + /// </summary> + public VonKriesChromaticAdaptation(IColorConversion<XYZColor, LMSColor> conversionToLMS, IColorConversion<LMSColor, XYZColor> conversionToXYZ) + { + _conversionToLMS = conversionToLMS ?? throw new ArgumentNullException(nameof(conversionToLMS)); + _conversionToXYZ = conversionToXYZ ?? throw new ArgumentNullException(nameof(conversionToXYZ)); + } + + /// <summary> + /// Transforms XYZ color to destination reference white. + /// </summary> + public XYZColor Transform(in XYZColor sourceColor, in XYZColor sourceWhitePoint, in XYZColor targetWhitePoint) + { + if (sourceWhitePoint.Equals(targetWhitePoint)) + return sourceColor; + + var sourceColorLMS = _conversionToLMS.Convert(sourceColor); + + if (sourceWhitePoint != _lastSourceWhitePoint || targetWhitePoint != _lastTargetWhitePoint) + { + var sourceWhitePointLMS = _conversionToLMS.Convert(sourceWhitePoint); + var targetWhitePointLMS = _conversionToLMS.Convert(targetWhitePoint); + + _cachedDiagonalMatrix = MatrixFactory.CreateDiagonal(targetWhitePointLMS.L / sourceWhitePointLMS.L, targetWhitePointLMS.M / sourceWhitePointLMS.M, targetWhitePointLMS.S / sourceWhitePointLMS.S); + _lastSourceWhitePoint = sourceWhitePoint; + _lastTargetWhitePoint = targetWhitePoint; + } + + var targetColorLMS = new LMSColor(_cachedDiagonalMatrix.MultiplyBy(sourceColorLMS.Vector)); + var targetColor = _conversionToXYZ.Convert(targetColorLMS); + return targetColor; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Difference/CIE76ColorDifference.cs b/Software/Visual_Studio/SideChains/Colourful/Difference/CIE76ColorDifference.cs new file mode 100644 index 000000000..5883151c1 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Difference/CIE76ColorDifference.cs @@ -0,0 +1,27 @@ +using System; + +namespace Colourful.Difference +{ + /// <summary> + /// CIE Delta-E 1976 formula + /// </summary> + public sealed class CIE76ColorDifference : IColorDifference<LabColor> + { + /// <param name="x">Reference color</param> + /// <param name="y">Sample color</param> + /// <returns>Delta-E (1976) color difference</returns> + public double ComputeDifference(in LabColor x, in LabColor y) + { + if (x.WhitePoint != y.WhitePoint) + throw new ArgumentException("Colors must have same white point to be compared."); + + // Euclidean distance + var distance = Math.Sqrt( + (x.L - y.L) * (x.L - y.L) + + (x.a - y.a) * (x.a - y.a) + + (x.b - y.b) * (x.b - y.b) + ); + return distance; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Difference/CIE94ColorDifference.cs b/Software/Visual_Studio/SideChains/Colourful/Difference/CIE94ColorDifference.cs new file mode 100644 index 000000000..d50602f9c --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Difference/CIE94ColorDifference.cs @@ -0,0 +1,94 @@ +using System; +using Colourful.Implementation; + +namespace Colourful.Difference +{ + /// <summary> + /// CIE Delta-E 1994 formula + /// </summary> + /// <remarks> + /// Implementation notes: + /// http://www.brucelindbloom.com/Eqn_DeltaE_CIE94.html + /// </remarks> + public sealed class CIE94ColorDifference : IColorDifference<LabColor> + { + private const double KH = 1; + private const double KC = 1; + + private readonly double K1; + private readonly double K2; + private readonly double KL; + + /// <summary> + /// Construct using weighting factors for <see cref="CIE94ColorDifferenceApplication.GraphicArts" />. + /// </summary> + public CIE94ColorDifference() : this(CIE94ColorDifferenceApplication.GraphicArts) + { + } + + /// <summary> + /// Construct using weighting factors for given application of color difference + /// </summary> + /// <param name="application">A <see cref="CIE94ColorDifferenceApplication" /> value specifying the application area. Different weighting factors are used in the computation depending on the application.</param> + public CIE94ColorDifference(CIE94ColorDifferenceApplication application) + { + switch (application) + { + case CIE94ColorDifferenceApplication.GraphicArts: + KL = 1; + K1 = 0.045; + K2 = 0.015; + break; + case CIE94ColorDifferenceApplication.Textiles: + KL = 2; + K1 = 0.048; + K2 = 0.014; + break; + default: + throw new ArgumentOutOfRangeException(nameof(application)); + } + } + + /// <param name="x">Reference color</param> + /// <param name="y">Sample color</param> + /// <returns>Delta-E (1994) color difference</returns> + public double ComputeDifference(in LabColor x, in LabColor y) + { + if (x.WhitePoint != y.WhitePoint) + throw new ArgumentException("Colors must have same white point to be compared."); + + var da = x.a - y.a; + var db = x.b - y.b; + var dL = x.L - y.L; + var C1 = Math.Sqrt(x.a * x.a + x.b * x.b); + var C2 = Math.Sqrt(y.a * y.a + y.b * y.b); + var dC = C1 - C2; + var dH_sq = da * da + db * db - dC * dC; // dH ^ 2 + const double SL = 1; + var SC = 1 + K1 * C1; + var SH = 1 + K2 * C1; + var dE94 = Math.Sqrt( + MathUtils.Pow2(dL / (KL * SL)) + + MathUtils.Pow2(dC / (KC * SC)) + + dH_sq / MathUtils.Pow2(KH * SH) + ); + return dE94; + } + } + + /// <summary> + /// Application area for CIE Delta-E 1994 + /// </summary> + public enum CIE94ColorDifferenceApplication + { + /// <summary> + /// Graphic arts + /// </summary> + GraphicArts, + + /// <summary> + /// Textiles + /// </summary> + Textiles + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Difference/CIEDE2000ColorDifference.cs b/Software/Visual_Studio/SideChains/Colourful/Difference/CIEDE2000ColorDifference.cs new file mode 100644 index 000000000..98d0a7b88 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Difference/CIEDE2000ColorDifference.cs @@ -0,0 +1,130 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Colourful.Implementation; + +namespace Colourful.Difference +{ + /// <summary> + /// CIE Delta-E 2000 formula + /// </summary> + public sealed class CIEDE2000ColorDifference : IColorDifference<LabColor> + { + // parametric weighting factors: + private const double k_H = 1; + private const double k_L = 1; + private const double k_C = 1; + + /// <param name="x">Reference color</param> + /// <param name="y">Sample color</param> + /// <remarks>Implemented according to: Sharma, Gaurav; Wencheng Wu, Edul N. Dalal (2005). "The CIEDE2000 color-difference formula: Implementation notes, supplementary test data, and mathematical observations" (http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf)</remarks> + /// <returns>Delta-E (2000) color difference</returns> + public double ComputeDifference(in LabColor x, in LabColor y) + { + if (x.WhitePoint != y.WhitePoint) + throw new ArgumentException("Colors must have same white point to be compared."); + + // 1. Calculate C_prime, h_prime + Calculate_a_prime(x.a, y.a, x.b, y.b, out var a_prime0, out var a_prime1); + Calculate_C_prime(a_prime0, a_prime1, x.b, y.b, out var C_prime0, out var C_prime1); + Calculate_h_prime(a_prime0, a_prime1, x.b, y.b, out var h_prime0, out var h_prime1); + + // 2. Calculate dL_prime, dC_prime, dH_prime + var dL_prime = y.L - x.L; // eq. (8) + var dC_prime = C_prime1 - C_prime0; // eq. (9) + var dh_prime = Calculate_dh_prime(C_prime0, C_prime1, h_prime0, h_prime1); + var dH_prime = 2 * Math.Sqrt(C_prime0 * C_prime1) * MathUtils.SinDeg(dh_prime / 2); // eq. (11) + + // 3. Calculate CIEDE2000 Color-Difference dE00 + var L_prime_mean = (x.L + y.L) / 2; // eq. (12) + var C_prime_mean = (C_prime0 + C_prime1) / 2; // eq. (13) + var h_prime_mean = Calculate_h_prime_mean(h_prime0, h_prime1, C_prime0, C_prime1); + var T = 1 - 0.17 * MathUtils.CosDeg(h_prime_mean - 30) + 0.24 * MathUtils.CosDeg(2 * h_prime_mean) + + 0.32 * MathUtils.CosDeg(3 * h_prime_mean + 6) - 0.20 * MathUtils.CosDeg(4 * h_prime_mean - 63); // eq. (15) + var dTheta = 30 * Math.Exp(-MathUtils.Pow2((h_prime_mean - 275) / 25)); // eq. (16) + var R_C = 2 * Math.Sqrt(MathUtils.Pow7(C_prime_mean) / (MathUtils.Pow7(C_prime_mean) + MathUtils.Pow7(25))); // eq. (17) + var S_L = 1 + 0.015 * MathUtils.Pow2(L_prime_mean - 50) / Math.Sqrt(20 + MathUtils.Pow2(L_prime_mean - 50)); // eq. (18) + var S_C = 1 + 0.045 * C_prime_mean; // eq. (19) + var S_H = 1 + 0.015 * C_prime_mean * T; // eq. (20) + var R_T = -MathUtils.SinDeg(2 * dTheta) * R_C; // eq. (21) + + var dE00 = Math.Sqrt( + MathUtils.Pow2(dL_prime / (k_L * S_L)) + + MathUtils.Pow2(dC_prime / (k_C * S_C)) + + MathUtils.Pow2(dH_prime / (k_H * S_H)) + + R_T * (dC_prime / (k_C * S_C)) * (dH_prime / (k_H * S_H)) + ); // eq. (22) + + return dE00; + } + + private static void Calculate_a_prime(double a0, double a1, double b0, double b1, out double a_prime0, out double a_prime1) + { + var C_ab0 = Math.Sqrt(a0 * a0 + b0 * b0); // eq. (2) + var C_ab1 = Math.Sqrt(a1 * a1 + b1 * b1); + + var C_ab_mean = (C_ab0 + C_ab1) / 2; // eq. (3) + + var G = 0.5d * (1 - Math.Sqrt(MathUtils.Pow7(C_ab_mean) / (MathUtils.Pow7(C_ab_mean) + MathUtils.Pow7(25)))); // eq. (4) + + a_prime0 = (1 + G) * a0; // eq. (5) + a_prime1 = (1 + G) * a1; + } + + private static void Calculate_C_prime(double a_prime0, double a_prime1, double b0, double b1, out double C_prime0, out double C_prime1) + { + C_prime0 = Math.Sqrt(a_prime0 * a_prime0 + b0 * b0); // eq. (6) + C_prime1 = Math.Sqrt(a_prime1 * a_prime1 + b1 * b1); + } + + private static void Calculate_h_prime(double a_prime0, double a_prime1, double b0, double b1, out double h_prime0, out double h_prime1) + { + // eq. (7) + var hRadians = Math.Atan2(b0, a_prime0); + var hDegrees = Angle.NormalizeDegree(Angle.RadianToDegree(hRadians)); + h_prime0 = hDegrees; + + hRadians = Math.Atan2(b1, a_prime1); + hDegrees = Angle.NormalizeDegree(Angle.RadianToDegree(hRadians)); + h_prime1 = hDegrees; + } + + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + private static double Calculate_dh_prime(double C_prime0, double C_prime1, double h_prime0, double h_prime1) + { + // eq. (10) + if (C_prime0 * C_prime1 == 0d) + return 0; + + if (Math.Abs(h_prime1 - h_prime0) <= 180) + return h_prime1 - h_prime0; + + if (h_prime1 - h_prime0 > 180) + return h_prime1 - h_prime0 - 360; + + if (h_prime1 - h_prime0 < -180) + return h_prime1 - h_prime0 + 360; + + return 0; + } + + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + private static double Calculate_h_prime_mean(double h_prime0, double h_prime1, double C_prime0, double C_prime1) + { + // eq. (14) + if (C_prime0 * C_prime1 == 0d) + return h_prime0 + h_prime1; + + if (Math.Abs(h_prime0 - h_prime1) <= 180) + return (h_prime0 + h_prime1) / 2; + + if (Math.Abs(h_prime0 - h_prime1) > 180 && + h_prime0 + h_prime1 < 360) + return (h_prime0 + h_prime1 + 360) / 2; + + if (Math.Abs(h_prime0 - h_prime1) > 180 && h_prime0 + h_prime1 >= 360) + return (h_prime0 + h_prime1 - 360) / 2; + + return h_prime0 + h_prime1; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Difference/CMCColorDifference.cs b/Software/Visual_Studio/SideChains/Colourful/Difference/CMCColorDifference.cs new file mode 100644 index 000000000..65663955e --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Difference/CMCColorDifference.cs @@ -0,0 +1,109 @@ +using System; +using Colourful.Implementation; + +namespace Colourful.Difference +{ + /// <summary> + /// CMC l:c (1984) color difference + /// </summary> + /// <remarks> + /// Equations: http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html + /// </remarks> + public sealed class CMCColorDifference : IColorDifference<LabColor> + { + /// <summary> + /// Chroma + /// </summary> + private readonly double _c; + + /// <summary> + /// Lightness + /// </summary> + private readonly double _l; + + /// <summary> + /// Constructs with given recommended threshold parameters. + /// </summary> + public CMCColorDifference(CMCColorDifferenceThreshold threshold) + { + switch (threshold) + { + case CMCColorDifferenceThreshold.Acceptability: + _l = 2; + _c = 1; + break; + case CMCColorDifferenceThreshold.Imperceptibility: + _l = 1; + _c = 1; + break; + default: + throw new ArgumentOutOfRangeException(nameof(threshold)); + } + } + + /// <summary> + /// Constructs with arbitrary threshold parameters. + /// </summary> + public CMCColorDifference(double lightness, double chroma) + { + _l = lightness; + _c = chroma; + } + + /// <inheritdoc /> + public double ComputeDifference(in LabColor x, in LabColor y) + { + double L1 = x.L, a1 = x.a, b1 = x.b; + double L2 = y.L, a2 = y.a, b2 = y.b; + + var dL = L1 - L2; + var da = a1 - a2; + var db = b1 - b2; + + var C1 = Math.Sqrt(a1 * a1 + b1 * b1); + var C2 = Math.Sqrt(a2 * a2 + b2 * b2); + var dC = C1 - C2; + + var dH_pow2 = da * da + db * db - dC * dC; + var H1_rad = Math.Atan2(b1, a1); + var H1 = Angle.NormalizeDegree(Angle.RadianToDegree(H1_rad)); + + var C1_pow4 = MathUtils.Pow4(C1); + var F = Math.Sqrt(C1_pow4 / (C1_pow4 + 1900)); + + var T = H1 >= 164 && H1 <= 345 + ? 0.56 + Math.Abs(0.2 * MathUtils.CosDeg(H1 + 168)) + : 0.36 + Math.Abs(0.4 * MathUtils.CosDeg(H1 + 35)); + + var SC = 0.0638 * C1 / (1 + 0.0131 * C1) + 0.638; + var SL = L1 < 16 + ? 0.511 + : 0.040975 * L1 / (1 + 0.01765 * L1); + + var SH = SC * (F * T + 1 - F); + + var dE_1 = dL / (_l * SL); + var dE_2 = dC / (_c * SC); + var dE_3_pow2 = dH_pow2 / (SH * SH); + + var dE = Math.Sqrt(dE_1 * dE_1 + dE_2 * dE_2 + dE_3_pow2); + return dE; + } + } + + /// <summary> + /// Weighting parameters for CMC l:c + /// </summary> + public enum CMCColorDifferenceThreshold + { + /// <summary> + /// 2:1 (l:c) + /// </summary> + Acceptability, + + /// <summary> + /// 1:1 (l:c) + /// </summary> + Imperceptibility + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Difference/IColorDifference.cs b/Software/Visual_Studio/SideChains/Colourful/Difference/IColorDifference.cs new file mode 100644 index 000000000..69b7837f8 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Difference/IColorDifference.cs @@ -0,0 +1,15 @@ +namespace Colourful.Difference +{ + /// <summary> + /// Computes distance between two vectors in color space + /// </summary> + /// <typeparam name="TColor"></typeparam> + public interface IColorDifference<TColor> + where TColor : struct + { + /// <summary> + /// Computes distance between color x and y. + /// </summary> + double ComputeDifference(in TColor x, in TColor y); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/CIEConstants.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/CIEConstants.cs new file mode 100644 index 000000000..e6ebb3666 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/CIEConstants.cs @@ -0,0 +1,8 @@ +namespace Colourful.Implementation.Conversion +{ + internal static class CIEConstants + { + public const double Epsilon = 216d / 24389d; + public const double Kappa = 24389d / 27d; + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/HunterLabToXYZConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/HunterLabToXYZConverter.cs new file mode 100644 index 000000000..238141b6b --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/HunterLabToXYZConverter.cs @@ -0,0 +1,50 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="HunterLabColor" /> to <see cref="XYZColor" />. + /// </summary> + public sealed class HunterLabToXYZConverter : XYZAndHunterLabConverterBase, IColorConversion<HunterLabColor, XYZColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly HunterLabToXYZConverter Default = new HunterLabToXYZConverter(); + + /// <summary> + /// Converts from <see cref="HunterLabColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in HunterLabColor input) + { + double L = input.L, a = input.a, b = input.b; + double Xn = input.WhitePoint.X, Yn = input.WhitePoint.Y, Zn = input.WhitePoint.Z; + + var Ka = ComputeKa(input.WhitePoint); + var Kb = ComputeKb(input.WhitePoint); + + var Y = MathUtils.Pow2(L / 100d) * Yn; + var X = (a / Ka * Math.Sqrt(Y / Yn) + Y / Yn) * Xn; + var Z = (b / Kb * Math.Sqrt(Y / Yn) - Y / Yn) * -Zn; + + var result = new XYZColor(X, Y, Z); + return result; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is HunterLabToXYZConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(HunterLabToXYZConverter left, HunterLabToXYZConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(HunterLabToXYZConverter left, HunterLabToXYZConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZAndHunterLabConverterBase.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZAndHunterLabConverterBase.cs new file mode 100644 index 000000000..2193ff75d --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZAndHunterLabConverterBase.cs @@ -0,0 +1,32 @@ +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Base class for converters between XYZ and Hunter Lab + /// </summary> + public abstract class XYZAndHunterLabConverterBase + { + /// <summary> + /// Computes the Ka parameter + /// </summary> + protected static double ComputeKa(XYZColor whitePoint) + { + if (whitePoint == Illuminants.C) + return 175; + + var Ka = 100 * (175 / 198.04) * (whitePoint.X + whitePoint.Y); + return Ka; + } + + /// <summary> + /// Computes the Kb parameter + /// </summary> + protected static double ComputeKb(XYZColor whitePoint) + { + if (whitePoint == Illuminants.C) + return 70; + + var Ka = 100 * (70 / 218.11) * (whitePoint.Y + whitePoint.Z); + return Ka; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZToHunterLabConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZToHunterLabConverter.cs new file mode 100644 index 000000000..d69899672 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/HunterLab/XYZToHunterLabConverter.cs @@ -0,0 +1,57 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="HunterLabColor" /> to <see cref="XYZColor" />. + /// </summary> + public sealed class XYZToHunterLabConverter : XYZAndHunterLabConverterBase, IColorConversion<XYZColor, HunterLabColor> + { + /// <summary> + /// Construct with <see cref="HunterLabColor.DefaultWhitePoint" /> + /// </summary> + public XYZToHunterLabConverter() + : this(HunterLabColor.DefaultWhitePoint) + { + } + + /// <summary> + /// Construct with arbitrary white point + /// </summary> + public XYZToHunterLabConverter(XYZColor labWhitePoint) + { + HunterLabWhitePoint = labWhitePoint; + } + + /// <summary> + /// Target reference white. When not set, <see cref="LabColor.DefaultWhitePoint" /> is used. + /// </summary> + public XYZColor HunterLabWhitePoint { get; } + + /// <summary> + /// Converts from <see cref="HunterLabColor" /> to <see cref="XYZColor" />. + /// </summary> + public HunterLabColor Convert(in XYZColor input) + { + // conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + double X = input.X, Y = input.Y, Z = input.Z; + double Xn = HunterLabWhitePoint.X, Yn = HunterLabWhitePoint.Y, Zn = HunterLabWhitePoint.Z; + + var Ka = ComputeKa(HunterLabWhitePoint); + var Kb = ComputeKb(HunterLabWhitePoint); + + var L = 100 * Math.Sqrt(Y / Yn); + var a = Ka * ((X / Xn - Y / Yn) / Math.Sqrt(Y / Yn)); + var b = Kb * ((Y / Yn - Z / Zn) / Math.Sqrt(Y / Yn)); + + if (double.IsNaN(a)) + a = 0; + + if (double.IsNaN(b)) + b = 0; + + var output = new HunterLabColor(L, a, b, HunterLabWhitePoint); + return output; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/IColorConversion.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/IColorConversion.cs new file mode 100644 index 000000000..eff56c438 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/IColorConversion.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts color between two color spaces. + /// </summary> + /// <typeparam name="TInput"></typeparam> + /// <typeparam name="TOutput"></typeparam> + [SuppressMessage("ReSharper", "TypeParameterCanBeVariant")] + public interface IColorConversion<TInput, TOutput> + where TInput : struct + where TOutput : struct + { + /// <summary> + /// Converts from the input color space to the output color space. + /// </summary> + TOutput Convert(in TInput input); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LChabToLabConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LChabToLabConverter.cs new file mode 100644 index 000000000..e607ae6e5 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LChabToLabConverter.cs @@ -0,0 +1,46 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LChabColor" /> to <see cref="LabColor" />. + /// </summary> + public sealed class LChabToLabConverter : IColorConversion<LChabColor, LabColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LChabToLabConverter Default = new LChabToLabConverter(); + + /// <summary> + /// Converts from <see cref="LChabColor" /> to <see cref="LabColor" />. + /// </summary> + public LabColor Convert(in LChabColor input) + { + double L = input.L, C = input.C, hDegrees = input.h; + var hRadians = Angle.DegreeToRadian(hDegrees); + + var a = C * Math.Cos(hRadians); + var b = C * Math.Sin(hRadians); + + var output = new LabColor(L, a, b, input.WhitePoint); + return output; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LChabToLabConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LChabToLabConverter left, LChabToLabConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LChabToLabConverter left, LChabToLabConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LabToLChabConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LabToLChabConverter.cs new file mode 100644 index 000000000..39bb80d0b --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChab/LabToLChabConverter.cs @@ -0,0 +1,45 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LabColor" /> to <see cref="LChabColor" />. + /// </summary> + public sealed class LabToLChabConverter : IColorConversion<LabColor, LChabColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LabToLChabConverter Default = new LabToLChabConverter(); + + /// <summary> + /// Converts from <see cref="LabColor" /> to <see cref="LChabColor" />. + /// </summary> + public LChabColor Convert(in LabColor input) + { + double L = input.L, a = input.a, b = input.b; + var C = Math.Sqrt(a * a + b * b); + var hRadians = Math.Atan2(b, a); + var hDegrees = Angle.NormalizeDegree(Angle.RadianToDegree(hRadians)); + + var output = new LChabColor(L, C, hDegrees, input.WhitePoint); + return output; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LabToLChabConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LabToLChabConverter left, LabToLChabConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LabToLChabConverter left, LabToLChabConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LChuvToLuvConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LChuvToLuvConverter.cs new file mode 100644 index 000000000..ef8c06d0b --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LChuvToLuvConverter.cs @@ -0,0 +1,46 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LChuvColor" /> to <see cref="LuvColor" />. + /// </summary> + public sealed class LChuvToLuvConverter : IColorConversion<LChuvColor, LuvColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LChuvToLuvConverter Default = new LChuvToLuvConverter(); + + /// <summary> + /// Converts from <see cref="LChuvColor" /> to <see cref="LuvColor" />. + /// </summary> + public LuvColor Convert(in LChuvColor input) + { + double L = input.L, C = input.C, hDegrees = input.h; + var hRadians = Angle.DegreeToRadian(hDegrees); + + var u = C * Math.Cos(hRadians); + var v = C * Math.Sin(hRadians); + + var output = new LuvColor(L, u, v, input.WhitePoint); + return output; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LChuvToLuvConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LChuvToLuvConverter left, LChuvToLuvConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LChuvToLuvConverter left, LChuvToLuvConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LuvToLChuvConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LuvToLChuvConverter.cs new file mode 100644 index 000000000..1cb75c1fb --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LChuv/LuvToLChuvConverter.cs @@ -0,0 +1,45 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LuvColor" /> to <see cref="LChuvColor" />. + /// </summary> + public sealed class LuvToLChuvConverter : IColorConversion<LuvColor, LChuvColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LuvToLChuvConverter Default = new LuvToLChuvConverter(); + + /// <summary> + /// Converts from <see cref="LuvColor" /> to <see cref="LChuvColor" />. + /// </summary> + public LChuvColor Convert(in LuvColor input) + { + double L = input.L, u = input.u, v = input.v; + var C = Math.Sqrt(u * u + v * v); + var hRadians = Math.Atan2(v, u); + var hDegrees = Angle.NormalizeDegree(Angle.RadianToDegree(hRadians)); + + var output = new LChuvColor(L, C, hDegrees, input.WhitePoint); + return output; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LuvToLChuvConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LuvToLChuvConverter left, LuvToLChuvConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LuvToLChuvConverter left, LuvToLChuvConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/LMSTransformationMatrix.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/LMSTransformationMatrix.cs new file mode 100644 index 000000000..8e439e81d --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/LMSTransformationMatrix.cs @@ -0,0 +1,84 @@ +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Matrix used for transformation from XYZ to LMS, defining the cone response domain. + /// Used in <see cref="Colourful.Conversion.IChromaticAdaptation" /> + /// </summary> + /// <remarks> + /// Matrix data obtained from: + /// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization + /// S. Bianco, R. Schettini + /// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy + /// http://www.ivl.disco.unimib.it/papers2003/CRA-CAT.pdf + /// </remarks> + public static class LMSTransformationMatrix + { + /// <summary> + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) + /// </summary> + public static readonly Matrix VonKriesHPEAdjusted = new Vector[] + { + new[] { 0.40024, 0.7076, -0.08081 }, + new[] { -0.2263, 1.16532, 0.0457 }, + new[] { 0, 0, 0.91822 }, + }; + + /// <summary> + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) + /// </summary> + public static readonly Matrix VonKriesHPE = new Vector[] + { + new[] { 0.3897, 0.6890, -0.0787 }, + new[] { -0.2298, 1.1834, 0.0464 }, + new[] { 0.0, 0.0, 1.0 }, + }; + + /// <summary> + /// XYZ scaling chromatic adaptation transform matrix + /// </summary> + public static readonly Matrix XYZScaling = MatrixFactory.CreateIdentity(3); + + /// <summary> + /// Bradford chromatic adaptation transform matrix (used in CMCCAT97) + /// </summary> + public static readonly Matrix Bradford = new Vector[] + { + new[] { 0.8951, 0.2664, -0.1614 }, + new[] { -0.7502, 1.7135, 0.0367 }, + new[] { 0.0389, -0.0685, 1.0296 }, + }; + + /// <summary> + /// Spectral sharpening and the Bradford transform + /// </summary> + public static readonly Matrix BradfordSharp = new Vector[] + { + new[] { 1.2694, -0.0988, -0.1706 }, + new[] { -0.8364, 1.8006, 0.0357 }, + new[] { 0.0297, -0.0315, 1.0018 }, + }; + + /// <summary> + /// CMCCAT2000 (fitted from all available color data sets) + /// </summary> + public static readonly Matrix CMCCAT2000 = new Vector[] + { + new[] { 0.7982, 0.3389, -0.1371 }, + new[] { -0.5918, 1.5512, 0.0406 }, + new[] { 0.0008, 0.239, 0.9753 }, + }; + + /// <summary> + /// CAT02 (optimized for minimizing CIELAB differences) + /// </summary> + public static readonly Matrix CAT02 = new Vector[] + { + new[] { 0.7328, 0.4296, -0.1624 }, + new[] { -0.7036, 1.6975, 0.0061 }, + new[] { 0.0030, 0.0136, 0.9834 }, + }; + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/XYZAndLMSConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/XYZAndLMSConverter.cs new file mode 100644 index 000000000..f8f7b8e49 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/LMS/XYZAndLMSConverter.cs @@ -0,0 +1,67 @@ +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LMSColor" /> and back. + /// </summary> + public sealed class XYZAndLMSConverter : IColorConversion<XYZColor, LMSColor>, IColorConversion<LMSColor, XYZColor> + { + /// <summary> + /// Default transformation matrix used, when no other is set. (Bradford) + /// <see cref="LMSTransformationMatrix" /> + /// </summary> + public static readonly Matrix DefaultTransformationMatrix = LMSTransformationMatrix.Bradford; + + private Matrix _transformationMatrix; + + private Matrix _transformationMatrixInverse; + + /// <summary> + /// Constructs with <see cref="DefaultTransformationMatrix" /> + /// </summary> + public XYZAndLMSConverter() : this(DefaultTransformationMatrix) + { + } + + /// <param name="transformationMatrix">Definition of the cone response domain (see <see cref="LMSTransformationMatrix" />), if not set <see cref="DefaultTransformationMatrix" /> will be used.</param> + public XYZAndLMSConverter(Matrix transformationMatrix) + { + TransformationMatrix = transformationMatrix; + } + + /// <summary> + /// Transformation matrix used for the conversion (definition of the cone response domain). + /// <see cref="LMSTransformationMatrix" /> + /// </summary> + public Matrix TransformationMatrix + { + get => _transformationMatrix; + internal set + { + _transformationMatrix = value; + _transformationMatrixInverse = TransformationMatrix.Inverse(); + } + } + + /// <summary> + /// Converts from <see cref="LMSColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in LMSColor input) + { + var outputVector = _transformationMatrixInverse.MultiplyBy(input.Vector); + var output = new XYZColor(outputVector); + return output; + } + + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LMSColor" />. + /// </summary> + public LMSColor Convert(in XYZColor input) + { + var outputVector = TransformationMatrix.MultiplyBy(input.Vector); + var output = new LMSColor(outputVector); + return output; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/LabToXYZConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/LabToXYZConverter.cs new file mode 100644 index 000000000..716195fc9 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/LabToXYZConverter.cs @@ -0,0 +1,62 @@ +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LabColor" /> to <see cref="XYZColor" />. + /// </summary> + public sealed class LabToXYZConverter : IColorConversion<LabColor, XYZColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LabToXYZConverter Default = new LabToXYZConverter(); + + /// <summary> + /// Converts from <see cref="LabColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in LabColor input) + { + // conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + double L = input.L, a = input.a, b = input.b; + var fy = (L + 16) / 116d; + var fx = a / 500d + fy; + var fz = fy - b / 200d; + + var fx3 = MathUtils.Pow3(fx); + var fz3 = MathUtils.Pow3(fz); + + var xr = fx3 > CIEConstants.Epsilon ? fx3 : (116 * fx - 16) / CIEConstants.Kappa; + var yr = L > CIEConstants.Kappa * CIEConstants.Epsilon ? MathUtils.Pow3((L + 16) / 116d) : L / CIEConstants.Kappa; + var zr = fz3 > CIEConstants.Epsilon ? fz3 : (116 * fz - 16) / CIEConstants.Kappa; + + double Xr = input.WhitePoint.X, Yr = input.WhitePoint.Y, Zr = input.WhitePoint.Z; + + // avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) + xr = xr.CropRange(0, 1); + yr = yr.CropRange(0, 1); + zr = zr.CropRange(0, 1); + + var X = xr * Xr; + var Y = yr * Yr; + var Z = zr * Zr; + + var result = new XYZColor(X, Y, Z); + return result; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LabToXYZConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LabToXYZConverter left, LabToXYZConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LabToXYZConverter left, LabToXYZConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/XYZToLabConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/XYZToLabConverter.cs new file mode 100644 index 000000000..4e9ee17c0 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Lab/XYZToLabConverter.cs @@ -0,0 +1,84 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LabColor" />. + /// </summary> + public sealed class XYZToLabConverter : IColorConversion<XYZColor, LabColor> + { + /// <summary> + /// Constructs with <see cref="LabColor.DefaultWhitePoint" /> + /// </summary> + public XYZToLabConverter() + : this(LabColor.DefaultWhitePoint) + { + } + + /// <summary> + /// Constructs with arbitrary white point + /// </summary> + public XYZToLabConverter(XYZColor labWhitePoint) + { + LabWhitePoint = labWhitePoint; + } + + /// <summary> + /// Target reference white. When not set, <see cref="LabColor.DefaultWhitePoint" /> is used. + /// </summary> + public XYZColor LabWhitePoint { get; } + + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LabColor" />. + /// </summary> + public LabColor Convert(in XYZColor input) + { + // conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html + double Xr = LabWhitePoint.X, Yr = LabWhitePoint.Y, Zr = LabWhitePoint.Z; + + double xr = input.X / Xr, yr = input.Y / Yr, zr = input.Z / Zr; + + var fx = f(xr); + var fy = f(yr); + var fz = f(zr); + + var L = 116 * fy - 16; + var a = 500 * (fx - fy); + var b = 200 * (fy - fz); + + var output = new LabColor(L, a, b, LabWhitePoint); + return output; + } + + private static double f(double cr) + { + var fc = cr > CIEConstants.Epsilon ? Math.Pow(cr, 1 / 3d) : (CIEConstants.Kappa * cr + 16) / 116d; + return fc; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(XYZToLabConverter other) + { + if (other == null) + return false; + + return ReferenceEquals(this, other) || LabWhitePoint.Equals(other.LabWhitePoint); + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is XYZToLabConverter other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => LabWhitePoint.GetHashCode(); + + /// <inheritdoc cref="object" /> + public static bool operator ==(XYZToLabConverter left, XYZToLabConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(XYZToLabConverter left, XYZToLabConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/LuvToXYZConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/LuvToXYZConverter.cs new file mode 100644 index 000000000..d6f3a02d1 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/LuvToXYZConverter.cs @@ -0,0 +1,69 @@ +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LuvColor" /> to <see cref="XYZColor" />. + /// </summary> + public sealed class LuvToXYZConverter : IColorConversion<LuvColor, XYZColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LuvToXYZConverter Default = new LuvToXYZConverter(); + + /// <summary> + /// Converts from <see cref="LuvColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in LuvColor input) + { + // conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html + double L = input.L, u = input.u, v = input.v; + + var u0 = Compute_u0(input.WhitePoint); + var v0 = Compute_v0(input.WhitePoint); + + var Y = L > CIEConstants.Kappa * CIEConstants.Epsilon + ? MathUtils.Pow3((L + 16) / 116) + : L / CIEConstants.Kappa; + + var a = (52 * L / (u + 13 * L * u0) - 1) / 3; + var b = -5 * Y; + var c = -1 / 3d; + var d = Y * (39 * L / (v + 13 * L * v0) - 5); + + var X = (d - b) / (a - c); + var Z = X * a + b; + + if (double.IsNaN(X) || X < 0) + X = 0; + + if (double.IsNaN(Y) || Y < 0) + Y = 0; + + if (double.IsNaN(Z) || Z < 0) + Z = 0; + + var result = new XYZColor(X, Y, Z); + return result; + } + + private static double Compute_u0(XYZColor input) => 4 * input.X / (input.X + 15 * input.Y + 3 * input.Z); + + private static double Compute_v0(XYZColor input) => 9 * input.Y / (input.X + 15 * input.Y + 3 * input.Z); + + #region Overrides + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LuvToXYZConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LuvToXYZConverter left, LuvToXYZConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LuvToXYZConverter left, LuvToXYZConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/XYZToLuvConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/XYZToLuvConverter.cs new file mode 100644 index 000000000..8ff59999f --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/Luv/XYZToLuvConverter.cs @@ -0,0 +1,90 @@ +using System; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LuvColor" />. + /// </summary> + public sealed class XYZToLuvConverter : IColorConversion<XYZColor, LuvColor> + { + /// <summary> + /// Constructs with <see cref="LuvColor.DefaultWhitePoint" /> + /// </summary> + public XYZToLuvConverter() + : this(LuvColor.DefaultWhitePoint) + { + } + + /// <summary> + /// Constructs with arbitrary white point + /// </summary> + public XYZToLuvConverter(XYZColor labWhitePoint) + { + LuvWhitePoint = labWhitePoint; + } + + /// <summary> + /// Target reference white. When not set, <see cref="LuvColor.DefaultWhitePoint" /> is used. + /// </summary> + public XYZColor LuvWhitePoint { get; } + + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LuvColor" />. + /// </summary> + public LuvColor Convert(in XYZColor input) + { + // conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html + + var yr = input.Y / LuvWhitePoint.Y; + var up = Compute_up(input); + var vp = Compute_vp(input); + var upr = Compute_up(LuvWhitePoint); + var vpr = Compute_vp(LuvWhitePoint); + + var L = yr > CIEConstants.Epsilon ? 116 * Math.Pow(yr, 1 / 3d) - 16 : CIEConstants.Kappa * yr; + + if (double.IsNaN(L) || L < 0) + L = 0; + + var u = 13 * L * (up - upr); + var v = 13 * L * (vp - vpr); + + if (double.IsNaN(u)) + u = 0; + + if (double.IsNaN(v)) + v = 0; + + return new LuvColor(L, u, v, LuvWhitePoint); + } + + private static double Compute_up(XYZColor input) => 4 * input.X / (input.X + 15 * input.Y + 3 * input.Z); + + private static double Compute_vp(XYZColor input) => 9 * input.Y / (input.X + 15 * input.Y + 3 * input.Z); + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(XYZToLuvConverter other) + { + if (other == null) + return false; + + return ReferenceEquals(this, other) || LuvWhitePoint.Equals(other.LuvWhitePoint); + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is XYZToLuvConverter other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => LuvWhitePoint.GetHashCode(); + + /// <inheritdoc cref="object" /> + public static bool operator ==(XYZToLuvConverter left, XYZToLuvConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(XYZToLuvConverter left, XYZToLuvConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBAndXYZConverterBase.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBAndXYZConverterBase.cs new file mode 100644 index 000000000..a873cfd39 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBAndXYZConverterBase.cs @@ -0,0 +1,70 @@ +using System; +using Vector = System.Collections.Generic.IReadOnlyList<double>; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Base class for conversions between <see cref="RGBColor" /> and <see cref="XYZColor" />. + /// </summary> + public abstract class LinearRGBAndXYZConverterBase + { + /// <summary> + /// Computes RGB/XYZ matrix + /// </summary> + protected static Matrix GetRGBToXYZMatrix(IRGBWorkingSpace workingSpace) + { + if (workingSpace == null) throw new ArgumentNullException(nameof(workingSpace)); + + // for more info, see: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + + var chromaticity = workingSpace.ChromaticityCoordinates; + double xr = chromaticity.R.x, + xg = chromaticity.G.x, + xb = chromaticity.B.x, + yr = chromaticity.R.y, + yg = chromaticity.G.y, + yb = chromaticity.B.y; + + var Xr = xr / yr; + const double Yr = 1; + var Zr = (1 - xr - yr) / yr; + + var Xg = xg / yg; + const double Yg = 1; + var Zg = (1 - xg - yg) / yg; + + var Xb = xb / yb; + const double Yb = 1; + var Zb = (1 - xb - yb) / yb; + + var S = new Vector[] + { + new[] { Xr, Xg, Xb }, + new[] { Yr, Yg, Yb }, + new[] { Zr, Zg, Zb }, + }.Inverse(); + + var W = workingSpace.WhitePoint.Vector; + + var SW = S.MultiplyBy(W); + var Sr = SW[0]; + var Sg = SW[1]; + var Sb = SW[2]; + + Matrix M = new Vector[] + { + new[] { Sr * Xr, Sg * Xg, Sb * Xb }, + new[] { Sr * Yr, Sg * Yg, Sb * Yb }, + new[] { Sr * Zr, Sg * Zg, Sb * Zb }, + }; + + return M; + } + + /// <summary> + /// Computes XYZ/RGB matrix + /// </summary> + protected static Matrix GetXYZToRGBMatrix(IRGBWorkingSpace workingSpace) => GetRGBToXYZMatrix(workingSpace).Inverse(); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToRGBConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToRGBConverter.cs new file mode 100644 index 000000000..b2b4cd650 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToRGBConverter.cs @@ -0,0 +1,59 @@ +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LinearRGBColor" /> to <see cref="RGBColor" />. + /// </summary> + public sealed class LinearRGBToRGBConverter : IColorConversion<LinearRGBColor, RGBColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly LinearRGBToRGBConverter Default = new LinearRGBToRGBConverter(); + + /// <summary> + /// Converts from <see cref="LinearRGBColor" /> to <see cref="RGBColor" />. + /// </summary> + public RGBColor Convert(in LinearRGBColor input) + { + var result = CompandVector(input.Vector, input.WorkingSpace); + return result; + } + + /// <summary> + /// Applying the working space companding function (<see cref="IRGBWorkingSpace.Companding" />) to uncompanded vector. + /// </summary> + private static RGBColor CompandVector(Vector uncompandedVector, IRGBWorkingSpace workingSpace) + { + var companding = workingSpace.Companding; + Vector compandedVector = new[] + { + companding.Companding(uncompandedVector[0]).CropRange(0, 1), + companding.Companding(uncompandedVector[1]).CropRange(0, 1), + companding.Companding(uncompandedVector[2]).CropRange(0, 1) + }; + var result = new RGBColor(compandedVector, workingSpace); + return result; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(LinearRGBToRGBConverter other) => other != null; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LinearRGBToRGBConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LinearRGBToRGBConverter left, LinearRGBToRGBConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LinearRGBToRGBConverter left, LinearRGBToRGBConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToXYZConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToXYZConverter.cs new file mode 100644 index 000000000..fda683888 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/LinearRGBToXYZConverter.cs @@ -0,0 +1,64 @@ +using System; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="LinearRGBColor" /> to <see cref="XYZColor" />. + /// </summary> + public sealed class LinearRGBToXYZConverter : LinearRGBAndXYZConverterBase, IColorConversion<LinearRGBColor, XYZColor> + { + private readonly Matrix _conversionMatrix; + + /// <param name="sourceRGBWorkingSpace">Source RGB working space</param> + public LinearRGBToXYZConverter(IRGBWorkingSpace sourceRGBWorkingSpace) + { + SourceRGBWorkingSpace = sourceRGBWorkingSpace; + _conversionMatrix = GetRGBToXYZMatrix(SourceRGBWorkingSpace); + } + + /// <summary> + /// Source RGB working space + /// </summary> + public IRGBWorkingSpace SourceRGBWorkingSpace { get; } + + /// <summary> + /// Converts from <see cref="LinearRGBColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in LinearRGBColor input) + { + if (!Equals(input.WorkingSpace, SourceRGBWorkingSpace)) + throw new InvalidOperationException("Working space of input RGB color must be equal to converter source RGB working space."); + + var xyz = _conversionMatrix.MultiplyBy(input.Vector); + + var converted = new XYZColor(xyz); + return converted; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(LinearRGBToXYZConverter other) + { + if (ReferenceEquals(this, other)) + return true; + + return Equals(SourceRGBWorkingSpace, other.SourceRGBWorkingSpace); + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LinearRGBToXYZConverter other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => SourceRGBWorkingSpace?.GetHashCode() ?? 0; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LinearRGBToXYZConverter left, LinearRGBToXYZConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LinearRGBToXYZConverter left, LinearRGBToXYZConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/RGBToLinearRGBConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/RGBToLinearRGBConverter.cs new file mode 100644 index 000000000..90122d1f8 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/RGBToLinearRGBConverter.cs @@ -0,0 +1,60 @@ +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="RGBColor" /> to <see cref="LinearRGBColor" />. + /// </summary> + public sealed class RGBToLinearRGBConverter : IColorConversion<RGBColor, LinearRGBColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly RGBToLinearRGBConverter Default = new RGBToLinearRGBConverter(); + + /// <summary> + /// Converts from <see cref="RGBColor" /> to <see cref="LinearRGBColor" />. + /// </summary> + public LinearRGBColor Convert(in RGBColor input) + { + var uncompandedVector = UncompandVector(input); + var converted = new LinearRGBColor(uncompandedVector, input.WorkingSpace); + return converted; + } + + /// <summary> + /// Applying the working space inverse companding function (<see cref="IRGBWorkingSpace.Companding" />) to RGB vector. + /// </summary> + private static Vector UncompandVector(in RGBColor rgbColor) + { + var companding = rgbColor.WorkingSpace.Companding; + var compandedVector = rgbColor.Vector; + Vector uncompandedVector = new[] + { + companding.InverseCompanding(compandedVector[0]).CropRange(0, 1), + companding.InverseCompanding(compandedVector[1]).CropRange(0, 1), + companding.InverseCompanding(compandedVector[2]).CropRange(0, 1) + }; + return uncompandedVector; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(RGBToLinearRGBConverter other) => other != null; + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is RGBToLinearRGBConverter; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(RGBToLinearRGBConverter left, RGBToLinearRGBConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(RGBToLinearRGBConverter left, RGBToLinearRGBConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/XYZToLinearRGBConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/XYZToLinearRGBConverter.cs new file mode 100644 index 000000000..9f3a548d2 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/RGB/XYZToLinearRGBConverter.cs @@ -0,0 +1,72 @@ +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LinearRGBColor" />. + /// </summary> + /// <remarks> + /// The target RGB working space is <see cref="RGBColor.DefaultWorkingSpace" /> when not set. + /// </remarks> + public sealed class XYZToLinearRGBConverter : LinearRGBAndXYZConverterBase, IColorConversion<XYZColor, LinearRGBColor> + { + private readonly Matrix _conversionMatrix; + + /// <summary> + /// Constructs with <see cref="RGBColor.DefaultWorkingSpace" />. + /// </summary> + public XYZToLinearRGBConverter() : this(null) + { + } + + /// <summary> + /// Constructs with arbitrary working space. + /// </summary> + public XYZToLinearRGBConverter(IRGBWorkingSpace targetRGBWorkingSpace) + { + TargetRGBWorkingSpace = targetRGBWorkingSpace ?? RGBColor.DefaultWorkingSpace; + _conversionMatrix = GetXYZToRGBMatrix(TargetRGBWorkingSpace); + } + + /// <summary> + /// Target RGB working space. When not set, target RGB working space is <see cref="RGBColor.DefaultWorkingSpace" />. + /// </summary> + public IRGBWorkingSpace TargetRGBWorkingSpace { get; } + + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="LinearRGBColor" />. + /// </summary> + public LinearRGBColor Convert(in XYZColor input) + { + var inputVector = input.Vector; + var uncompandedVector = _conversionMatrix.MultiplyBy(inputVector).CropRange(0, 1); + var result = new LinearRGBColor(uncompandedVector, TargetRGBWorkingSpace); + return result; + } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(XYZToLinearRGBConverter other) + { + if (other == null) + return false; + + return ReferenceEquals(this, other) || TargetRGBWorkingSpace.Equals(other.TargetRGBWorkingSpace); + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is XYZToLinearRGBConverter other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => TargetRGBWorkingSpace?.GetHashCode() ?? 0; + + /// <inheritdoc cref="object" /> + public static bool operator ==(XYZToLinearRGBConverter left, XYZToLinearRGBConverter right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(XYZToLinearRGBConverter left, XYZToLinearRGBConverter right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/xyY/xyYAndXYZConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/xyY/xyYAndXYZConverter.cs new file mode 100644 index 000000000..512e49f1e --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Conversion/xyY/xyYAndXYZConverter.cs @@ -0,0 +1,45 @@ +namespace Colourful.Implementation.Conversion +{ + /// <summary> + /// Converts from <see cref="xyYColor" /> to <see cref="XYZColor" /> and back. + /// </summary> + public sealed class xyYAndXYZConverter : IColorConversion<XYZColor, xyYColor>, IColorConversion<xyYColor, XYZColor> + { + /// <summary> + /// Default singleton instance of the converter. + /// </summary> + public static readonly xyYAndXYZConverter Default = new xyYAndXYZConverter(); + + /// <summary> + /// Converts from <see cref="xyYColor" /> to <see cref="XYZColor" />. + /// </summary> + public XYZColor Convert(in xyYColor input) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (input.y == 0) + return new XYZColor(0, 0, input.Luminance); + // ReSharper restore CompareOfFloatsByEqualityOperator + + var X = input.x * input.Luminance / input.y; + var Y = input.Luminance; + var Z = (1 - input.x - input.y) * Y / input.y; + + return new XYZColor(X, Y, Z); + } + + /// <summary> + /// Converts from <see cref="XYZColor" /> to <see cref="xyYColor" />. + /// </summary> + public xyYColor Convert(in XYZColor input) + { + var x = input.X / (input.X + input.Y + input.Z); + var y = input.Y / (input.X + input.Y + input.Z); + + if (double.IsNaN(x) || double.IsNaN(y)) + return new xyYColor(0, 0, input.Y); + + var Y = input.Y; + return new xyYColor(x, y, Y); + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/GammaCompanding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/GammaCompanding.cs new file mode 100644 index 000000000..5a64871b7 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/GammaCompanding.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Gamma companding + /// </summary> + /// <remarks> + /// For more info see: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + /// </remarks> + public sealed class GammaCompanding : ICompanding + { + /// <summary> + /// Constructs with given gamma + /// </summary> + public GammaCompanding(double gamma) + { + Gamma = gamma; + } + + /// <summary> + /// Gamma + /// </summary> + public double Gamma { get; } + + /// <inheritdoc /> + public double InverseCompanding(double channel) + { + var V = channel; + var v = Math.Pow(V, Gamma); + return v; + } + + /// <inheritdoc /> + public double Companding(double channel) + { + var v = channel; + var V = Math.Pow(v, 1 / Gamma); + return V; + } + + /// <inheritdoc cref="object" /> + [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] + public bool Equals(GammaCompanding other) + { + if (other == null) + return false; + + return Gamma == other.Gamma; + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is GammaCompanding other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => Gamma.GetHashCode(); + + /// <inheritdoc cref="object" /> + public static bool operator ==(GammaCompanding left, GammaCompanding right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(GammaCompanding left, GammaCompanding right) => !Equals(left, right); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/ICompanding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/ICompanding.cs new file mode 100644 index 000000000..f170307f1 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/ICompanding.cs @@ -0,0 +1,28 @@ +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Pair of companding functions for <see cref="IRGBWorkingSpace" />. + /// Used for conversion to XYZ and backwards. + /// See also: <seealso cref="IRGBWorkingSpace.Companding" /> + /// </summary> + public interface ICompanding + { + /// <summary> + /// Companded channel is made linear with respect to the energy. + /// </summary> + /// <remarks> + /// For more info see: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// </remarks> + double InverseCompanding(double channel); + + /// <summary> + /// Uncompanded channel (linear) is made nonlinear (depends on the RGB color system). + /// </summary> + /// <remarks> + /// For more info see: + /// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + /// </remarks> + double Companding(double channel); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/LCompanding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/LCompanding.cs new file mode 100644 index 000000000..9c8fd005d --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/LCompanding.cs @@ -0,0 +1,46 @@ +using System; + +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// L* companding + /// </summary> + /// <remarks> + /// For more info see: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + /// </remarks> + public sealed class LCompanding : ICompanding + { + private const double Kappa = 24389d / 27d; + private const double Epsilon = 216d / 24389d; + + /// <inheritdoc /> + public double InverseCompanding(double channel) + { + var V = channel; + var v = V <= 0.08 ? 100.0 * V / Kappa : Math.Pow((V + 0.16) / 1.16, 3.0); + return v; + } + + /// <inheritdoc /> + public double Companding(double channel) + { + var v = channel; + var V = v <= Epsilon ? v * Kappa / 100.0 : 1.16 * Math.Pow(v, 1.0 / 3.0) - 0.16; + return V; + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is LCompanding; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(LCompanding left, LCompanding right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(LCompanding left, LCompanding right) => !Equals(left, right); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBPrimariesChromaticityCoordinates.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBPrimariesChromaticityCoordinates.cs new file mode 100644 index 000000000..5b9ea5ff1 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBPrimariesChromaticityCoordinates.cs @@ -0,0 +1,61 @@ +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Chromaticity coordinates of RGB primaries. + /// One of the specifiers of <see cref="IRGBWorkingSpace" />. + /// </summary> + public readonly struct RGBPrimariesChromaticityCoordinates + { + /// <summary> + /// Constructs coordinates + /// </summary> + public RGBPrimariesChromaticityCoordinates(xyChromaticityCoordinates r, xyChromaticityCoordinates g, xyChromaticityCoordinates b) + { + R = r; + G = g; + B = b; + } + + /// <summary> + /// Red + /// </summary> + public xyChromaticityCoordinates R { get; } + + /// <summary> + /// Green + /// </summary> + public xyChromaticityCoordinates G { get; } + + /// <summary> + /// Blue + /// </summary> + public xyChromaticityCoordinates B { get; } + + /// <inheritdoc cref="object" /> + public bool Equals(RGBPrimariesChromaticityCoordinates other) => + R.Equals(other.R) && + G.Equals(other.G) && + B.Equals(other.B); + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is RGBPrimariesChromaticityCoordinates other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = R.GetHashCode(); + hashCode = (hashCode * 397) ^ G.GetHashCode(); + hashCode = (hashCode * 397) ^ B.GetHashCode(); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(RGBPrimariesChromaticityCoordinates left, RGBPrimariesChromaticityCoordinates right) => left.Equals(right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(RGBPrimariesChromaticityCoordinates left, RGBPrimariesChromaticityCoordinates right) => !left.Equals(right); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBWorkingSpace.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBWorkingSpace.cs new file mode 100644 index 000000000..98ee3f66d --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/RGBWorkingSpace.cs @@ -0,0 +1,72 @@ +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Trivial implementation of <see cref="IRGBWorkingSpace" /> + /// </summary> + public sealed class RGBWorkingSpace : IRGBWorkingSpace + { + /// <summary> + /// Constructs RGB working space using a reference white, companding, and chromacity coordinates. + /// </summary> + public RGBWorkingSpace(XYZColor referenceWhite, ICompanding companding, RGBPrimariesChromaticityCoordinates chromaticityCoordinates) + { + WhitePoint = referenceWhite; + Companding = companding; + ChromaticityCoordinates = chromaticityCoordinates; + } + + /// <summary> + /// Reference white point + /// </summary> + public XYZColor WhitePoint { get; } + + /// <summary> + /// Chromacity coordinates + /// </summary> + public RGBPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// <summary> + /// Companding + /// </summary> + public ICompanding Companding { get; } + + #region Overrides + + /// <inheritdoc cref="object" /> + public bool Equals(IRGBWorkingSpace other) + { + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return Equals(WhitePoint, other.WhitePoint) + && ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) + && Companding.Equals(other.Companding); + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is IRGBWorkingSpace other && Equals(other); + + /// <inheritdoc cref="object" /> + public override int GetHashCode() + { + unchecked + { + var hashCode = WhitePoint.GetHashCode(); + hashCode = (hashCode * 397) ^ ChromaticityCoordinates.GetHashCode(); + hashCode = (hashCode * 397) ^ (Companding != null ? Companding.GetHashCode() : 0); + return hashCode; + } + } + + /// <inheritdoc cref="object" /> + public static bool operator ==(RGBWorkingSpace left, RGBWorkingSpace right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(RGBWorkingSpace left, RGBWorkingSpace right) => !Equals(left, right); + + #endregion + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec2020Companding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec2020Companding.cs new file mode 100644 index 000000000..eb6765824 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec2020Companding.cs @@ -0,0 +1,34 @@ +using System; + +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Rec. 2020 companding function (for 12-bit). + /// </summary> + /// <remarks> + /// http://en.wikipedia.org/wiki/Rec._2020 + /// For 10-bits, companding is identical to <see cref="Colourful.Implementation.RGB.Rec709Companding" /> + /// </remarks> + public sealed class Rec2020Companding : ICompanding + { + private const double Alpha = 1.09929682680944; + private const double Beta = 0.018053968510807; + private const double InverseBeta = Beta * 4.5; + + /// <inheritdoc /> + public double InverseCompanding(double channel) + { + var V = channel; + var L = V < InverseBeta ? V / 4.5 : Math.Pow((V + Alpha - 1.0) / Alpha, 1 / 0.45); + return L; + } + + /// <inheritdoc /> + public double Companding(double channel) + { + var L = channel; + var V = L < Beta ? 4.5 * L : Alpha * Math.Pow(L, 0.45) - (Alpha - 1.0); + return V; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec709Companding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec709Companding.cs new file mode 100644 index 000000000..48c5b511e --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/Rec709Companding.cs @@ -0,0 +1,29 @@ +using System; + +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// Rec. 709 companding function + /// </summary> + /// <remarks> + /// http://en.wikipedia.org/wiki/Rec._709 + /// </remarks> + public sealed class Rec709Companding : ICompanding + { + /// <inheritdoc /> + public double InverseCompanding(double channel) + { + var V = channel; + var L = V < 0.081 ? V / 4.5 : Math.Pow((V + 0.099) / 1.099, 1 / 0.45); + return L; + } + + /// <inheritdoc /> + public double Companding(double channel) + { + var L = channel; + var V = L < 0.018 ? 4.5 * L : 1.099 * Math.Pow(L, 0.45) - 0.099; + return V; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/sRGBCompanding.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/sRGBCompanding.cs new file mode 100644 index 000000000..c3689f14e --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/RGB/sRGBCompanding.cs @@ -0,0 +1,43 @@ +using System; + +namespace Colourful.Implementation.RGB +{ + /// <summary> + /// sRGB companding + /// </summary> + /// <remarks> + /// For more info see: + /// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html + /// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + /// </remarks> + public sealed class sRGBCompanding : ICompanding + { + /// <inheritdoc /> + public double InverseCompanding(double channel) + { + var V = channel; + var v = V <= 0.04045 ? V / 12.92 : Math.Pow((V + 0.055) / 1.055, 2.4); + return v; + } + + /// <inheritdoc /> + public double Companding(double channel) + { + var v = channel; + var V = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.Pow(v, 1 / 2.4d) - 0.055; + return V; + } + + /// <inheritdoc cref="object" /> + public override bool Equals(object obj) => obj is sRGBCompanding; + + /// <inheritdoc cref="object" /> + public override int GetHashCode() => 1; + + /// <inheritdoc cref="object" /> + public static bool operator ==(sRGBCompanding left, sRGBCompanding right) => Equals(left, right); + + /// <inheritdoc cref="object" /> + public static bool operator !=(sRGBCompanding left, sRGBCompanding right) => !Equals(left, right); + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Angle.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Angle.cs new file mode 100644 index 000000000..a50fbe7c5 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Angle.cs @@ -0,0 +1,30 @@ +using System; + +namespace Colourful.Implementation +{ + /// <summary> + /// Angle unit conversion helpers + /// </summary> + internal static class Angle + { + private const double TwoPI = 2 * Math.PI; + + public static double RadianToDegree(double rad) + { + var deg = 360 * (rad / TwoPI); + return deg; + } + + public static double DegreeToRadian(double deg) + { + var rad = TwoPI * (deg / 360d); + return rad; + } + + public static double NormalizeDegree(double deg) + { + var d = deg % 360d; + return d >= 0 ? d : d + 360d; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Extensions.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Extensions.cs new file mode 100644 index 000000000..87a231dc3 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/Extensions.cs @@ -0,0 +1,125 @@ +using System; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; +using Vector = System.Collections.Generic.IReadOnlyList<double>; + +namespace Colourful.Implementation +{ + internal static class Extensions + { + public static double CheckRange(this double value, double min, double max) + { + if (value < min) + throw new ArgumentOutOfRangeException(nameof(value), value, "The minimum value is " + min); + + if (value > max) + throw new ArgumentOutOfRangeException(nameof(value), value, "The maximum value is " + max); + + return value; + } + + public static double CropRange(this double value, double min, double max) + { + if (value < min) + return min; + + if (value > max) + return max; + + return value; + } + + public static Vector CropRange(this Vector vector, double min, double max) + { + var croppedVector = new double[vector.Count]; + + for (var i = 0; i < vector.Count; i++) + { + if (vector[i] < min) + { + croppedVector[i] = min; + } + else if (vector[i] > max) + { + croppedVector[i] = max; + } + else + { + croppedVector[i] = vector[i]; + } + } + + // ReSharper disable once CoVariantArrayConversion + return croppedVector; + } + + /// <summary> + /// Matrix inverse for 3 by 3 matrices + /// </summary> + /// <param name="matrix"></param> + /// <returns></returns> + public static Matrix Inverse(this Matrix matrix) + { + if (matrix.Count != 3 || matrix[0].Count != 3) + throw new ArgumentOutOfRangeException(nameof(matrix), "Inversion is supported only on 3 by 3 matrices."); + + var A = matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1]; + var D = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1]); + var G = matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1]; + var B = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0]); + var E = matrix[0][0] * matrix[2][2] - matrix[0][2] * matrix[2][0]; + var H = -(matrix[0][0] * matrix[1][2] - matrix[0][2] * matrix[1][0]); + var C = matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]; + var F = -(matrix[0][0] * matrix[2][1] - matrix[0][1] * matrix[2][0]); + var I = matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]; + var det = matrix[0][0] * A + matrix[0][1] * B + matrix[0][2] * C; + Matrix result = new Vector[] + { + new[] { A / det, D / det, G / det }, + new[] { B / det, E / det, H / det }, + new[] { C / det, F / det, I / det }, + }; + return result; + } + + public static Vector MultiplyBy(this Matrix matrix, Vector vector) + { + if (matrix[0].Count != vector.Count) + throw new ArgumentOutOfRangeException(nameof(matrix), "Non-conformable matrices and vectors cannot be multiplied."); + + var result = new double[matrix.Count]; + + for (var i = 0; i < matrix.Count; ++i) // each row of matrix + { + for (var k = 0; k < vector.Count; ++k) // each element of vector + { + result[i] += matrix[i][k] * vector[k]; + } + } + + // ReSharper disable once CoVariantArrayConversion + return result; + } + + public static Matrix MultiplyBy(this Matrix matrix1, Matrix matrix2) + { + if (matrix1[0].Count != matrix2.Count) + throw new ArgumentOutOfRangeException(nameof(matrix1), "Non-conformable matrices cannot be multiplied."); + + var result = MatrixFactory.CreateEmpty(matrix1.Count, matrix2[0].Count); + + for (var i = 0; i < matrix1.Count; ++i) // each row of 1 + { + for (var j = 0; j < matrix2[0].Count; ++j) // each column of 2 + { + for (var k = 0; k < matrix1[0].Count; ++k) + { + result[i][j] += matrix1[i][k] * matrix2[k][j]; + } + } + } + + // ReSharper disable once CoVariantArrayConversion + return result; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MathUtils.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MathUtils.cs new file mode 100644 index 000000000..dfe396808 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MathUtils.cs @@ -0,0 +1,62 @@ +using System; + +namespace Colourful.Implementation +{ + /// <summary> + /// Math helper functions + /// </summary> + internal static class MathUtils + { + /// <summary> + /// Compute x^2 + /// </summary> + /// <param name="x">Base</param> + /// <returns>Result of the exponentiation</returns> + public static double Pow2(double x) => x * x; + + /// <summary> + /// Compute x^3 + /// </summary> + /// <param name="x">Base</param> + /// <returns>Result of the exponentiation</returns> + public static double Pow3(double x) => x * x * x; + + /// <summary> + /// Compute x^4 + /// </summary> + /// <param name="x">Base</param> + /// <returns>Result of the exponentiation</returns> + public static double Pow4(double x) => x * x * (x * x); + + /// <summary> + /// Compute x^7 + /// </summary> + /// <param name="x">Base</param> + /// <returns>Result of the exponentiation</returns> + public static double Pow7(double x) => x * x * x * (x * x * x) * x; + + /// <summary> + /// Compute sine of angle in degrees + /// </summary> + /// <param name="x">Given angle</param> + /// <returns></returns> + public static double SinDeg(double x) + { + var x_rad = Angle.DegreeToRadian(x); + var y = Math.Sin(x_rad); + return y; + } + + /// <summary> + /// Compute cosine of angle in degrees + /// </summary> + /// <param name="x">Given angle</param> + /// <returns></returns> + public static double CosDeg(double x) + { + var x_rad = Angle.DegreeToRadian(x); + var y = Math.Cos(x_rad); + return y; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MatrixFactory.cs b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MatrixFactory.cs new file mode 100644 index 000000000..c80d80dc4 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Implementation/Utils/MatrixFactory.cs @@ -0,0 +1,46 @@ +using Vector = System.Collections.Generic.IReadOnlyList<double>; +using Matrix = System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<double>>; + +namespace Colourful.Implementation +{ + internal static class MatrixFactory + { + public static double[][] CreateEmpty(int rows, int columns) + { + var result = new double[rows][]; + for (var i = 0; i < rows; i++) + { + result[i] = new double[columns]; + } + + return result; + } + + public static Matrix CreateIdentity(int size) + { + var result = new double[size][]; + for (var i = 0; i < size; i++) + { + result[i] = new double[size]; + result[i][i] = 1; + } + + // ReSharper disable once CoVariantArrayConversion + return result; + } + + public static Matrix CreateDiagonal(params double[] items) + { + var size = items.Length; + var result = new double[size][]; + for (var i = 0; i < size; i++) + { + result[i] = new double[size]; + result[i][i] = items[i]; + } + + // ReSharper disable once CoVariantArrayConversion + return result; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Other/CCTConverter.cs b/Software/Visual_Studio/SideChains/Colourful/Other/CCTConverter.cs new file mode 100644 index 000000000..0a7c8939f --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Other/CCTConverter.cs @@ -0,0 +1,63 @@ +using System; +using Colourful.Implementation; + +namespace Colourful +{ + /// <summary> + /// Can compute chromaticity from CCT (Correlated color temperature) and also + /// compute CCT of given chromaticity. + /// </summary> + public static class CCTConverter + { + /// <summary> + /// Returns chromaticity coordinates of given CCT (specified in K) + /// </summary> + public static xyChromaticityCoordinates GetChromaticityOfCCT(double temperature) + { + // approximation described here: http://en.wikipedia.org/wiki/Planckian_locus#Approximation + + double x_c; + + if (temperature <= 4000) // correctly 1667 <= T <= 4000 + x_c = -0.2661239 * (1000000000 / MathUtils.Pow3(temperature)) - 0.2343580 * (1000000 / MathUtils.Pow2(temperature)) + 0.8776956 * (1000 / temperature) + 0.179910; + + else // correctly 4000 <= T <= 25000 + x_c = -3.0258469 * (1000000000 / MathUtils.Pow3(temperature)) + 2.1070379 * (1000000 / MathUtils.Pow2(temperature)) + 0.2226347 * (1000 / temperature) + 0.240390; + + double y_c; + + if (temperature <= 2222) // correctly 1667 <= T <= 2222 + y_c = -1.1063814 * MathUtils.Pow3(x_c) - 1.34811020 * MathUtils.Pow2(x_c) + 2.18555832 * x_c - 0.20219683; + + else if (temperature <= 4000) // correctly 2222 <= T <= 4000 + y_c = -0.9549476 * MathUtils.Pow3(x_c) - 1.37418593 * MathUtils.Pow2(x_c) + 2.09137015 * x_c - 0.16748867; + + else // correctly 4000 <= T <= 25000 + y_c = +3.0817580 * MathUtils.Pow3(x_c) - 5.87338670 * MathUtils.Pow2(x_c) + 3.75112997 * x_c - 0.37001483; + + return new xyChromaticityCoordinates(x_c, y_c); + } + + /// <summary> + /// Returns CCT (specified in K) of given chromaticity coordinates + /// </summary> + /// <remarks>Ranges usually from around 0 to 25000</remarks> + public static double GetCCTOfChromaticity(in xyChromaticityCoordinates chromaticity) + { + // approximation described here: http://en.wikipedia.org/wiki/Color_temperature#Approximation + + const double xe = 0.3366; + const double ye = 0.1735; + const double A0 = -949.86315; + const double A1 = 6253.80338; + const double t1 = 0.92159; + const double A2 = 28.70599; + const double t2 = 0.20039; + const double A3 = 0.00004; + const double t3 = 0.07125; + var n = (chromaticity.x - xe) / (chromaticity.y - ye); + var cct = A0 + A1 * Math.Exp(-n / t1) + A2 * Math.Exp(-n / t2) + A3 * Math.Exp(-n / t3); + return cct; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Other/SaturationLChFormulas.cs b/Software/Visual_Studio/SideChains/Colourful/Other/SaturationLChFormulas.cs new file mode 100644 index 000000000..24ecb4d20 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Other/SaturationLChFormulas.cs @@ -0,0 +1,30 @@ +namespace Colourful +{ + /// <summary> + /// Extensions useful for <see cref="LChabColor" /> and <see cref="LChuvColor" /> color spaces. + /// </summary> + internal static class SaturationLChFormulas + { + /// <summary> + /// Returns saturation of the color (chroma normalized by lightness) + /// </summary> + public static double GetSaturation(double L, double C) + { + var result = 100 * (C / L); + + if (double.IsNaN(result)) + return 0; + + return result; + } + + /// <summary> + /// Gets chroma from saturation and lightness + /// </summary> + public static double GetChroma(double saturation, double L) + { + var result = L * (saturation / 100); + return result; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/SideChains/Colourful/Properties/AssemblyInfo.cs b/Software/Visual_Studio/SideChains/Colourful/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d01c7b045 --- /dev/null +++ b/Software/Visual_Studio/SideChains/Colourful/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: Guid("d11f6be9-3dcb-45b7-a076-4d476236c3cb")]
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.BL/Entities/BrushStop.cs b/Software/Visual_Studio/Tango.BL/Entities/BrushStop.cs index 2fd65cdd4..adc7629b1 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/BrushStop.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/BrushStop.cs @@ -206,7 +206,7 @@ namespace Tango.BL.Entities _yellow = cmyk.Y; _black = cmyk.K; - Lab lab = rgb.To<Lab>(); + Lab lab = rgb.ToLabUsingColorful(); _l = lab.L; _a = lab.A; _b = lab.B; @@ -589,14 +589,14 @@ namespace Tango.BL.Entities { case ColorSpaces.RGB: cmyk = rgb.To<Cmyk>(); - lab = rgb.To<Lab>(); + lab = rgb.ToLabUsingColorful(); break; case ColorSpaces.CMYK: rgb = cmyk.To<Rgb>(); lab = cmyk.To<Lab>(); break; case ColorSpaces.LAB: - rgb = lab.To<Rgb>(); + rgb = lab.ToRgbUsingColorful(); cmyk = lab.To<Cmyk>(); break; case ColorSpaces.Catalog: diff --git a/Software/Visual_Studio/Tango.BL/ExtensionMethods/ColorMineExtensions.cs b/Software/Visual_Studio/Tango.BL/ExtensionMethods/ColorMineExtensions.cs new file mode 100644 index 000000000..de8c7f69b --- /dev/null +++ b/Software/Visual_Studio/Tango.BL/ExtensionMethods/ColorMineExtensions.cs @@ -0,0 +1,29 @@ +using ColorMine.ColorSpaces; +using Colourful; +using Colourful.Conversion; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +public static class ColorMineExtensions +{ + public static Rgb ToRgbUsingColorful(this Lab lab) + { + LabColor labColor = new LabColor(lab.L, lab.A, lab.B); + var converter = new ColourfulConverter { WhitePoint = Illuminants.D65 }; + var output = converter.ToRGB(labColor); + var color = output.ToColor(); + return new Rgb(color.R, color.G, color.B); + } + + public static Lab ToLabUsingColorful(this Rgb rgb) + { + RGBColor rgbColor = RGBColor.FromRGB8bit((byte)rgb.R, (byte)rgb.G, (byte)rgb.B); + var converter = new ColourfulConverter { WhitePoint = Illuminants.D65 }; + var output = converter.ToLab(rgbColor); + return new Lab(output.L, output.a, output.b); + } +} + diff --git a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj index b036df6dd..b7341d589 100644 --- a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj +++ b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj @@ -450,6 +450,7 @@ <Compile Include="Enumerations\TangoUpdateStatuses.cs" /> <Compile Include="Enumerations\RmlQualifications.cs" /> <Compile Include="ExtensionMethods\ColorCatalogItemsExtensions.cs" /> + <Compile Include="ExtensionMethods\ColorMineExtensions.cs" /> <Compile Include="Helpers\EventTypeTextConverter.cs" /> <Compile Include="Helpers\SegmentsCsvHelper.cs" /> <Compile Include="IObservableEntityDTO.cs" /> @@ -622,6 +623,10 @@ </None> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\..\..\..\..\Users\Roy\Desktop\Colourful-master\src\Colourful\Colourful.csproj"> + <Project>{1254c6b8-dedb-45f9-a880-04556ea1aa1c}</Project> + <Name>Colourful</Name> + </ProjectReference> <ProjectReference Include="..\DataStore\Tango.DataStore\Tango.DataStore.csproj"> <Project>{e0364dfa-0721-4637-9d32-9d22aac109d6}</Project> <Name>Tango.DataStore</Name> @@ -674,7 +679,7 @@ </Target> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> + <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.sln b/Software/Visual_Studio/Tango.sln index cc1eaf6fd..997e6a361 100644 --- a/Software/Visual_Studio/Tango.sln +++ b/Software/Visual_Studio/Tango.sln @@ -469,6 +469,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.BitTypesGenerator.CLI EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.MachineStudio.MachineManager", "MachineStudio\Modules\Tango.MachineStudio.MachineManager\Tango.MachineStudio.MachineManager.csproj", "{6C784296-CCF9-469C-A7C1-4C13305E1203}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colourful", "..\..\..\..\..\Users\Roy\Desktop\Colourful-master\src\Colourful\Colourful.csproj", "{1254C6B8-DEDB-45F9-A880-04556EA1AA1C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -4464,6 +4466,26 @@ Global {6C784296-CCF9-469C-A7C1-4C13305E1203}.Release|x64.Build.0 = Release|Any CPU {6C784296-CCF9-469C-A7C1-4C13305E1203}.Release|x86.ActiveCfg = Release|Any CPU {6C784296-CCF9-469C-A7C1-4C13305E1203}.Release|x86.Build.0 = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|ARM.Build.0 = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|ARM64.Build.0 = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|x64.ActiveCfg = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|x64.Build.0 = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Debug|x86.Build.0 = Debug|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|Any CPU.Build.0 = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|ARM.ActiveCfg = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|ARM.Build.0 = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|ARM64.ActiveCfg = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|ARM64.Build.0 = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|x64.ActiveCfg = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|x64.Build.0 = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|x86.ActiveCfg = Release|Any CPU + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4630,14 +4652,15 @@ Global {6B13E186-ADE2-4D97-9643-8132E00FC207} = {8336A702-9C49-4C9E-ADCC-1886A666D3BD} {B356201F-F958-4AC9-BBEB-E4EAE9DA9EC6} = {5F6BBAA8-EAD0-4B18-97E5-55B4F56DD760} {6C784296-CCF9-469C-A7C1-4C13305E1203} = {B2AF4F3F-2828-47C3-8F3E-A0EA0BD66FF8} + {1254C6B8-DEDB-45F9-A880-04556EA1AA1C} = {EC62BC9C-F2FE-4333-B7E4-110E38D43958} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7986F7F4-A86A-4994-B1B6-0988D7F057B6} - BuildVersion_BuildVersioningStyle = None.None.Increment.DeltaBaseYearDayOfYear - BuildVersion_UpdateAssemblyVersion = True - BuildVersion_UpdateFileVersion = False - BuildVersion_StartDate = 2000/1/1 - BuildVersion_AssemblyInfoFilename = Properties\AssemblyInfo.cs BuildVersion_UseGlobalSettings = False + BuildVersion_AssemblyInfoFilename = Properties\AssemblyInfo.cs + BuildVersion_StartDate = 2000/1/1 + BuildVersion_UpdateFileVersion = False + BuildVersion_UpdateAssemblyVersion = True + BuildVersion_BuildVersioningStyle = None.None.Increment.DeltaBaseYearDayOfYear + SolutionGuid = {7986F7F4-A86A-4994-B1B6-0988D7F057B6} EndGlobalSection EndGlobal |
