Browse Source

Add HunterLab plus more conversions

af/merge-core
James Jackson-South 9 years ago
parent
commit
deda1dd3a3
  1. 52
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs
  2. 23
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs
  3. 85
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs
  4. 39
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  5. 52
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs
  6. 39
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs
  7. 7
      src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs
  8. 51
      src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs
  9. 66
      src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs
  10. 34
      src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs
  11. 190
      src/ImageSharp/Colors/Spaces/HunterLab.cs
  12. 2
      src/ImageSharp/Colors/Spaces/LinearRgb.cs
  13. 2
      src/ImageSharp/Colors/Spaces/Rgb.cs
  14. 34
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs
  15. 35
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs
  16. 3
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs
  17. 3
      tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs
  18. 6
      tests/ImageSharp.Tests/App.config
  19. 73
      tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest - Copy.cs
  20. 64
      tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs
  21. 5
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  22. 3
      tests/ImageSharp.Tests/xunit.runner.json

52
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);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
}
}

23
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;
/// <summary>
@ -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;
/// <summary>
@ -84,6 +87,26 @@ namespace ImageSharp.Colors.Spaces.Conversion
: this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyz"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyz"/></returns>
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;
}
/// <summary>
/// Gets the correct converter for the given rgb working space.
/// </summary>

85
src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -0,0 +1,85 @@
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Colors.Spaces.Conversion
{
using ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab;
/// <summary>
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
/// </summary>
public partial class ColorSpaceConverter
{
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
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);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
}
}

39
src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -48,6 +48,45 @@ namespace ImageSharp.Colors.Spaces.Conversion
return xyzConverter.Convert(adapted);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRGB(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRGB(CieLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRGB(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Gets the correct converter for the given rgb working space.
/// </summary>

52
src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs

@ -24,5 +24,57 @@ namespace ImageSharp.Colors.Spaces.Conversion
// Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color);
}
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
}
}

39
src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -42,5 +42,44 @@ namespace ImageSharp.Colors.Spaces.Conversion
// Compand
return this.ToRgb(linear);
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
}
}

7
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
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; }
/// <summary>
/// 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: <see cref="Rgb.DefaultWorkingSpace"/>.

51
src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs

@ -0,0 +1,51 @@
// <copyright file="CieXyzAndHunterLabConverterBase.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab
{
using System.Runtime.CompilerServices;
/// <summary>
/// The base class for converting between <see cref="HunterLab"/> and <see cref="CieXyz"/> color spaces.
/// </summary>
internal abstract class CieXyzAndHunterLabConverterBase
{
/// <summary>
/// Returns the Ka coefficient that depends upon the whitepoint illuminant.
/// </summary>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[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);
}
/// <summary>
/// Returns the Kb coefficient that depends upon the whitepoint illuminant.
/// </summary>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[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);
}
}
}

66
src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs

@ -0,0 +1,66 @@
// <copyright file="CieXyzToHunterLabConverter.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab
{
using HunterLab = ImageSharp.Colors.Spaces.HunterLab;
/// <summary>
/// Color converter between CieXyz and HunterLab
/// </summary>
internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion<CieXyz, HunterLab>
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
public CieXyzToHunterLabConverter()
: this(HunterLab.DefaultWhitePoint)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
/// <param name="labWhitePoint">The hunter Lab white point.</param>
public CieXyzToHunterLabConverter(CieXyz labWhitePoint)
{
this.HunterLabWhitePoint = labWhitePoint;
}
/// <summary>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz HunterLabWhitePoint { get; }
/// <inheritdoc/>
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);
}
}
}

34
src/ImageSharp/Colors/Spaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs

@ -0,0 +1,34 @@
// <copyright file="HunterLabToCieXyzConverter.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Colors.Spaces.Conversion.Implementation.HunterLab
{
using HunterLab = ImageSharp.Colors.Spaces.HunterLab;
/// <summary>
/// Color converter between HunterLab and CieXyz
/// </summary>
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz>
{
/// <inheritdoc/>
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);
}
}
}

190
src/ImageSharp/Colors/Spaces/HunterLab.cs

@ -0,0 +1,190 @@
// <copyright file="HunterLab.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an Hunter LAB color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
public struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float>
{
/// <summary>
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.C;
/// <summary>
/// Represents a <see cref="HunterLab"/> that has L, A, B values set to zero.
/// </summary>
public static readonly HunterLab Empty = default(HunterLab);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary>
/// <param name="l">The lightness dimension.</param>
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
public HunterLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary>
/// <param name="l">The lightness dimension.</param>
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
public HunterLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
public HunterLab(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary>
/// <param name="vector">The vector representing the l a b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
public HunterLab(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L => this.backingVector.X;
/// <summary>
/// Gets the a color component.
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public float A => this.backingVector.Y;
/// <summary>
/// Gets the b color component.
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public float B => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="HunterLab"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="HunterLab"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HunterLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HunterLab left, HunterLab right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="HunterLab"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="HunterLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HunterLab"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HunterLab left, HunterLab right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "HunterLab [Empty]";
}
return $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is HunterLab)
{
return this.Equals((HunterLab)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(HunterLab other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
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;
}
}
}

2
src/ImageSharp/Colors/Spaces/LinearRgb.cs

@ -22,7 +22,7 @@ namespace ImageSharp.Colors.Spaces
/// <summary>
/// The default LinearRgb working space
/// </summary>
private static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/Colors/Spaces/Rgb.cs

@ -22,7 +22,7 @@ namespace ImageSharp.Colors.Spaces
/// <summary>
/// The default rgb working space
/// </summary>
internal static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// The backing vector for SIMD support.

34
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);
}
}
}

35
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);
}
}
}

3
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
{

3
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
{

6
tests/ImageSharp.Tests/App.config

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="xunit.methodDisplay" value="method"/>
</appSettings>
</configuration>

73
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;
/// <summary>
/// Tests <see cref="CieXyz"/>-<see cref="CieLab"/> conversions.
/// </summary>
/// <remarks>
/// Test data generated using:
/// http://www.brucelindbloom.com/index.html?ColorCalculator.html
/// </remarks>
public class CieXyzAndCieLabConversionTest
{
private static readonly IEqualityComparer<float> FloatComparerLabPrecision = new ApproximateFloatComparer(4);
private static readonly IEqualityComparer<float> FloatComparerXyzPrecision = new ApproximateFloatComparer(6);
/// <summary>
/// Tests conversion from <see cref="CieLab"/> to <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>).
/// </summary>
[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);
}
/// <summary>
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="CieLab"/>.
/// </summary>
[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);
}
}
}

64
tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs

@ -13,61 +13,71 @@
/// Test data generated using:
/// http://www.brucelindbloom.com/index.html?ColorCalculator.html
/// </remarks>
public class CieXyzAndCieLabConversionTest
public class CieXyzAndHunterLabConversionTest
{
private static readonly IEqualityComparer<float> FloatComparerLabPrecision = new ApproximateFloatComparer(4);
private static readonly IEqualityComparer<float> FloatComparerXyzPrecision = new ApproximateFloatComparer(6);
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(4);
/// <summary>
/// Tests conversion from <see cref="CieLab"/> to <see cref="CieXyz"/> (<see cref="Illuminants.C"/>).
/// </summary>
[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);
}
/// <summary>
/// Tests conversion from <see cref="CieLab"/> to <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>).
/// </summary>
[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);
}
/// <summary>
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="CieLab"/>.
/// </summary>
[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);
}
}
}

5
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -19,4 +19,9 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

3
tests/ImageSharp.Tests/xunit.runner.json

@ -0,0 +1,3 @@
{
"methodDisplay": "method"
}
Loading…
Cancel
Save