Browse Source

Simplify structs, 5% perf increase.

af/merge-core
James Jackson-South 8 years ago
parent
commit
96d27c46ea
  1. 126
      src/ImageSharp/ColorSpaces/CieLab.cs
  2. 122
      src/ImageSharp/ColorSpaces/CieLch.cs
  3. 127
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  4. 129
      src/ImageSharp/ColorSpaces/CieLuv.cs
  5. 92
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  6. 108
      src/ImageSharp/ColorSpaces/CieXyy.cs
  7. 108
      src/ImageSharp/ColorSpaces/CieXyz.cs
  8. 126
      src/ImageSharp/ColorSpaces/Cmyk.cs
  9. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndLmsConverter.cs
  10. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToLinearRgbConverter.cs
  11. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CmykAndRgbConverter.cs
  12. 26
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbAndCieXyzConverterBase.cs
  13. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToCieXyzConverter.cs
  14. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToRgbConverter.cs
  15. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RgbToLinearRgbConverter.cs
  16. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCrAndRgbConverter.cs
  17. 4
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  18. 104
      src/ImageSharp/ColorSpaces/Hsl.cs
  19. 107
      src/ImageSharp/ColorSpaces/Hsv.cs
  20. 120
      src/ImageSharp/ColorSpaces/HunterLab.cs
  21. 18
      src/ImageSharp/ColorSpaces/IColorVector.cs
  22. 31
      src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs
  23. 125
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  24. 119
      src/ImageSharp/ColorSpaces/Lms.cs
  25. 126
      src/ImageSharp/ColorSpaces/Rgb.cs
  26. 108
      src/ImageSharp/ColorSpaces/YCbCr.cs
  27. 498
      tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs

126
src/ImageSharp/ColorSpaces/CieLab.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents a CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal readonly struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float>
internal readonly struct CieLab : IEquatable<CieLab>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
@ -34,7 +51,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint)
: this(l, a, b, DefaultWhitePoint)
{
}
@ -47,8 +64,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint)
{
this.L = l;
this.A = a;
this.B = b;
this.WhitePoint = whitePoint;
}
/// <summary>
@ -71,88 +91,41 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieLab(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
this.L = vector.X;
this.A = vector.Y;
this.B = vector.Z;
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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLab left, CieLab right)
{
return left.Equals(right);
}
public static bool operator ==(CieLab left, CieLab right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLab left, CieLab right)
{
return !left.Equals(right);
}
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode());
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
/// <inheritdoc/>
@ -164,29 +137,16 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLab other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLab other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieLab other)
{
return this.backingVector.Equals(other.backingVector)
return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLab other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
}
}

122
src/ImageSharp/ColorSpaces/CieLch.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </summary>
internal readonly struct CieLch : IColorVector, IEquatable<CieLch>, IAlmostEquatable<CieLch, float>
internal readonly struct CieLch : IEquatable<CieLch>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLch"/> struct.
@ -34,7 +51,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint)
: this(l, c, h, DefaultWhitePoint)
{
}
@ -47,8 +64,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint)
{
this.L = l;
this.C = c;
this.H = h;
this.WhitePoint = whitePoint;
}
/// <summary>
@ -69,59 +89,18 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
this.L = vector.X;
this.C = vector.Y;
this.H = vector.Z;
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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieLch"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLch"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLch"/> 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>
@ -134,25 +113,21 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Compares two <see cref="CieLch"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLch"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLch"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLch"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLch"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLch left, CieLch right)
{
return !left.Equals(right);
}
public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode());
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
/// <inheritdoc/>
@ -165,31 +140,18 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj is CieLch other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLch other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieLch other)
{
return this.backingVector.Equals(other.backingVector)
return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLch other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
/// <summary>
/// Computes the saturation of the color (chroma normalized by lightness)
/// </summary>

127
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CieLchuv_or_CIEHLC"/>
/// </summary>
internal readonly struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float>
internal readonly struct CieLchuv : IEquatable<CieLchuv>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
@ -34,7 +51,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint)
: this(l, c, h, DefaultWhitePoint)
{
}
@ -47,8 +64,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint)
{
this.L = l;
this.C = c;
this.H = h;
this.WhitePoint = whitePoint;
}
/// <summary>
@ -71,87 +91,39 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieLchuv(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
this.L = vector.X;
this.C = vector.Y;
this.H = vector.Z;
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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 100.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieLchuv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLchuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLchuv"/> 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 ==(CieLchuv left, CieLchuv right)
{
return left.Equals(right);
}
public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLchuv"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLchuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLchuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLchuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLchuv"/> 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 !=(CieLchuv left, CieLchuv right)
{
return !left.Equals(right);
}
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode());
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
/// <inheritdoc/>
@ -163,31 +135,18 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLchuv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLchuv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieLchuv other)
{
return this.backingVector.Equals(other.backingVector)
return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLchuv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
/// <summary>
/// Computes the saturation of the color (chroma normalized by lightness)
/// </summary>

129
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// attempted perceptual uniformity
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
/// </summary>
internal readonly struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float>
internal readonly struct CieLuv : IEquatable<CieLuv>
{
/// <summary>
/// D65 standard illuminant.
@ -23,9 +22,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the lightness dimension
/// <remarks>A value usually ranging between 0 and 100.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float U;
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float V;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
private readonly Vector3 backingVector;
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
@ -36,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv(float l, float u, float v)
: this(new Vector3(l, u, v), DefaultWhitePoint)
: this(l, u, v, DefaultWhitePoint)
{
}
@ -49,8 +66,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv(float l, float u, float v, CieXyz whitePoint)
: this(new Vector3(l, u, v), whitePoint)
{
this.L = l;
this.U = u;
this.V = v;
this.WhitePoint = whitePoint;
}
/// <summary>
@ -71,90 +91,42 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
this.L = vector.X;
this.U = vector.Y;
this.V = vector.Z;
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 usually ranging between 0 and 100.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public float U
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public float V
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieLuv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLuv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLuv left, CieLuv right)
{
return left.Equals(right);
}
public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieLuv"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLuv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLuv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieLuv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLuv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLuv left, CieLuv right)
{
return !left.Equals(right);
}
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode());
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.U.GetHashCode());
hash = HashHelpers.Combine(hash, this.V.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
/// <inheritdoc/>
@ -166,29 +138,16 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieLuv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieLuv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieLuv other)
{
return this.backingVector.Equals(other.backingVector)
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieLuv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.L.Equals(other.L)
&& this.U.Equals(other.U)
&& this.V.Equals(other.V)
&& this.WhitePoint.Equals(other.WhitePoint);
}
}
}

92
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
// ReSharper disable CompareOfFloatsByEqualityOperator
@ -12,45 +10,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents the coordinates of CIEXY chromaticity space
/// </summary>
internal readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>, IAlmostEquatable<CieXyChromaticityCoordinates, float>
internal readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>
{
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector2 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyChromaticityCoordinates(float x, float y)
: this(new Vector2(x, y))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="vector">The vector containing the XY Chromaticity coordinates</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyChromaticityCoordinates(Vector2 vector)
{
this.backingVector = vector;
}
/// <summary>
/// Gets the chromaticity X-coordinate.
/// </summary>
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
public readonly float X;
/// <summary>
/// Gets the chromaticity Y-coordinate
@ -58,21 +26,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public float Y
public readonly float Y;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyChromaticityCoordinates(float x, float y)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
this.X = x;
this.Y = y;
}
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> 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>
@ -85,12 +57,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> 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>
@ -102,10 +70,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
}
public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
/// <inheritdoc/>
public override string ToString()
@ -116,27 +81,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyChromaticityCoordinates other)
{
// The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443
return this.X == other.X && this.Y == other.Y;
}
public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyChromaticityCoordinates other, float precision)
{
var result = Vector2.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision;
}
public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
}
}

108
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE xyY 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space"/>
/// </summary>
internal readonly struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float>
internal readonly struct CieXyy : IEquatable<CieXyy>
{
/// <summary>
/// The backing vector for SIMD support.
/// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float Yl;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
@ -27,8 +39,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="yl">The y luminance component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyy(float x, float y, float yl)
: this(new Vector3(x, y, yl))
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.X = x;
this.Y = y;
this.Yl = yl;
}
/// <summary>
@ -40,83 +55,39 @@ namespace SixLabors.ImageSharp.ColorSpaces
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
}
/// <summary>
/// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Yl
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
this.X = vector.X;
this.Y = vector.Y;
this.Yl = vector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieXyy"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyy"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyy"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyy left, CieXyy right)
{
return left.Equals(right);
}
public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyy"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyy"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyy"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyy"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyy"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyy left, CieXyy right)
{
return !left.Equals(right);
}
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Yl.GetHashCode());
}
/// <inheritdoc/>
@ -137,18 +108,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyy other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyy other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Yl.Equals(other.Yl);
}
}
}

108
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -12,12 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE XYZ 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#Definition_of_the_CIE_XYZ_color_space"/>
/// </summary>
internal readonly struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float>
internal readonly struct CieXyz : IEquatable<CieXyz>
{
/// <summary>
/// The backing vector for SIMD support.
/// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float Z;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
@ -29,6 +42,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieXyz(float x, float y, float z)
: this(new Vector3(x, y, z))
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.X = x;
this.Y = y;
this.Z = z;
}
/// <summary>
@ -39,51 +56,16 @@ namespace SixLabors.ImageSharp.ColorSpaces
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
}
/// <summary>
/// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float Z
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
this.X = vector.X;
this.Y = vector.Y;
this.Z = vector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyz"/> 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>
@ -96,12 +78,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="CieXyz"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyz"/> 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>
@ -111,11 +89,19 @@ namespace SixLabors.ImageSharp.ColorSpaces
return !left.Equals(right);
}
/// <inheritdoc/>
/// <summary>
/// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Z.GetHashCode());
}
/// <inheritdoc/>
@ -127,27 +113,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyz other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieXyz other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyz other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(CieXyz other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Z.Equals(other.Z);
}
}
}

126
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -11,12 +10,31 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
/// </summary>
internal readonly struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float>
internal readonly struct Cmyk : IEquatable<Cmyk>
{
/// <summary>
/// The backing vector for SIMD support.
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector4 backingVector;
public readonly float M;
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float K;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
@ -37,92 +55,44 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="vector">The vector representing the c, m, y, k components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk(Vector4 vector)
: this()
{
this.backingVector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
}
/// <summary>
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float M
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float K
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.W;
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
this.C = vector.X;
this.Y = vector.Y;
this.M = vector.Z;
this.K = vector.W;
}
/// <summary>
/// Compares two <see cref="Cmyk"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Cmyk"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Cmyk"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Cmyk"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Cmyk left, Cmyk right)
{
return left.Equals(right);
}
public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Cmyk"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="Cmyk"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Cmyk"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Cmyk"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Cmyk left, Cmyk right)
{
return !left.Equals(right);
}
public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.C.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.K.GetHashCode());
}
/// <inheritdoc/>
@ -134,28 +104,16 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Cmyk other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Cmyk other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Cmyk other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Cmyk other, float precision)
{
var result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision
&& result.W <= precision;
return this.C.Equals(other.C)
&& this.M.Equals(other.M)
&& this.Y.Equals(other.Y)
&& this.K.Equals(other.K);
}
}
}

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndLmsConverter.cs

@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lms Convert(in CieXyz input)
{
var vector = Vector3.Transform(input.Vector, this.transformationMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix);
return new Lms(vector);
}
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(in Lms input)
{
var vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
return new CieXyz(vector);
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToLinearRgbConverter.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
public LinearRgb Convert(in CieXyz input)
{
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
var vector = Vector3.Transform(input.Vector, inverted);
var vector = Vector3.Transform(input.ToVector3(), inverted);
return new LinearRgb(vector, this.TargetWorkingSpace);
}
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CmykAndRgbConverter.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
public Cmyk Convert(in Rgb input)
{
// To CMYK
Vector3 cmy = Vector3.One - input.Vector;
Vector3 cmy = Vector3.One - input.ToVector3();
// To CMYK
var k = new Vector3(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z)));

26
src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbAndCieXyzConverterBase.cs

@ -42,23 +42,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
var xyzMatrix = new Matrix4x4
{
M11 = mXr, M21 = mXg, M31 = mXb,
M12 = Yr, M22 = Yg, M32 = Yb,
M13 = mZr, M23 = mZg, M33 = mZb,
M11 = mXr,
M21 = mXg,
M31 = mXb,
M12 = Yr,
M22 = Yg,
M32 = Yb,
M13 = mZr,
M23 = mZg,
M33 = mZb,
M44 = 1F
};
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
var vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix);
var vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix);
// Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication?
return new Matrix4x4
{
M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb,
M12 = vector.X * Yr, M22 = vector.Y * Yg, M32 = vector.Z * Yb,
M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb,
M11 = vector.X * mXr,
M21 = vector.Y * mXg,
M31 = vector.Z * mXb,
M12 = vector.X * Yr,
M22 = vector.Y * Yg,
M32 = vector.Z * Yb,
M13 = vector.X * mZr,
M23 = vector.Y * mZg,
M33 = vector.Z * mZb,
M44 = 1F
};
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToCieXyzConverter.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
var vector = Vector3.Transform(input.Vector, this.conversionMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
return new CieXyz(vector);
}
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToRgbConverter.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc/>
public Rgb Convert(in LinearRgb input)
{
Vector3 vector = input.Vector;
Vector3 vector = input.ToVector3();
vector.X = input.WorkingSpace.Companding.Compress(vector.X);
vector.Y = input.WorkingSpace.Companding.Compress(vector.Y);
vector.Z = input.WorkingSpace.Companding.Compress(vector.Z);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/RgbToLinearRgbConverter.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc/>
public LinearRgb Convert(in Rgb input)
{
Vector3 vector = input.Vector;
Vector3 vector = input.ToVector3();
vector.X = input.WorkingSpace.Companding.Expand(vector.X);
vector.Y = input.WorkingSpace.Companding.Expand(vector.Y);
vector.Z = input.WorkingSpace.Companding.Expand(vector.Z);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCrAndRgbConverter.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public YCbCr Convert(in Rgb input)
{
Vector3 rgb = input.Vector * MaxBytes;
Vector3 rgb = input.ToVector3() * MaxBytes;
float r = rgb.X;
float g = rgb.Y;
float b = rgb.Z;

4
src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs

@ -58,8 +58,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(targetWhitePoint);
Vector3 vector = targetWhitePointLms.Vector / sourceWhitePointLms.Vector;
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector));
Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3();
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3()));
return this.converter.Convert(targetColorLms);
}

104
src/ImageSharp/ColorSpaces/Hsl.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -11,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a Hsl (hue, saturation, lightness) color.
/// </summary>
internal readonly struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
internal readonly struct Hsl : IEquatable<Hsl>
{
/// <summary>
/// Max range used for clamping.
@ -19,9 +18,22 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
/// <summary>
/// The backing vector for SIMD support.
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float S;
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct.
@ -42,83 +54,43 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsl(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
vector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
this.H = vector.X;
this.S = vector.Y;
this.L = vector.Z;
}
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="Hsl"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <param name="right">The <see cref="Hsl"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Hsl left, Hsl right)
{
return left.Equals(right);
}
public static bool operator ==(Hsl left, Hsl right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Hsl"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Hsl"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Hsl"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsl left, Hsl right)
{
return !left.Equals(right);
}
public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.L.GetHashCode());
}
/// <inheritdoc/>
@ -130,27 +102,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Hsl other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Hsl other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Hsl other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Hsl other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.L.Equals(other.L);
}
}
}

107
src/ImageSharp/ColorSpaces/Hsv.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
/// </summary>
internal readonly struct Hsv : IColorVector, IEquatable<Hsv>, IAlmostEquatable<Hsv, float>
internal readonly struct Hsv : IEquatable<Hsv>
{
/// <summary>
/// Max range used for clamping.
@ -21,9 +21,22 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
/// <summary>
/// The backing vector for SIMD support.
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float V;
/// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct.
@ -44,42 +57,12 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsv(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
vector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
this.H = vector.X;
this.S = vector.Y;
this.V = vector.Z;
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float V
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Hsv"/>.
@ -133,44 +116,32 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Compares two <see cref="Hsv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Hsv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Hsv left, Hsv right)
{
return left.Equals(right);
}
public static bool operator ==(Hsv left, Hsv right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Hsv"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Hsv"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Hsv"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsv left, Hsv right)
{
return !left.Equals(right);
}
public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.V.GetHashCode());
}
/// <inheritdoc/>
@ -182,27 +153,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Hsv other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Hsv other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Hsv other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Hsv other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.V.Equals(other.V);
}
}
}

120
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an Hunter LAB color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal readonly struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float>
internal readonly struct HunterLab : IEquatable<HunterLab>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +21,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly CieXyz DefaultWhitePoint = Illuminants.C;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
@ -69,90 +87,43 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HunterLab(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
// TODO: Clamp?
this.L = vector.X;
this.A = vector.Y;
this.B = vector.Z;
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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <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>
/// <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);
}
public static bool operator ==(HunterLab left, HunterLab right) => 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>
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(HunterLab left, HunterLab right)
{
return !left.Equals(right);
}
public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode());
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
/// <inheritdoc/>
@ -164,29 +135,16 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is HunterLab other && this.Equals(other);
}
public override bool Equals(object obj) => obj is HunterLab other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(HunterLab other)
{
return this.backingVector.Equals(other.backingVector)
return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(HunterLab other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return this.WhitePoint.Equals(other.WhitePoint)
&& result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
}
}

18
src/ImageSharp/ColorSpaces/IColorVector.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Color represented as a vector in its color space
/// </summary>
internal interface IColorVector
{
/// <summary>
/// Gets the vector representation of the color
/// </summary>
Vector3 Vector { get; }
}
}

31
src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Encasulates the RGB working color space
/// </summary>
internal interface IRgbWorkingSpace : IEquatable<IRgbWorkingSpace>
{
/// <summary>
/// Gets the reference white of the color space.
/// </summary>
CieXyz WhitePoint { get; }
/// <summary>
/// Gets the chromaticity coordinates of the primaries.
/// </summary>
RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Gets the companding function associated with the RGB color system. Used for conversion to XYZ and backwards.
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </summary>
ICompanding Companding { get; }
}
}

125
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an linear Rgb color with specified <see cref="RgbWorkingSpace"/> working space
/// </summary>
internal readonly struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float>
internal readonly struct LinearRgb : IEquatable<LinearRgb>
{
/// <summary>
/// The default LinearRgb working space.
@ -19,9 +19,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
@ -65,92 +83,51 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="workingSpace">The LinearRgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
{
// Clamp to 0-1 range.
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
this.R = vector.X;
this.G = vector.Y;
this.B = vector.Z;
this.WorkingSpace = workingSpace;
}
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float R
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float G
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public RgbWorkingSpace WorkingSpace { get; }
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="LinearRgb"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="LinearRgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="LinearRgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(LinearRgb left, LinearRgb right)
{
return left.Equals(right);
}
public static bool operator ==(LinearRgb left, LinearRgb right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="LinearRgb"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="LinearRgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="LinearRgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="LinearRgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="LinearRgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(LinearRgb left, LinearRgb right)
{
return !left.Equals(right);
}
public static bool operator !=(LinearRgb left, LinearRgb right) => !left.Equals(right);
/// <summary>
/// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
/// <inheritdoc/>
@ -162,27 +139,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is LinearRgb other && this.Equals(other);
}
public override bool Equals(object obj) => obj is LinearRgb other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(LinearRgb other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(LinearRgb other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}
}

119
src/ImageSharp/ColorSpaces/Lms.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -13,12 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
/// </summary>
internal readonly struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float>
internal readonly struct Lms : IEquatable<Lms>
{
/// <summary>
/// The backing vector for SIMD support.
/// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float M;
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary>
/// Initializes a new instance of the <see cref="Lms"/> struct.
@ -28,8 +40,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="s">S represents the responsivity at short wavelengths.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lms(float l, float m, float s)
: this(new Vector3(l, m, s))
{
this.L = l;
this.M = m;
this.S = s;
}
/// <summary>
@ -38,85 +52,48 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="vector">The vector representing the l, m, s components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lms(Vector3 vector)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
}
/// <summary>
/// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float M
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
this.L = vector.X;
this.M = vector.Y;
this.S = vector.Z;
}
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="Lms"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Lms"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Lms"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Lms left, Lms right)
{
return left.Equals(right);
}
public static bool operator ==(Lms left, Lms right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Lms"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Lms"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Lms"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Lms"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Lms"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Lms left, Lms right)
{
return !left.Equals(right);
}
public static bool operator !=(Lms left, Lms right) => !left.Equals(right);
/// <summary>
/// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
return HashHelpers.Combine(hash, this.S.GetHashCode());
}
/// <inheritdoc/>
@ -128,27 +105,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Lms other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Lms other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Lms other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Lms other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.L.Equals(other.L)
&& this.M.Equals(other.M)
&& this.S.Equals(other.S);
}
}
}

126
src/ImageSharp/ColorSpaces/Rgb.cs

@ -10,9 +10,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents an RGB color with specified <see cref="RgbWorkingSpace"/> working space
/// Represents an RGB color with specified <see cref="RgbWorkingSpace"/> working space.
/// </summary>
internal readonly struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float>
internal readonly struct Rgb : IEquatable<Rgb>
{
/// <summary>
/// The default rgb working space
@ -20,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// The backing vector for SIMD support.
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct.
@ -32,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="b">The blue component ranging between 0 and 1.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(float r, float g, float b)
: this(new Vector3(r, g, b))
: this(r, g, b, DefaultWorkingSpace)
{
}
@ -45,8 +63,11 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace)
: this(new Vector3(r, g, b), workingSpace)
{
this.R = r;
this.G = g;
this.B = b;
this.WorkingSpace = workingSpace;
}
/// <summary>
@ -66,51 +87,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
{
// Clamp to 0-1 range.
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
this.R = vector.X;
this.G = vector.Y;
this.B = vector.Z;
this.WorkingSpace = workingSpace;
}
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float R
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float G
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public float B
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public RgbWorkingSpace WorkingSpace { get; }
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Rgb"/>.
@ -140,33 +125,32 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rgb left, Rgb right)
{
return left.Equals(right);
}
public static bool operator ==(Rgb left, Rgb right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Rgb"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgb"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgb"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Rgb"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgb"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgb left, Rgb right)
{
return !left.Equals(right);
}
public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right);
/// <summary>
/// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
/// <inheritdoc/>
@ -178,27 +162,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Rgb other && this.Equals(other);
}
public override bool Equals(object obj) => obj is Rgb other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgb other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Rgb other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}
}

108
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -13,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// <see href="http://www.ijg.org/files/T-REC-T.871-201105-I!!PDF-E.pdf"/>
/// </summary>
internal readonly struct YCbCr : IColorVector, IEquatable<YCbCr>, IAlmostEquatable<YCbCr, float>
internal readonly struct YCbCr : IEquatable<YCbCr>
{
/// <summary>
/// Vector which is used in clamping to the max value.
@ -21,9 +20,22 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector3 VectorMax = new Vector3(255F);
/// <summary>
/// The backing vector for SIMD support.
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Cb;
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
private readonly Vector3 backingVector;
public readonly float Cr;
/// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
@ -44,82 +56,40 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public YCbCr(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
vector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
this.Y = vector.X;
this.Cb = vector.Y;
this.Cr = vector.Z;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Y
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Cb
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public float Cr
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="YCbCr"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="YCbCr"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="YCbCr"/> 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 ==(YCbCr left, YCbCr right)
{
return left.Equals(right);
}
public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="YCbCr"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="YCbCr"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="YCbCr"/> 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(YCbCr left, YCbCr right)
{
return !left.Equals(right);
}
public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
int hash = this.Y.GetHashCode();
hash = HashHelpers.Combine(hash, this.Cb.GetHashCode());
return HashHelpers.Combine(hash, this.Cr.GetHashCode());
}
/// <inheritdoc/>
@ -131,27 +101,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is YCbCr other && this.Equals(other);
}
public override bool Equals(object obj) => obj is YCbCr other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(YCbCr other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(YCbCr other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
return this.Y.Equals(other.Y)
&& this.Cb.Equals(other.Cb)
&& this.Cr.Equals(other.Cr);
}
}
}

498
tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs

@ -17,228 +17,191 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces
/// </summary>
public class ColorSpaceEqualityTests
{
internal static readonly Dictionary<string, IColorVector> EmptyDataLookup =
new Dictionary<string, IColorVector>
{
{nameof( CieLab), default(CieLab) },
{nameof( CieLch), default(CieLch) },
{nameof( CieLchuv), default(CieLchuv) },
{nameof( CieLuv), default(CieLuv) },
{nameof( CieXyz), default(CieXyz) },
{nameof( CieXyy), default(CieXyy) },
{nameof( Hsl), default(Hsl) },
{nameof( HunterLab), default(HunterLab) },
{nameof( Lms), default(Lms) },
{nameof( LinearRgb), default(LinearRgb) },
{nameof( Rgb), default(Rgb) },
{nameof( YCbCr), default(YCbCr) }
};
public static readonly IEnumerable<object[]> EmptyData = EmptyDataLookup.Select(x => new[] { x.Key });
public static readonly TheoryData<object, object, Type> EqualityData =
new TheoryData<object, object, Type>
{
{ new CieLab(Vector3.One), new CieLab(Vector3.One), typeof(CieLab) },
{ new CieLch(Vector3.One), new CieLch(Vector3.One), typeof(CieLch) },
{ new CieLchuv(Vector3.One), new CieLchuv(Vector3.One), typeof(CieLchuv) },
{ new CieLuv(Vector3.One), new CieLuv(Vector3.One), typeof(CieLuv) },
{ new CieXyz(Vector3.One), new CieXyz(Vector3.One), typeof(CieXyz) },
{ new CieXyy(Vector3.One), new CieXyy(Vector3.One), typeof(CieXyy) },
{ new HunterLab(Vector3.One), new HunterLab(Vector3.One), typeof(HunterLab) },
{ new Lms(Vector3.One), new Lms(Vector3.One), typeof(Lms) },
{ new LinearRgb(Vector3.One), new LinearRgb(Vector3.One), typeof(LinearRgb) },
{ new Rgb(Vector3.One), new Rgb(Vector3.One), typeof(Rgb) },
{ new Hsl(Vector3.One), new Hsl(Vector3.One), typeof(Hsl) },
{ new Hsv(Vector3.One), new Hsv(Vector3.One), typeof(Hsv) },
{ new YCbCr(Vector3.One), new YCbCr(Vector3.One), typeof(YCbCr) },
};
public static readonly TheoryData<object, object, Type> NotEqualityDataNulls =
new TheoryData<object, object, Type>
{
// Valid object against null
{ new CieLab(Vector3.One), null, typeof(CieLab) },
{ new CieLch(Vector3.One), null, typeof(CieLch) },
{ new CieLchuv(Vector3.One), null, typeof(CieLchuv) },
{ new CieLuv(Vector3.One), null, typeof(CieLuv) },
{ new CieXyz(Vector3.One), null, typeof(CieXyz) },
{ new CieXyy(Vector3.One), null, typeof(CieXyy) },
{ new HunterLab(Vector3.One), null, typeof(HunterLab) },
{ new Lms(Vector3.One), null, typeof(Lms) },
{ new LinearRgb(Vector3.One), null, typeof(LinearRgb) },
{ new Rgb(Vector3.One), null, typeof(Rgb) },
{ new Hsl(Vector3.One), null, typeof(Hsl) },
{ new Hsv(Vector3.One), null, typeof(Hsv) },
{ new YCbCr(Vector3.One), null, typeof(YCbCr) },
};
public static readonly TheoryData<object, object, Type> NotEqualityDataDifferentObjects =
new TheoryData<object, object, Type>
{
// Valid objects of different types but not equal
{ new CieLab(Vector3.One), new CieLch(Vector3.Zero), null },
{ new CieLuv(Vector3.One), new CieLchuv(Vector3.Zero), null },
{ new CieXyz(Vector3.One), new HunterLab(Vector3.Zero), null },
{ new Rgb(Vector3.One), new LinearRgb(Vector3.Zero), null },
{ new Rgb(Vector3.One), new Lms(Vector3.Zero), null },
{ new Cmyk(Vector4.One), new Hsl(Vector3.Zero), null },
{ new YCbCr(Vector3.One), new CieXyy(Vector3.Zero), null },
{ new Hsv(Vector3.One), new Hsl(Vector3.Zero), null },
};
public static readonly TheoryData<object, object, Type> NotEqualityData =
new TheoryData<object, object, Type>
{
// Valid objects of the same type but not equal
{ new CieLab(Vector3.One), new CieLab(Vector3.Zero), typeof(CieLab) },
{ new CieLch(Vector3.One), new CieLch(Vector3.Zero), typeof(CieLch) },
{ new CieLchuv(Vector3.One), new CieLchuv(Vector3.Zero), typeof(CieLchuv) },
{ new CieLuv(Vector3.One), new CieLuv(Vector3.Zero), typeof(CieLuv) },
{ new CieXyz(Vector3.One), new CieXyz(Vector3.Zero), typeof(CieXyz) },
{ new CieXyy(Vector3.One), new CieXyy(Vector3.Zero), typeof(CieXyy) },
{ new HunterLab(Vector3.One), new HunterLab(Vector3.Zero), typeof(HunterLab) },
{ new Lms(Vector3.One), new Lms(Vector3.Zero), typeof(Lms) },
{ new LinearRgb(Vector3.One), new LinearRgb(Vector3.Zero), typeof(LinearRgb) },
{ new Rgb(Vector3.One), new Rgb(Vector3.Zero), typeof(Rgb) },
{ new Cmyk(Vector4.One), new Cmyk(Vector4.Zero), typeof(Cmyk) },
{ new Hsl(Vector3.One), new Hsl(Vector3.Zero), typeof(Hsl) },
{ new Hsv(Vector3.One), new Hsv(Vector3.Zero), typeof(Hsv) },
{ new YCbCr(Vector3.One), new YCbCr(Vector3.Zero), typeof(YCbCr) },
};
public static readonly TheoryData<object, object, Type, float> AlmostEqualsData =
new TheoryData<object, object, Type, float>
{
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), 0F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .001F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0001F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0005F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, .001F, 0F), typeof(CieLab), .001F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, .0001F), typeof(CieLab), .0001F },
{ new CieLab(0F, 0F, 0F), new CieLab(.0005F, 0F, 0F), typeof(CieLab), .0005F },
{ new CieLch(0F, 0F, 0F), new CieLch(0F, .001F, 0F), typeof(CieLch), .001F },
{ new CieLchuv(0F, 0F, 0F), new CieLchuv(0F, .001F, 0F), typeof(CieLchuv), .001F },
{ new CieLuv(0F, 0F, 0F), new CieLuv(0F, .001F, 0F), typeof(CieLuv), .001F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380F), typeof(CieXyz), 0F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380.001F, 380F, 380F), typeof(CieXyz), .01F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.001F, 380F), typeof(CieXyz), .01F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.001F), typeof(CieXyz), .01F },
{ new Cmyk(1, 1, 1, 1), new Cmyk(1, 1, 1, .99F), typeof(Cmyk), .01F },
{ new YCbCr(255F, 128F, 128F), new YCbCr(255F, 128F, 128.001F), typeof(YCbCr), .01F },
{ new Hsv(0F, 0F, 0F), new Hsv(0F, 0F, 0F), typeof(Hsv), 0F },
{ new Hsl(0F, 0F, 0F), new Hsl(0F, 0F, 0F), typeof(Hsl), 0F },
};
public static readonly TheoryData<object, object, Type, float> AlmostNotEqualsData =
new TheoryData<object, object, Type, float>
{
{ new CieLab(0F, 0F, 0F), new CieLab(0.1F, 0F, 0F), typeof(CieLab), .001F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0.1F, 0F), typeof(CieLab), .001F },
{ new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0.1F), typeof(CieLab), .001F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380.1F, 380F, 380F), typeof(CieXyz), .001F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.1F, 380F), typeof(CieXyz), .001F },
{ new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.1F), typeof(CieXyz), .001F },
};
[Theory]
[MemberData(nameof(EmptyData))]
public void Vector_Equals_WhenTrue(string color)
{
IColorVector colorVector = EmptyDataLookup[color];
// Act
bool equal = colorVector.Vector.Equals(Vector3.Zero);
// Assert
Assert.True(equal);
}
[Theory]
[MemberData(nameof(EqualityData))]
public void Equals_WhenTrue(object first, object second, Type type)
{
// Act
bool equal = first.Equals(second);
// Assert
Assert.True(equal);
}
[Theory]
[MemberData(nameof(NotEqualityDataNulls))]
[MemberData(nameof(NotEqualityDataDifferentObjects))]
[MemberData(nameof(NotEqualityData))]
public void Equals_WhenFalse(object first, object second, Type type)
{
// Act
bool equal = first.Equals(second);
// Assert
Assert.False(equal);
}
[Theory]
[MemberData(nameof(EqualityData))]
public void GetHashCode_WhenEqual(object first, object second, Type type)
{
// Act
bool equal = first.GetHashCode() == second.GetHashCode();
// Assert
Assert.True(equal);
}
[Theory]
[MemberData(nameof(NotEqualityDataDifferentObjects))]
public void GetHashCode_WhenNotEqual(object first, object second, Type type)
{
// Act
bool equal = first.GetHashCode() == second.GetHashCode();
// Assert
Assert.False(equal);
}
[Theory]
[MemberData(nameof(EqualityData))]
public void GenericEquals_WhenTrue(object first, object second, Type type)
{
// Arrange
// Cast to the known object types, this is so that we can hit the
// equality operator on the concrete type, otherwise it goes to the
// default "object" one :)
dynamic firstObject = Convert.ChangeType(first, type);
dynamic secondObject = Convert.ChangeType(second, type);
// Act
dynamic equal = firstObject.Equals(secondObject);
// Assert
Assert.True(equal);
}
[Theory]
[MemberData(nameof(NotEqualityData))]
public void GenericEquals_WhenFalse(object first, object second, Type type)
{
// Arrange
// Cast to the known object types, this is so that we can hit the
// equality operator on the concrete type, otherwise it goes to the
// default "object" one :)
dynamic firstObject = Convert.ChangeType(first, type);
dynamic secondObject = Convert.ChangeType(second, type);
// Act
dynamic equal = firstObject.Equals(secondObject);
// Assert
Assert.False(equal);
}
// TODO:Disabled due to RuntypeBinder errors while structs are internal
//internal static readonly Dictionary<string, IColorVector> EmptyDataLookup =
// new Dictionary<string, IColorVector>
// {
// {nameof( CieLab), default(CieLab) },
// {nameof( CieLch), default(CieLch) },
// {nameof( CieLchuv), default(CieLchuv) },
// {nameof( CieLuv), default(CieLuv) },
// {nameof( CieXyz), default(CieXyz) },
// {nameof( CieXyy), default(CieXyy) },
// {nameof( Hsl), default(Hsl) },
// {nameof( HunterLab), default(HunterLab) },
// {nameof( Lms), default(Lms) },
// {nameof( LinearRgb), default(LinearRgb) },
// {nameof( Rgb), default(Rgb) },
// {nameof( YCbCr), default(YCbCr) }
// };
//public static readonly IEnumerable<object[]> EmptyData = EmptyDataLookup.Select(x => new[] { x.Key });
//public static readonly TheoryData<object, object, Type> EqualityData =
// new TheoryData<object, object, Type>
// {
// { new CieLab(Vector3.One), new CieLab(Vector3.One), typeof(CieLab) },
// { new CieLch(Vector3.One), new CieLch(Vector3.One), typeof(CieLch) },
// { new CieLchuv(Vector3.One), new CieLchuv(Vector3.One), typeof(CieLchuv) },
// { new CieLuv(Vector3.One), new CieLuv(Vector3.One), typeof(CieLuv) },
// { new CieXyz(Vector3.One), new CieXyz(Vector3.One), typeof(CieXyz) },
// { new CieXyy(Vector3.One), new CieXyy(Vector3.One), typeof(CieXyy) },
// { new HunterLab(Vector3.One), new HunterLab(Vector3.One), typeof(HunterLab) },
// { new Lms(Vector3.One), new Lms(Vector3.One), typeof(Lms) },
// { new LinearRgb(Vector3.One), new LinearRgb(Vector3.One), typeof(LinearRgb) },
// { new Rgb(Vector3.One), new Rgb(Vector3.One), typeof(Rgb) },
// { new Hsl(Vector3.One), new Hsl(Vector3.One), typeof(Hsl) },
// { new Hsv(Vector3.One), new Hsv(Vector3.One), typeof(Hsv) },
// { new YCbCr(Vector3.One), new YCbCr(Vector3.One), typeof(YCbCr) },
// };
//public static readonly TheoryData<object, object, Type> NotEqualityDataNulls =
// new TheoryData<object, object, Type>
// {
// // Valid object against null
// { new CieLab(Vector3.One), null, typeof(CieLab) },
// { new CieLch(Vector3.One), null, typeof(CieLch) },
// { new CieLchuv(Vector3.One), null, typeof(CieLchuv) },
// { new CieLuv(Vector3.One), null, typeof(CieLuv) },
// { new CieXyz(Vector3.One), null, typeof(CieXyz) },
// { new CieXyy(Vector3.One), null, typeof(CieXyy) },
// { new HunterLab(Vector3.One), null, typeof(HunterLab) },
// { new Lms(Vector3.One), null, typeof(Lms) },
// { new LinearRgb(Vector3.One), null, typeof(LinearRgb) },
// { new Rgb(Vector3.One), null, typeof(Rgb) },
// { new Hsl(Vector3.One), null, typeof(Hsl) },
// { new Hsv(Vector3.One), null, typeof(Hsv) },
// { new YCbCr(Vector3.One), null, typeof(YCbCr) },
// };
//public static readonly TheoryData<object, object, Type> NotEqualityDataDifferentObjects =
// new TheoryData<object, object, Type>
// {
// // Valid objects of different types but not equal
// { new CieLab(Vector3.One), new CieLch(Vector3.Zero), null },
// { new CieLuv(Vector3.One), new CieLchuv(Vector3.Zero), null },
// { new CieXyz(Vector3.One), new HunterLab(Vector3.Zero), null },
// { new Rgb(Vector3.One), new LinearRgb(Vector3.Zero), null },
// { new Rgb(Vector3.One), new Lms(Vector3.Zero), null },
// { new Cmyk(Vector4.One), new Hsl(Vector3.Zero), null },
// { new YCbCr(Vector3.One), new CieXyy(Vector3.Zero), null },
// { new Hsv(Vector3.One), new Hsl(Vector3.Zero), null },
// };
//public static readonly TheoryData<object, object, Type> NotEqualityData =
// new TheoryData<object, object, Type>
// {
// // Valid objects of the same type but not equal
// { new CieLab(Vector3.One), new CieLab(Vector3.Zero), typeof(CieLab) },
// { new CieLch(Vector3.One), new CieLch(Vector3.Zero), typeof(CieLch) },
// { new CieLchuv(Vector3.One), new CieLchuv(Vector3.Zero), typeof(CieLchuv) },
// { new CieLuv(Vector3.One), new CieLuv(Vector3.Zero), typeof(CieLuv) },
// { new CieXyz(Vector3.One), new CieXyz(Vector3.Zero), typeof(CieXyz) },
// { new CieXyy(Vector3.One), new CieXyy(Vector3.Zero), typeof(CieXyy) },
// { new HunterLab(Vector3.One), new HunterLab(Vector3.Zero), typeof(HunterLab) },
// { new Lms(Vector3.One), new Lms(Vector3.Zero), typeof(Lms) },
// { new LinearRgb(Vector3.One), new LinearRgb(Vector3.Zero), typeof(LinearRgb) },
// { new Rgb(Vector3.One), new Rgb(Vector3.Zero), typeof(Rgb) },
// { new Cmyk(Vector4.One), new Cmyk(Vector4.Zero), typeof(Cmyk) },
// { new Hsl(Vector3.One), new Hsl(Vector3.Zero), typeof(Hsl) },
// { new Hsv(Vector3.One), new Hsv(Vector3.Zero), typeof(Hsv) },
// { new YCbCr(Vector3.One), new YCbCr(Vector3.Zero), typeof(YCbCr) },
// };
//public static readonly TheoryData<object, object, Type, float> AlmostEqualsData =
// new TheoryData<object, object, Type, float>
// {
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), 0F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .001F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0001F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0005F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, .001F, 0F), typeof(CieLab), .001F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, .0001F), typeof(CieLab), .0001F },
// { new CieLab(0F, 0F, 0F), new CieLab(.0005F, 0F, 0F), typeof(CieLab), .0005F },
// { new CieLch(0F, 0F, 0F), new CieLch(0F, .001F, 0F), typeof(CieLch), .001F },
// { new CieLchuv(0F, 0F, 0F), new CieLchuv(0F, .001F, 0F), typeof(CieLchuv), .001F },
// { new CieLuv(0F, 0F, 0F), new CieLuv(0F, .001F, 0F), typeof(CieLuv), .001F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380F), typeof(CieXyz), 0F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380.001F, 380F, 380F), typeof(CieXyz), .01F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.001F, 380F), typeof(CieXyz), .01F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.001F), typeof(CieXyz), .01F },
// { new Cmyk(1, 1, 1, 1), new Cmyk(1, 1, 1, .99F), typeof(Cmyk), .01F },
// { new YCbCr(255F, 128F, 128F), new YCbCr(255F, 128F, 128.001F), typeof(YCbCr), .01F },
// { new Hsv(0F, 0F, 0F), new Hsv(0F, 0F, 0F), typeof(Hsv), 0F },
// { new Hsl(0F, 0F, 0F), new Hsl(0F, 0F, 0F), typeof(Hsl), 0F },
// };
//public static readonly TheoryData<object, object, Type, float> AlmostNotEqualsData =
// new TheoryData<object, object, Type, float>
// {
// { new CieLab(0F, 0F, 0F), new CieLab(0.1F, 0F, 0F), typeof(CieLab), .001F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0.1F, 0F), typeof(CieLab), .001F },
// { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0.1F), typeof(CieLab), .001F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380.1F, 380F, 380F), typeof(CieXyz), .001F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.1F, 380F), typeof(CieXyz), .001F },
// { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.1F), typeof(CieXyz), .001F },
// };
//[Theory]
//[MemberData(nameof(EmptyData))]
//public void Vector_Equals_WhenTrue(string color)
//{
// IColorVector colorVector = EmptyDataLookup[color];
// // Act
// bool equal = colorVector.Vector.Equals(Vector3.Zero);
// // Assert
// Assert.True(equal);
//}
//[Theory]
//[MemberData(nameof(EqualityData))]
//public void EqualityOperator(object first, object second, Type type)
//public void Equals_WhenTrue(object first, object second, Type type)
//{
// // Act
// bool equal = first.Equals(second);
// // Assert
// Assert.True(equal);
//}
//[Theory]
//[MemberData(nameof(NotEqualityDataNulls))]
//[MemberData(nameof(NotEqualityDataDifferentObjects))]
//[MemberData(nameof(NotEqualityData))]
//public void Equals_WhenFalse(object first, object second, Type type)
//{
// // Act
// bool equal = first.Equals(second);
// // Assert
// Assert.False(equal);
//}
//[Theory]
//[MemberData(nameof(EqualityData))]
//public void GetHashCode_WhenEqual(object first, object second, Type type)
//{
// // Act
// bool equal = first.GetHashCode() == second.GetHashCode();
// // Assert
// Assert.True(equal);
//}
//[Theory]
//[MemberData(nameof(NotEqualityDataDifferentObjects))]
//public void GetHashCode_WhenNotEqual(object first, object second, Type type)
//{
// // Act
// bool equal = first.GetHashCode() == second.GetHashCode();
// // Assert
// Assert.False(equal);
//}
//[Theory]
//[MemberData(nameof(EqualityData))]
//public void GenericEquals_WhenTrue(object first, object second, Type type)
//{
// // Arrange
// // Cast to the known object types, this is so that we can hit the
@ -248,34 +211,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces
// dynamic secondObject = Convert.ChangeType(second, type);
// // Act
// dynamic equal = firstObject == secondObject;
// dynamic equal = firstObject.Equals(secondObject);
// // Assert
// Assert.True(equal);
//}
[Theory]
[MemberData(nameof(NotEqualityData))]
public void Operator_WhenTrue(object first, object second, Type type)
{
// Arrange
// Cast to the known object types, this is so that we can hit the
// equality operator on the concrete type, otherwise it goes to the
// default "object" one :)
dynamic firstObject = Convert.ChangeType(first, type);
dynamic secondObject = Convert.ChangeType(second, type);
// Act
dynamic notEqual = firstObject != secondObject;
// Assert
Assert.True(notEqual);
}
// TODO:Disabled due to RuntypeBinder errors while structs are internal
//[Theory]
//[MemberData(nameof(AlmostEqualsData))]
//public void AlmostEquals(object first, object second, Type type, float precision)
//[MemberData(nameof(NotEqualityData))]
//public void GenericEquals_WhenFalse(object first, object second, Type type)
//{
// // Arrange
// // Cast to the known object types, this is so that we can hit the
@ -285,16 +229,34 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces
// dynamic secondObject = Convert.ChangeType(second, type);
// // Act
// dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision);
// dynamic equal = firstObject.Equals(secondObject);
// // Assert
// Assert.True(almostEqual);
// Assert.False(equal);
//}
// TODO:Disabled due to RuntypeBinder errors while structs are internal
//// TODO:Disabled due to RuntypeBinder errors while structs are internal
////[Theory]
////[MemberData(nameof(EqualityData))]
////public void EqualityOperator(object first, object second, Type type)
////{
//// // Arrange
//// // Cast to the known object types, this is so that we can hit the
//// // equality operator on the concrete type, otherwise it goes to the
//// // default "object" one :)
//// dynamic firstObject = Convert.ChangeType(first, type);
//// dynamic secondObject = Convert.ChangeType(second, type);
//// // Act
//// dynamic equal = firstObject == secondObject;
//// // Assert
//// Assert.True(equal);
////}
//[Theory]
//[MemberData(nameof(AlmostNotEqualsData))]
//public void AlmostNotEquals(object first, object second, Type type, float precision)
//[MemberData(nameof(NotEqualityData))]
//public void Operator_WhenTrue(object first, object second, Type type)
//{
// // Arrange
// // Cast to the known object types, this is so that we can hit the
@ -304,10 +266,48 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces
// dynamic secondObject = Convert.ChangeType(second, type);
// // Act
// dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision);
// dynamic notEqual = firstObject != secondObject;
// // Assert
// Assert.False(almostEqual);
// Assert.True(notEqual);
//}
//// TODO:Disabled due to RuntypeBinder errors while structs are internal
////[Theory]
////[MemberData(nameof(AlmostEqualsData))]
////public void AlmostEquals(object first, object second, Type type, float precision)
////{
//// // Arrange
//// // Cast to the known object types, this is so that we can hit the
//// // equality operator on the concrete type, otherwise it goes to the
//// // default "object" one :)
//// dynamic firstObject = Convert.ChangeType(first, type);
//// dynamic secondObject = Convert.ChangeType(second, type);
//// // Act
//// dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision);
//// // Assert
//// Assert.True(almostEqual);
////}
//// TODO:Disabled due to RuntypeBinder errors while structs are internal
////[Theory]
////[MemberData(nameof(AlmostNotEqualsData))]
////public void AlmostNotEquals(object first, object second, Type type, float precision)
////{
//// // Arrange
//// // Cast to the known object types, this is so that we can hit the
//// // equality operator on the concrete type, otherwise it goes to the
//// // default "object" one :)
//// dynamic firstObject = Convert.ChangeType(first, type);
//// dynamic secondObject = Convert.ChangeType(second, type);
//// // Act
//// dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision);
//// // Assert
//// Assert.False(almostEqual);
////}
}
}

Loading…
Cancel
Save