From deda1dd3a3d81fad0b11bfe052764b165d2eb2a6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 Mar 2017 00:05:56 +1100 Subject: [PATCH] Add HunterLab plus more conversions --- .../Conversion/ColorSpaceConverter.CieLab.cs | 52 +++++ .../Conversion/ColorSpaceConverter.CieXyz.cs | 23 +++ .../ColorSpaceConverter.HunterLab.cs | 85 ++++++++ .../ColorSpaceConverter.LinearRgb.cs | 39 ++++ .../Conversion/ColorSpaceConverter.Lms.cs | 52 +++++ .../Conversion/ColorSpaceConverter.Rgb.cs | 39 ++++ .../Spaces/Conversion/ColorSpaceConverter.cs | 7 + .../CieXyzAndHunterLabConverterBase.cs | 51 +++++ .../HunterLab/CieXyzToHunterLabConverter.cs | 66 ++++++ .../HunterLab/HunterLabToCieXyzConverter.cs | 34 ++++ src/ImageSharp/Colors/Spaces/HunterLab.cs | 190 ++++++++++++++++++ src/ImageSharp/Colors/Spaces/LinearRgb.cs | 2 +- src/ImageSharp/Colors/Spaces/Rgb.cs | 2 +- .../Color/ColorspaceCieXyzToCieLabConvert.cs | 34 ++++ .../ColorspaceCieXyzToHunterLabConvert.cs | 35 ++++ .../Color/ColorspaceCieXyzToLmsConvert.cs | 3 +- .../Color/ColorspaceCieXyzToRgbConvert.cs | 3 +- tests/ImageSharp.Tests/App.config | 6 - .../CieXyzAndCieLabConversionTest - Copy.cs | 73 +++++++ .../CieXyzAndCieLabConversionTest.cs | 64 +++--- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 5 + tests/ImageSharp.Tests/xunit.runner.json | 3 + 22 files changed, 829 insertions(+), 39 deletions(-) create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs create mode 100644 src/ImageSharp/Colors/Spaces/HunterLab.cs create mode 100644 tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs create mode 100644 tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs delete mode 100644 tests/ImageSharp.Tests/App.config create mode 100644 tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest - Copy.cs create mode 100644 tests/ImageSharp.Tests/xunit.runner.json diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs index 17b57e4c70..7b0b69a68c 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -31,5 +31,57 @@ namespace ImageSharp.Colors.Spaces.Conversion CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); return converter.Convert(adapted); } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(Rgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs index 01a9680611..ab42c10433 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Colors.Spaces.Conversion { using ImageSharp.Colors.Spaces; using ImageSharp.Colors.Spaces.Conversion.Implementation.CieLab; + using ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab; using ImageSharp.Colors.Spaces.Conversion.Implementation.Rgb; /// @@ -16,6 +17,8 @@ namespace ImageSharp.Colors.Spaces.Conversion { private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); + private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); + private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; /// @@ -84,6 +87,26 @@ namespace ImageSharp.Colors.Spaces.Conversion : this.Adapt(unadapted, color.WorkingSpace.WhitePoint); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieXyz ToCieXyz(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + // Conversion + CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); + + // Adaptation + CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed + ? unadapted + : this.Adapt(unadapted, color.WhitePoint); + + return adapted; + } + /// /// Gets the correct converter for the given rgb working space. /// diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs new file mode 100644 index 0000000000..630b1cfbeb --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -0,0 +1,85 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion +{ + using ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab; + + /// + /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. + /// + public partial class ColorSpaceConverter + { + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + // Adaptation + CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed + ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) + : color; + + // Conversion + return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(Rgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 3724efaf2c..41cba6f67a 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -48,6 +48,45 @@ namespace ImageSharp.Colors.Spaces.Conversion return xyzConverter.Convert(adapted); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public LinearRgb ToLinearRGB(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public LinearRgb ToLinearRGB(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public LinearRgb ToLinearRGB(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); + } + /// /// Gets the correct converter for the given rgb working space. /// diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs index 25f5941772..c53e999ecf 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs @@ -24,5 +24,57 @@ namespace ImageSharp.Colors.Spaces.Conversion // Conversion return this.cachedCieXyzAndLmsConverter.Convert(color); } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(Rgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs index b6a7dd86d4..c82e554e49 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -42,5 +42,44 @@ namespace ImageSharp.Colors.Spaces.Conversion // Compand return this.ToRgb(linear); } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Rgb ToRgb(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Rgb ToRgb(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Rgb ToRgb(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs index 459a4f474a..8727c4e6a3 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs @@ -33,6 +33,7 @@ namespace ImageSharp.Colors.Spaces.Conversion this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix; this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter, this.cachedCieXyzAndLmsConverter); this.TargetLabWhitePoint = CieLab.DefaultWhitePoint; + this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint; this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace; } @@ -48,6 +49,12 @@ namespace ImageSharp.Colors.Spaces.Conversion /// public CieXyz TargetLabWhitePoint { get; set; } + /// + /// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) + /// Defaults to: . + /// + public CieXyz TargetHunterLabWhitePoint { get; set; } + /// /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) /// Defaults to: . diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs new file mode 100644 index 0000000000..aa8851cfca --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab +{ + using System.Runtime.CompilerServices; + + /// + /// The base class for converting between and color spaces. + /// + internal abstract class CieXyzAndHunterLabConverterBase + { + /// + /// Returns the Ka coefficient that depends upon the whitepoint illuminant. + /// + /// The whitepoint + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ComputeKa(CieXyz whitePoint) + { + DebugGuard.NotNull(whitePoint, nameof(whitePoint)); + + if (whitePoint.Equals(Illuminants.C)) + { + return 175; + } + + return 100 * (175 / 198.04F) * (whitePoint.X + whitePoint.Y); + } + + /// + /// Returns the Kb coefficient that depends upon the whitepoint illuminant. + /// + /// The whitepoint + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ComputeKb(CieXyz whitePoint) + { + DebugGuard.NotNull(whitePoint, nameof(whitePoint)); + + if (whitePoint == Illuminants.C) + { + return 70; + } + + return 100 * (70 / 218.11F) * (whitePoint.Y + whitePoint.Z); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs new file mode 100644 index 0000000000..c9b31e7e18 --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs @@ -0,0 +1,66 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab +{ + using HunterLab = ImageSharp.Colors.Spaces.HunterLab; + + /// + /// Color converter between CieXyz and HunterLab + /// + internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion + { + /// + /// Initializes a new instance of the class. + /// + public CieXyzToHunterLabConverter() + : this(HunterLab.DefaultWhitePoint) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The hunter Lab white point. + public CieXyzToHunterLabConverter(CieXyz labWhitePoint) + { + this.HunterLabWhitePoint = labWhitePoint; + } + + /// + /// Gets the target reference white. When not set, is used. + /// + public CieXyz HunterLabWhitePoint { get; } + + /// + public HunterLab Convert(CieXyz input) + { + DebugGuard.NotNull(input, nameof(input)); + + // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + float x = input.X, y = input.Y, z = input.Z; + float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.Z; + + float ka = ComputeKa(this.HunterLabWhitePoint); + float kb = ComputeKb(this.HunterLabWhitePoint); + + float l = 100 * MathF.Sqrt(y / yn); + float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn)); + float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn)); + + if (float.IsNaN(a)) + { + a = 0; + } + + if (float.IsNaN(b)) + { + b = 0; + } + + return new HunterLab(l, a, b, this.HunterLabWhitePoint); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs new file mode 100644 index 0000000000..73190884a8 --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab +{ + using HunterLab = ImageSharp.Colors.Spaces.HunterLab; + + /// + /// Color converter between HunterLab and CieXyz + /// + internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion + { + /// + public CieXyz Convert(HunterLab input) + { + DebugGuard.NotNull(input, nameof(input)); + + // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + float l = input.L, a = input.A, b = input.B; + float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; + + float ka = ComputeKa(input.WhitePoint); + float kb = ComputeKb(input.WhitePoint); + + float y = MathF.Pow(l / 100F, 2) * yn; + float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; + float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); + + return new CieXyz(x, y, z); + } + } +} diff --git a/src/ImageSharp/Colors/Spaces/HunterLab.cs b/src/ImageSharp/Colors/Spaces/HunterLab.cs new file mode 100644 index 0000000000..b156f081ad --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/HunterLab.cs @@ -0,0 +1,190 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces +{ + using System; + using System.ComponentModel; + using System.Numerics; + + /// + /// Represents an Hunter LAB color. + /// + /// + public struct HunterLab : IColorVector, IEquatable, IAlmostEquatable + { + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.C; + + /// + /// Represents a that has L, A, B values set to zero. + /// + public static readonly HunterLab Empty = default(HunterLab); + + /// + /// The backing vector for SIMD support. + /// + private readonly Vector3 backingVector; + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + /// Uses as white point. + public HunterLab(float l, float a, float b) + : this(new Vector3(l, a, b), DefaultWhitePoint) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + /// The reference white point. + public HunterLab(float l, float a, float b, CieXyz whitePoint) + : this(new Vector3(l, a, b), whitePoint) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, a, b components. + /// Uses as white point. + public HunterLab(Vector3 vector) + : this(vector, DefaultWhitePoint) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l a b components. + /// The reference white point. + public HunterLab(Vector3 vector, CieXyz whitePoint) + : this() + { + this.backingVector = vector; + this.WhitePoint = whitePoint; + } + + /// + /// Gets the reference white point of this color + /// + public CieXyz WhitePoint { get; } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public float L => this.backingVector.X; + + /// + /// Gets the a color component. + /// A value ranging from -100 to 100. Negative is green, positive magenta. + /// + public float A => this.backingVector.Y; + + /// + /// Gets the b color component. + /// A value ranging from -100 to 100. Negative is blue, positive is yellow + /// + public float B => this.backingVector.Z; + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + public Vector3 Vector => this.backingVector; + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(HunterLab left, HunterLab right) + { + return left.Equals(right); + } + + /// + /// Compares two objects for inequality + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(HunterLab left, HunterLab right) + { + return !left.Equals(right); + } + + /// + public override int GetHashCode() + { + return this.backingVector.GetHashCode(); + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "HunterLab [Empty]"; + } + + return $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; + } + + /// + public override bool Equals(object obj) + { + if (obj is HunterLab) + { + return this.Equals((HunterLab)obj); + } + + return false; + } + + /// + public bool Equals(HunterLab other) + { + return this.backingVector.Equals(other.backingVector); + } + + /// + public bool AlmostEquals(HunterLab other, float precision) + { + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X <= precision + && result.Y <= precision + && result.Z <= precision; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/LinearRgb.cs b/src/ImageSharp/Colors/Spaces/LinearRgb.cs index c101d66ece..8ae4e01511 100644 --- a/src/ImageSharp/Colors/Spaces/LinearRgb.cs +++ b/src/ImageSharp/Colors/Spaces/LinearRgb.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Colors.Spaces /// /// The default LinearRgb working space /// - private static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; /// /// The backing vector for SIMD support. diff --git a/src/ImageSharp/Colors/Spaces/Rgb.cs b/src/ImageSharp/Colors/Spaces/Rgb.cs index e97b57bee0..85378a1c8f 100644 --- a/src/ImageSharp/Colors/Spaces/Rgb.cs +++ b/src/ImageSharp/Colors/Spaces/Rgb.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Colors.Spaces /// /// The default rgb working space /// - internal static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; /// /// The backing vector for SIMD support. diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs new file mode 100644 index 0000000000..895d7c891d --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -0,0 +1,34 @@ +namespace ImageSharp.Benchmarks.Color +{ + using BenchmarkDotNet.Attributes; + + using Colourful; + using Colourful.Conversion; + + using ImageSharp.Colors.Spaces; + using ImageSharp.Colors.Spaces.Conversion; + + public class ColorspaceCieXyzToCieLabConvert + { + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + + private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); + + + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public LabColor ColourfulConvert() + { + return ColourfulConverter.ToLab(XYZColor); + } + + [Benchmark(Description = "ImageSharp Convert")] + public CieLab ColorSpaceConvert() + { + return ColorSpaceConverter.ToCieLab(CieXyz); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs new file mode 100644 index 0000000000..d686d38c85 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -0,0 +1,35 @@ +namespace ImageSharp.Benchmarks.Color +{ + using BenchmarkDotNet.Attributes; + + using Colourful; + using Colourful.Conversion; + + using ImageSharp.Colors.Spaces; + + using ColorSpaceConverter = ImageSharp.Colors.Spaces.Conversion.ColorSpaceConverter; + + public class ColorspaceCieXyzToHunterLabConvert + { + private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + + private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + + private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter(); + + + [Benchmark(Baseline = true, Description = "Colourful Convert")] + public HunterLabColor ColourfulConvert() + { + return ColourfulConverter.ToHunterLab(XYZColor); + } + + [Benchmark(Description = "ImageSharp Convert")] + public HunterLab ColorSpaceConvert() + { + return ColorSpaceConverter.ToHunterLab(CieXyz); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index 007f45f8e3..8d167d7450 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -6,8 +6,7 @@ using Colourful.Conversion; using ImageSharp.Colors.Spaces; - - using ColorSpaceConverter = ImageSharp.Colors.Spaces.Conversion.ColorSpaceConverter; + using ImageSharp.Colors.Spaces.Conversion; public class ColorspaceCieXyzToLmsConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index d45267cff0..f56bde7390 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -6,8 +6,7 @@ using Colourful.Conversion; using ImageSharp.Colors.Spaces; - - using ColorSpaceConverter = ImageSharp.Colors.Spaces.Conversion.ColorSpaceConverter; + using ImageSharp.Colors.Spaces.Conversion; public class ColorspaceCieXyzToRgbConvert { diff --git a/tests/ImageSharp.Tests/App.config b/tests/ImageSharp.Tests/App.config deleted file mode 100644 index 7b47f780e3..0000000000 --- a/tests/ImageSharp.Tests/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest - Copy.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest - Copy.cs new file mode 100644 index 0000000000..55aec4db56 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest - Copy.cs @@ -0,0 +1,73 @@ +namespace ImageSharp.Tests +{ + using System.Collections.Generic; + using ImageSharp.Colors.Spaces; + using ImageSharp.Colors.Spaces.Conversion; + + using Xunit; + + /// + /// Tests - conversions. + /// + /// + /// Test data generated using: + /// http://www.brucelindbloom.com/index.html?ColorCalculator.html + /// + public class CieXyzAndCieLabConversionTest + { + private static readonly IEqualityComparer FloatComparerLabPrecision = new ApproximateFloatComparer(4); + private static readonly IEqualityComparer FloatComparerXyzPrecision = new ApproximateFloatComparer(6); + + /// + /// Tests conversion from to (). + /// + [Theory] + [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 431.0345, 0, 0.95047, 0, 0)] + [InlineData(100, -431.0345, 172.4138, 0, 1, 0)] + [InlineData(0, 0, -172.4138, 0, 0, 1.08883)] + [InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] + [InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] + [InlineData(10, -400, 20, 0, 0.011260, 0)] + public void Convert_Lab_to_XYZ(float l, float a, float b, float x, float y, float z) + { + // Arrange + CieLab input = new CieLab(l, a, b, Illuminants.D65); + ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + + // Act + CieXyz output = converter.ToCieXyz(input); + + // Assert + Assert.Equal(output.X, x, FloatComparerXyzPrecision); + Assert.Equal(output.Y, y, FloatComparerXyzPrecision); + Assert.Equal(output.Z, z, FloatComparerXyzPrecision); + } + + /// + /// Tests conversion from () to . + /// + [Theory] + [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.95047, 0, 0, 0, 431.0345, 0)] + [InlineData(0, 1, 0, 100, -431.0345, 172.4138)] + [InlineData(0, 0, 1.08883, 0, 0, -172.4138)] + [InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] + public void Convert_XYZ_to_Lab(float x, float y, float z, float l, float a, float b) + { + // Arrange + CieXyz input = new CieXyz(x, y, z); + ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + + // Act + CieLab output = converter.ToCieLab(input); + + // Assert + Assert.Equal(output.L, l, FloatComparerLabPrecision); + Assert.Equal(output.A, a, FloatComparerLabPrecision); + Assert.Equal(output.B, b, FloatComparerLabPrecision); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs index 55aec4db56..afcb2d7195 100644 --- a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs @@ -13,61 +13,71 @@ /// Test data generated using: /// http://www.brucelindbloom.com/index.html?ColorCalculator.html /// - public class CieXyzAndCieLabConversionTest + public class CieXyzAndHunterLabConversionTest { - private static readonly IEqualityComparer FloatComparerLabPrecision = new ApproximateFloatComparer(4); - private static readonly IEqualityComparer FloatComparerXyzPrecision = new ApproximateFloatComparer(6); + private static readonly IEqualityComparer FloatComparer = new ApproximateFloatComparer(4); + + /// + /// Tests conversion from to (). + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0 + public void Convert_HunterLab_to_XYZ(float l, float a, float b, float x, float y, float z) + { + // Arrange + HunterLab input = new HunterLab(l, a, b); + ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.C }; + + // Act + CieXyz output = converter.ToCieXyz(input); + + // Assert + Assert.Equal(output.X, x, FloatComparer); + Assert.Equal(output.Y, y, FloatComparer); + Assert.Equal(output.Z, z, FloatComparer); + } /// /// Tests conversion from to (). /// [Theory] - [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 431.0345, 0, 0.95047, 0, 0)] - [InlineData(100, -431.0345, 172.4138, 0, 1, 0)] - [InlineData(0, 0, -172.4138, 0, 0, 1.08883)] - [InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] - [InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] - [InlineData(10, -400, 20, 0, 0.011260, 0)] - public void Convert_Lab_to_XYZ(float l, float a, float b, float x, float y, float z) + [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) + public void Convert_HunterLab_to_XYZ_D65(float l, float a, float b, float x, float y, float z) { // Arrange - CieLab input = new CieLab(l, a, b, Illuminants.D65); - ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + HunterLab input = new HunterLab(l, a, b); + ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; // Act CieXyz output = converter.ToCieXyz(input); // Assert - Assert.Equal(output.X, x, FloatComparerXyzPrecision); - Assert.Equal(output.Y, y, FloatComparerXyzPrecision); - Assert.Equal(output.Z, z, FloatComparerXyzPrecision); + Assert.Equal(output.X, x, FloatComparer); + Assert.Equal(output.Y, y, FloatComparer); + Assert.Equal(output.Z, z, FloatComparer); } /// /// Tests conversion from () to . /// [Theory] - [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 0, 0, 0, 431.0345, 0)] - [InlineData(0, 1, 0, 100, -431.0345, 172.4138)] - [InlineData(0, 0, 1.08883, 0, 0, -172.4138)] - [InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] - public void Convert_XYZ_to_Lab(float x, float y, float z, float l, float a, float b) + [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) + public void Convert_XYZ_D65_to_HunterLab(float x, float y, float z, float l, float a, float b) { // Arrange CieXyz input = new CieXyz(x, y, z); - ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; // Act - CieLab output = converter.ToCieLab(input); + HunterLab output = converter.ToHunterLab(input); // Assert - Assert.Equal(output.L, l, FloatComparerLabPrecision); - Assert.Equal(output.A, a, FloatComparerLabPrecision); - Assert.Equal(output.B, b, FloatComparerLabPrecision); + Assert.Equal(output.L, l, FloatComparer); + Assert.Equal(output.A, a, FloatComparer); + Assert.Equal(output.B, b, FloatComparer); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index c6f916e00c..ff5eccc787 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -19,4 +19,9 @@ + + + PreserveNewest + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json new file mode 100644 index 0000000000..df1c3d50d0 --- /dev/null +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "methodDisplay": "method" +} \ No newline at end of file